使用 Redis 队列定期从多个进程中无重复地添加值

希望定期将时间戳添加到resque队列中,并想要以cron的方式使用,但是将添加到队列的操作放在一个进程中会导致可用性较低,所以希望能够从多个进程(分散在多个服务器上)无重复地添加值。

以下的方法看起来很有希望成功。

    在最后添加值之后保留时间戳,并在添加之前进行检查,以防止在几乎同时执行时发生重复操作。为此,可以利用Redis的事务机制。

以下是一个每秒将时间戳添加到队列的示例。

下面是一个每秒往队列里添加时间戳的例子。

# cron.rb
require "redis"
require "json"

QUEUE_NAME = "per_sec" # キューの名前
TIMESTAMP_NAME = "per_sec_timestamp" # 最後にキューに値を追加したときのタイムスタンプ

redis = Redis.new

loop do
  t = Time.now.to_i

  redis.watch(TIMESTAMP_NAME) # TIMESTAMP_NAME の値が exec までに変わっていれば multi - exec 間のコマンドを失敗させる
  last = redis.get(TIMESTAMP_NAME).to_i

  if last < t
    redis.multi
    redis.rpush(QUEUE_NAME, t)
    redis.set(TIMESTAMP_NAME, t)
    redis.exec ? puts("set #{t}") : puts("transaction fail")
  end
  sleep 0.1
end

通过启动两个进程来执行上述脚本,使用 while true; do redis-cli lpop per_sec; sleep 0.1s; done 来不断地弹出队列的内容,并且可以看到以下结果,表明可以在队列中无重复地进行添加。

redis.gif

Redis 对集群模式的支持

在集群模式下,仅当操作的键位于相同的哈希槽时,MULTI-EXEC 指令才会生效(参考)。要实现这一功能,只需将用于保存时间戳的键的哈希用作队列的键名即可。

TIMESTAMP_KEY = "{#{QUEUE_KEY}}:updated_at".freeze

当CLUSTER KEYSLOT per_sec与CLUSTER KEYSLOT {per_sec}:updated_at相符时,它们被保存在同一个哈希槽中,因此可以使用MULTI-EXEC。

广告
将在 10 秒后关闭
bannerAds