znzの日記

 | 

2006-10-17

YAMLでRPC

http://subtech.g.hatena.ne.jp/secondlife/20061012/1160641587 を見てちょっとやってみたら、例外のmessageがちゃんと復元されなかったので、作りかけでやめてしまった。

インスタンス変数が復元できない」とか書いてるけど、YAML.dumpしてYAML.loadすると普通はちゃんと復元される。

メッセージの形式はメソッド呼び出しがハッシュのキーにメソッド名で値に引数YAML.dumpしたものを"\0"終端で。gets("\0")というのはRailsChatのまね。

返値はメソッドの返値か例外が発生した場合はその例外をYAML.dumpしたものを"\0"終端で。

require 'yaml'

module YAMLRPC
  class Server
    def initialize(obj, io)
      @obj = obj
      @io = io
    end

    def serve
      while yaml = @io.gets("\0")
        begin
          doc = YAML.load(yaml)
          doc.each_pair do |key, value|
            ret = YAML.dump(@obj.__send__(key, *value))
            @io.write ret
            @io.write "\0"
            @io.flush
          end
        rescue Exception => e
          @io.write YAML.dump(e)
          @io.write "\0"
          @io.flush
        end
      end
      @io.close
    end

    def self.start_tcp_server(obj, *tcpserver_args)
      require 'socket'
      sock = TCPServer.open(*tcpserver_args)
      Thread.start do
        while true
          s = sock.accept
          Thread.start do
            self.new(obj, s).serve
          end
        end
      end
      sock.addr[1]
    end
  end

  class Client
    def initialize(io)
      @io = io
    end

    def method_missing(sym, *args)
      @io.write(YAML.dump({sym => args}))
      @io.write("\0")
      ret = YAML.load(@io.gets("\0"))
      if ret.is_a?(Exception)
        raise ret
      else
        return ret
      end
    end
  end
end

if __FILE__ == $0
  require 'socket'
  port = YAMLRPC::Server.start_tcp_server({}, '127.0.0.1', 0)
  p [:port, port]
  h = YAMLRPC::Client.new(TCPSocket.open('127.0.0.1', port))
  h[1] = 'one'
  h['two'] = 2
  p h[1]
  p h[2]
  h.fetch(3)
end

問題点としては

  • 例外のmessageやbacktraceが復元されない
  • method_missingにならないメソッド(to_sなど)がRPCにならない(ruby 1.9ならClientがBasicObjectを継承するようにすれば解決する)
  • サーバ側はどんなメソッドでも呼び出せるので危険
  • エラー処理が適当
  • ブロックは渡せない(eachとかが使えない)
  • とりあえずTCPのみ
  • start_tcp_serverでテスト用にとりあえずポート番号を返してるけどDRb.start_serviceとかDRb.uriみたいなAPIにすべき
  • 止められない

など。

 |