06 June 2014

    EventMachineで簡単インターフェース

     初めてmorizyun氏のこの記事を読んだときはEventMachineとかforemanとかって何をするものか知らなかったのですが、EventMachineというのは「[Ruby] Reactorパターンについて」にも書いてある通り、ソケットの受信待機をして貰って簡単にReactorパターンを実現することが出来るツールってことのようです。だからわざわざ自分でプロセス間通信の手続きを書く必要がなくアプリケーションレベルのコマンドを用意してあげるだけで済むのではないか?と思いながら作ったら思惑通りにうまく動いたって感じです。実際に組み込む前には、以下のような単純なサンプルで確認しました。

    #server側サンプル  
    require 'eventmachine'
    
    class Serv < EM::Connection
      attr_accessor :track
      def post_init
        puts "myserv: init"
      end
    
      def receive_data(data)
        puts data
        send_data @track
        EM.stop if data =~ /stop/i
      end
    
      def connection_completed
        puts "myserv: completed"
      end
    
      def unbind
        puts "myserv: unbind"
      end
    end
    
    EM.run do
      EM.start_server("127.0.0.1", 10000, Serv) do |conn|
        conn.track = ARGV[0]
      end
    end
    

    以下のようにコマンドラインパラメータを付加して起動し、

    ruby myserv.rb O.K.
    クライアント接続時にServクラスのプロパティに保持されていることを確認する。

    #client側サンプル
    require "net/telnet"
    
    begin
      localhost = Net::Telnet::new("Host" => "localhost",
                                 "Port" => 10000,
                                 "Timeout" => 1,
                                 "Telnetmode" => false,
                                 "Output_log" => "./temp0.log",
                                 "Prompt" => "O.K.")
      localhost.cmd("search word") { |c| print c }
      localhost.close
      localhost = nil
      p "end"
    rescue Net::ReadTimeout
      p "readtimeout"
      p $!.to_s
    rescue
      p $!.to_s
    end
    
    

    このserver側の処理を、twitterAPIを使ってDBに検索結果を保存するスクリプトに組み込んで、client側の処理をRails製のサイトに組み込めばいいわけです。
     あと、server側のスクリプトは、ユーザ毎に起動してユーザがWebサイトから制御しなければならないので、foremanは必要無さそうです。

    サーバースクリプトへのパラメータの受け渡し

     EventMachine::Connectionを継承したクラスServにユーザ情報を保存するメンバ変数(プロパティ)を用意して、Webサイト(client側)からサーバースクリプトを起動する時にコマンドライン引数でそれらの値を渡してあげます。起動の際に「10000+ユーザID」をポート番号に指定することでWebサイト利用者とサーバースクリプト利用者を一致させています。EventMachineを使ったプログラム同士が変数を共有する仕組みのようなものがあるかもしれませんが、ステートレスなHTTPで動いているWebサイトから起動するのだから、コマンドラインで渡すのがちょうど良さそうです。
     twitterのStreamingAPIを使用する部分は以前書いた記事で紹介したmorizyun氏のコードをServ.trackingメソッドに閉じ込めてほぼそのまま動かしています。twitter側でエラーが発生した場合このままでいいのかよく分かりませんが、エラーになればWebサイト側からサーバースクリプトを再起動してもらえばいいという考え方で作ってます。
     client側からserver側スクリプトに受け渡すパラメータはTwitterAPIを利用するために必要な認証key文字列(4種類)とユーザIDとTwitterのfilterAPIに渡す検索文字列(検索タグ)です。

    #実際のソースの一部
    require 'eventmachine'
    require 'optparse'
    
    class Serv < EM::Connection
      attr_accessor :uid
      attr_accessor :track
      attr_accessor :c_key
      attr_accessor :c_secret
      attr_accessor :a_key
      attr_accessor :a_secret
      def receive_data(data)
        case data
        when /stop/i
          send_data "O.K."
          EM.stop
        when /check/i
          send_data @track
        省略
          :
    
      def tracking(track, c_key, c_secret, a_key, a_secret, uid)
        stream = Twitter::JSONStream.connect(
      省略
        :
    
    EM.run do
      EM.start_server("127.0.0.1", 10000 + params["p"].to_i, Serv) do |conn|
        conn.track = params["t"]
        conn.uid = params["p"].to_i
        conn.c_key = params["c"]
        conn.c_secret = params["k"]
        conn.a_key = params["s"]
        conn.a_secret = params["a"]
      end
    end
    

    参考サイト



    blog comments powered by Disqus