Journal InTime


2014-06-29 (Sun) [長年日記]

_ RedisのPub/Subの遅いsubscriber問題

RedisのPub/Subを試していて、subscriber側の受信が遅いとredis-server上のキューが伸び続けて、メモリ使用量が増大し続けてしまう問題にはまった。

以下のようにひたすらpublisher側でひたすらデータを送るようにしておいて、

require "redis"
require "json"

redis = Redis.new(timeout: 0)

redis.subscribe("foo") do |on|
  on.message do |channel, msg|
    i = msg.slice(/(\d+):/, 1)
    p i
    sleep(1.0)
  end
end

subscriber側で以下のようにsleepすると、どんどんredis-serverが太っていく。

require "redis"
require "json"

redis = Redis.new(timeout: 0)

redis.subscribe("foo") do |on|
  on.message do |channel, msg|
    i = msg.slice(/(\d+):/, 1)
    p i
    sleep(1.0)
  end
end

調べてみると、Redis 2.6以降ではバッファサイズの上限を指定でき、上限に達したクライアントは切断されるようだ。

たまたまUbuntu 14.04に上げたところだったので確認すると、redis-server-2:2.8.4-2では最初からredis.confで以下のように設定されていた。

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

この環境で上記のスクリプトを試すと、たしかに以下のように切断される。

$ ruby sub.rb
"0"
"1"
"2"
"3"
"4"
"5"
"6"
"7"
"8"
"1681"
"1682"
"1683"
"1684"
"1685"
"1686"
"1687"
"1688"
"1689"
/home/shugo/local/lib/ruby/gems/2.2.0/gems/redis-3.1.0/lib/redis/client.rb:233:in `rescue in io': Connection lost (ECONNRESET) (Redis::ConnectionError)

番号が飛んでいるところを見ると、上限に達してもすぐに切断されるわけではないようだ。

結論: ソフトウェアに何か問題があったらとりあえず最新版を確認しましょう。