用 ZooKeeper 做全局计数器 / 发号器是常见需求。Curator 的 DistributedAtomicLong 用乐观重试实现,但「失败了不抛异常」这个设计很容易踩坑。这篇量正确性、重试耗尽时的行为、PromotedToLock、吞吐随线程数的变化,并与顺序节点发号比较。
succeeded()=true 1000 次,false 0 次;最终值 1000;用时 6.6 s
原理就是乐观锁(带 version 的写):读到 version,写回时版本不符就重试。
succeeded()=true 328 次,false 672 次;最终值 328(= 成功次数,失败的 increment 没有被计入,也没有抛异常);用时 2.6 s
succeeded()=true 1000 次,false 0 次;最终值 1000;用时 6.0 s
锁的机制见上一篇。
1 线程:238 次/s(成功 100 / 100) 2 线程:206 次/s(成功 200 / 200) 5 线程:194 次/s(成功 500 / 500) 10 线程:131 次/s(成功 1000 / 1000)
每次 increment 至少一次读和一次写(都要经过 leader 写提交),所以它是一个低频计数器。需要每秒数万次的计数,应当在应用内批量累加后定期写入,或者用别的存储。
1000 个序号中不同的 1000 个;用时 0.9 s(1142 个/s,含每个一次删除);最大序号 /ids/n-0000000999
顺序号的行为见顺序节点:由服务端串行分配,不会冲突。