EventMachineで簡単なロードバランサーを書いてみた

同期の問題は大変デスネー
Hostヘッダ渡さなくてもレスポンス返してくれたのがちょっと意外だった。
hHatena Blogはレスポンス返してくれるのかな?

#!/usr/bin/env ruby
require 'rubygems'a
require 'eventmachine'

class Backend < EM::Connection
  def initialize(proxy)
    @proxy = proxy
  end

  def receive_data(data)
    @proxy.send_data(data)
  end
end

class Proxy < EM::Connection
  def initialize(backends)
    # コネクション毎の生成なので、このタイミングでバックエンドを決める
    host, port = backends[rand(backends.length)]
    @backend = EM.connect(host, port, Backend, self)

    # プロセス内でグローバルなラウンドロビンをやろうと思うと
    # プールの同期が必要になって、パフォーマンスが低下する気がする
    #
    # "以前に言及したように、接続と接続の間で君のクラスのインスタンスはどんな情報も共有できない。
    #  幸運にも、いや、設計されたことだろうが、EventMachine はこれを処理するメカニズムを提供する。"
    # http://keijinsonyaban.blogspot.com/2010/12/eventmachine.html
    # 
    # 共有…まじめに同期しようと思うと大変な気がするなー
    # mutableなオブジェクトについてのソリューションもあるのかしら
    #
    # バックエンドのコネクションもプーリングできたらよいけど
    # EventMachineのパターンと相性悪いような…
    # まあProxyへの接続が維持されている間はコネクションも維持されるので
    # そんなに問題ではないのかも
  end

  def receive_data(data)
    @backend.send_data(data)
  end
end

EM.run do
  # 順序が大事!なのでHashは×(たぶん)
  EM.start_server('0.0.0.0', 80, Proxy, [
    ['www.yahoo.co.jp', 80],
    ['www.hatena.ne.jp', 80],
  ])
end


shell1> ruby lb.rb

shell2> for i in {1..5}
> do
> wget -q -O- 127.0.0.1 | nkf | fgrep '' > done <title>はてな
はてな
Yahoo! JAPAN
Yahoo! JAPAN
はてな