脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

Rubyで一度接続したコネクションを使いまわす方法(コネクションプール)

RubyでDBなどにアクセスしたり、他のサーバへコネクションを接続したりすると思いますが、頻繁に接続がある場合はその度に接続をしていてはコストになります。
対応として、コネクションを一定時間維持して、そのコネクションを使いまわすとよいです。

方法

1. まずはコネクション接続のクラスをsingletonとして作成
require 'socket'
require 'singleton'

class ConPool
  include Singleton

  # コネクションプールの作成
  def initialize
    @pool = {}
  end

  # コネクションの作成
  def create_connection(host, port)
    TCPSocket.new(host, port)
  end

  # コネクションの取得
  def get_connection(host)
    if @pool.key?(host) && @pool[host].length > 0
      # 既にコネクションプールにコネクションが保存されている場合は取得
      con = @pool[host].shift 
    else
      # 保存されていない場合はコネクションを新規に作成
      con = create_connection(nid) unless con
    end

    con
  end

  # コネクションの返却(コネクションプールへ)
  def return_connection(host, con)
    if @pool.key?(host)
      @pool[host] << con
    else
      @pool[host] = [con]
    end
  end

  # コネクションの削除
  def delete_connection(host)
    @pool.delete(host)
  end

end # class ConPool
2. 実際にコネクションを利用するアプリケーション等で上記のコネクションプールクラスを呼び出す
# コネクション取得
con = ConPool.instance.get_connection(host)

#{処理}

# コネクション返却
ConPool.instance.return_connection(host, con)

コネクションの数や保存期間に制限をつけたい場合

1. コネクションプールクラスにインスタンス変数として

maxlength(コネクション最大数)
expire_time(保持期間)
を設定する

2. コネクションの取得時と返却時に上記を確認して、保存すべきか破棄するかを判断する
・
・
・
  attr_accessor :maxlength
  attr_accessor :expire_time

  def initialize(maxlength = 10, expire_time = 60)
    @pool = {}
    @maxlength = maxlength
    @expire_time = expire_time
  end
 ・
 ・
 ・
  # コネクションの取得
  def get_connection(host)
    con,last = @pool[host].shift if @pool.key?(host) && @pool[host].length > 0
    if con && last < Time.now - @expire_time
      con.close  # 保存期間を過ぎている古いコネクションの場合は破棄する
      con = nil
    end
    con = create_connection(host) unless con
    con
  end

  # コネクションの返却(コネクションプールへ)
  def return_connection(host, con)
    if @pool.key?(host) && @pool[host].length > 0
      if @pool[host].length > @maxlength
        con.close  # コネクションプールが満杯の場合は保存せずにコネクションを破棄する
      else
        @pool[host] << [con, Time.now] # コネクションを保存する際に時間も一緒に保存する
      end
    else
      @pool[host] = [[con, Time.now]]
    end
  end

 ・
 ・
 ・
3. 実際の利用は上記と同じです