1、实现锁
ZK的每一个存在一个顺序节点,在你创建顺序节点时,ZK会自动添加一个自增的编号上去,编号最大是INT_MAX(2147483647 ),如果超出就走溢出,变成负数。
每个获取锁的客户端,在父节点下新建一个自己顺序节点,然后再获取父节点的所有孩子节点,并且设置Watch,观看孩子节点的变化,查看当前第一个节点是否是自己(根据ZK添加的自增编号从小到大排序)。
1、如果是自己即获取到锁,执行业务,然后释放锁。
2、如果不是自己,即未获取到锁,进行thread.wait(),或者再去获取都行。如果wait(),必须在获取孩子节点时设置watch,并且得到孩子节点变化进行notify()。
2、实现Barrier
Barrier含义是等待内部事件,然后同时开始,比如等有5人后就开始干活。
实现过程:
1、参与Barrier的选定一个父节点,然后在父节点上设置child类型watch(观察子节点的变化)。
2、在进入barrier的函数里面,设置自己的子节点,获取当前父节点的孩子节点个数,如果等于Barrier size,那就是人到齐了,进行业务处理,业务处理完后,把子节点delete掉。
3、如果不够,则进入thread.wait()。
4、在zk的事件回调函数,调用thread.notify()函数,注意,需要判断EventType。
异常情况处理。
客户端连接中断,如果出现这个情况,再次执行流程,如果创建的是临时节点,需要检查节点是否存在,如果不存在,需要重建。如果重连后不处理,可能导致Barrier永远无法满足。
3、实现Queue
实现原理和实现锁类似。使用顺序节点,把Queue的数据放在Data内部。
4、实现2PC
2PC角色分为俩个,协调者和参与者。
1、协调者创建事务节点 /app/Tx
2、协调者给每个参与者在/app/Tx下创建子节点,比如/app/Tx/s_i,协调者创建的孩子节点让数据域是空的。
3、参与者读取所有孩子节点,然后在每个孩子节点上设置watch。
4、参与者处理完事务后在自己的节点的内写commit或abort。
5、参与者写完后,其他节点就会收到通知,这样参与者就能收到所有决定,决定是abort还是commit。
6、2PC过程完毕,协调者删除/app/Tx。
实现的缺点:
1:消息复杂,需要O(n²)。
2:无法通过创建临时节点来检查到参与者故障。
解决方法:
1:事务节点的改变事件仅通知给协调者,然后协调者通知参与者是应该commit还是abort,但是这样会很慢,因为所有的消息全部由协调者处理。
2:第二个问题,由协调者者生成每个事务节点名字,发送给参与者,由参与者自己创建临时节点。
异常情况处理:
正常的2PC过程,
第一步:协调者发起,首先各个事务节点进入prepare过程。
第二步:事务节点进行事务处理,然后返回给协调者结果。
第三步:协调者根据各个事务节点的结果,决定让各个事务节点是commit/abort。
在用ZK实现时,不存在prepare过程。
1.如果协调者在事务处理过程中宕机,重启后,需要询问事务的结果,然后决定是否应该进行事务重做。
2:此时协调者是弱关系,如果宕机,只要询问最后的结果就行。
5、实现选举
实现选举也很简单,每个参与选举的客户端都对应ZK的一个节点,然后其他流程和实现锁的方式一致,只是,当前是自己时,一直不删除节点就行了。
具体可以参考我的代码,实现很粗略:
https://github.com/tangzhe7/zklock
curator框架的sample。
https://github.com/apache/curator/tree/master/curator-examples