使用 ZooKeeper 的高效排他锁,用于不频繁的操作

Efficient exclusive lock with ZooKeeper for infrequent operations

我在多台服务器上部署了一个微服务,它有两个主要数据源:

  1. 不断 (24/7/365) 大量接收事件 (100-1000 event/sec)

  2. 一天一次的操作,可能需要一点时间才能完成

我想运行独占模式下的这个一天一次的处理:暂停事件处理,运行一天一次的任务,然后恢复事件处理。我已经有办法正确启动一天一次的操作,但我仍然必须在 1 和 2 之间实现锁定以确保排他性。

我发现的大多数 ZooKeeper 食谱都需要对每个处理的事件进行写入操作,例如使用 InterProcessReadWriteLock or counter increase with DistributedAtomicLong 读取锁定获取。由于每天一次的操作很少发生,因此每个事件的开销似乎很浪费。

是否有针对此类用例优化的 ZooKeeper/Curator 收据?

我考虑过以下方法,但我不能 100% 确定这是否是正确的方法(以及如何实施下面的第 2 点):

  1. 当一天一次的操作开始时,在 ZooKeeper
  2. 中创建新的 /exclusive 路径
  3. 等待所有飞行中的事件完成
  4. 在处理事件之前检查 /exclusive 是否存在。如果它在那里停止处理,直到 /exclusive 路径被删除
  5. 当一天一次结束时删除 /exclusive 路径

这个呢?

每个事件处理器必须:

  • 通过InterProcessReadWriteLock获得一个锁。
  • 使用NodeCache 监视一个"signal" 节点并侦听此节点上的更改。当节点存在时,表示它是每日处理时间。当它不存在时,事件处理可以继续。
  • 当 NodeCache 显示信号节点已创建时,事件处理器必须释放它们的锁并等待信号节点被删除(再次通过 NodeCache 侦听)。
  • 当NodeCache显示信号节点已经被删除,事件处理器重新获得读锁,继续处理事件。

一旦设置好,就没有额外的 ZooKeeper activity 而它是 运行ning。

当一天一次的操作准备就绪时 运行:

  • 它创建信号节点(作为临时节点)
  • 获取 write 锁在与事件处理器用于读锁相同的路径上
  • 是否定期处理
  • 释放写锁
  • 删除信号节点

然而,这有一个很大的警告,这就是 JVM 暂停可能发生的情况。另请阅读此 Tech Note 以了解重要的边缘情况。