圧縮転送 - MessagePack-RPC for Ruby 0.3.0

高速な非同期メッセージングライブラリ MessagePack-RPC for Ruby をアップデートしました。互換性はだいたい維持されています。

gem install msgpack-rpc

今回の目玉は、圧縮転送のサポートです。メッセージをdeflate(zlib)を使って圧縮します。

使い方

いつも通りにKey-valueストアを作ってみました。
↓このように、Client#overメソッドを使って、圧縮オプションを指定します。

require 'msgpack/rpc'  # gem install msgpack-rpc

cli = MessagePack::RPC::Client.new('127.0.0.1', 9090)


# テキスト(HTML)ファイルを読み込む http://preferred.jp/index.html を使用
text = File.read("index.html")

# テキスト圧縮なし
cli.call(:set, "k1", text)  # 6608バイト ->  <-    5バイト
cli.call(:get, "k1")        #   11バイト ->  <- 6601バイト

# テキスト圧縮あり
cli.over(:tx_deflate).call(:set, "k2", text)  # 2609バイト ->  <-    5バイト
cli.over(:rx_deflate).call(:get, "k2")        #   11バイト ->  <- 2604バイト
cli.over(:rx_deflate).call(:get, "k2")        #   11バイト ->  <-   68バイト


# 画像(PNG)ファイルを読み込む http://preferred.jp/labslogo_small.png を使用
image = File.read("labslogo_small.png")

# 画像圧縮なし
cli.call(:set, "k3", image)  # 7753バイト ->  <-    5バイト
cli.call(:get, "k3")         #   11バイト ->  <- 7746バイト

# 画像圧縮あり
cli.over(:tx_deflate).call(:set, "k4", image)  # 7716バイト ->  <-    5バイト
cli.over(:rx_deflate).call(:get, "k4")         #   11バイト ->  <- 7710バイト
cli.over(:rx_deflate).call(:get, "k4")         #   11バイト ->  <-   78バイト


cli.close

:tx_deflate は送信するデータを圧縮するオプションです。:rx_deflate は受信するデータを圧縮するオプションです。このように送信と受信で別々に圧縮の有効・無効を指定できます。:deflateと指定すると両方圧縮します。
ここではデータを set するときは送信するデータを圧縮し、get するときは受信するデータを圧縮しています。


まずテキストの圧縮率を見てみると、圧縮無しでは6608バイトだったデータが、圧縮を有効化すると2609バイトまで縮んでいます。
setに対して、getは2回行っています。1回目は2604バイトですが、2回目はさらに68バイトまで縮んでいます。
これはTCPコネクションを1本まるまる圧縮しているためです。2回目は1回目の圧縮で作成された辞書を使って圧縮するので、圧縮率が高くなります。


この特徴があるので、場合によっても画像ファイルでも縮みます。
画像ファイルだと1回目の送信では7753バイトから7716バイトにしか縮みませんが、2回目のgetだと、78バイトまで縮んでいます。

同じデータを2回続けて送るというケースは稀かもしれませんが、1つのデータのエントロピーが小さくても、複数のデータの間で傾向が似ていれば圧縮率が高くなるという仕組みです。


サーバ側は何も変わりません。圧縮の有効化・無効化は、クライアント側で指定します。

require 'msgpack/rpc'  # gem install msgpack-rpc

class KVServer
  def initialize
    @hash = Hash.new
  end
  def get(key)
    @hash[key]
  end
  def set(key, val)
    @hash[key] = val
    nil
  end
end

svr = MessagePack::RPC::Server.new
svr.listen '0.0.0.0', 9090, KVServer.new
svr.run