dRuby实践

常用的任务分发工具很多,也有不少基于消息队列的方案。比如erlang,各种mq(zmq, rabbitmq)等。在代码实践中,需要对代码比较进行比较小的改造就进行部署,ruby核心模块中包含的druby模块可以很好的达到目的。

近期需要将之前单机运行的任务分拆到不同地理位置的服务器中运行,以实现对不同基础网络环境的监测评估。功能代码已经在现有class中实现,这里的主要工作是将相关代码改为远程调用。

先来看dRuby改造中server端的实现:

#!/usr/bin/env ruby -w

require 'drb/drb'
require 'drb/acl'

addr = "192.168.1.2:8787"
SERVER_URI = "druby://#{addr}"

class CheckURLServer
  def checkurl(url)
    # do my job
    "_MY_RESULT_"
  end
end

FRONT_OBJECT=CheckURLServer.new

list = %w[
  deny all
  allow 192.168.1.1
]
acl = ACL.new(list, ACL::DENY_ALLOW)

DRb.install_acl(acl)
DRb.start_service(SERVER_URI, FRONT_OBJECT)
DRb.thread.join

再来看客户端的实现:

#!/usr/bin/env ruby

require 'drb/drb'

CLIENT_URI="druby://192.168.1.1:8788"
DRb.start_service(CLIENT_URI)

url = "http://example.com/robots.txt"

server_uri = "druby://192.168.1.2:8787"

begin
  checkurlserver = DRbObject.new_with_uri(server_uri)

  puts checkurlserver.checkurl(url)

rescue DRb::DRbConnError => e
  puts "# connect failed #{addr}"

end

这里面有几个特点需要注意:

  1. ACL
    因为部署在不同位置,为了防止接口被滥用,可以通过install_acl来指定acl规则。
  2. 调度
    任务调度采用的是指定server端ip:port的方式,需要自行进行调度。在实际生产环境中我们有一个比较简洁的class来负责调度。
  3. DRbObject可以通过更灵活(也更危险)方式将所有本地方法调用转换为远程方法调用。 下面是一个例子。

     # !!! UNSAFE CODE !!!
     ro = DRbObject::new_with_uri("druby://your.server.com:8989")
     class << ro
       undef :instance_eval  # force call to be passed to remote object
     end
     ro.instance_eval("`touch /tmp/testfile && rm -rf /tmp/testfile`")