【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
ZooKeeper 分布式锁
目前分布式锁,比较成熟、主流的方案有基于redis及基于zookeeper的二种方案。 基于zookeeper的分布式锁
大体来讲,基于redis的分布式锁核心指令为SETNX,即如果目标key存在,写入缓存失败返回0,反之如果目标key不存在,写入缓存成功返回1,通过区分这二个不同的返回值,可以认为SETNX成功即为获得了锁。
redis分布式锁,看上去很简单,但其实要考虑周全,并不容易,网上有一篇文章讨论得很详细:http://blog.csdn.net/ugg/article/details/41894947/,有兴趣的可以阅读一下。
其主要问题在于某些异常情况下,锁的释放会有问题,比如SETNX成功,应用获得锁,这时出于某种原因,比如网络中断,或程序出异常退出,会导致锁无法及时释放,只能依赖于缓存的过期时间,但是过期时间这个值设置多大,也是一个纠结的问题,设置小了,应用处理逻辑很复杂的话,可能会导致锁提前释放,如果设置大了,又会导致锁不能及时释放,所以那篇文章中针对这些细节讨论了很多。
而基于zk的分布式锁,在锁的释放问题上处理起来要容易一些,其大体思路是利用zk的“临时顺序”节点,需要获取锁时,在某个约定节点下注册一个临时顺序节点,然后将所有临时节点按小从到大排序,如果自己注册的临时节点正好是最小的,表示获得了锁。(zk能保证临时节点序号始终递增,所以如果后面有其它应用也注册了临时节点,序号肯定比获取锁的应用更大)
当应用处理完成,或者处理过程中出现某种原因,导致与zk断开,超过时间阈值(可配置)后,zk server端会自动删除该临时节点,即:锁被释放。所有参与锁竞争的应用,只要监听父路径的子节点变化即可,有变化时(即:有应用断开或注册时),开始抢锁,抢完了大家都在一边等着,直到有新变化时,开始新一轮抢锁。
关于zk的分布式锁,网上也有一篇文章写得不错,见http://blog.csdn.net/desilting/article/details/41280869
个人感觉:zk做分布式锁机制更完善,但zk抗并发的能力弱于redis,性能上略差,建议如果并发要求高,锁竞争激烈,可考虑用redis,如果抢锁的频度不高,用zk更适合。
最后送福利时间到:
文中提到的基于zk分布式锁的那篇文章,逻辑上虽然没有问题,但是有些场景下,锁的数量限制可能要求不止1个,比如:某些应用,我希望同时启动2个实例来处理,但是出于HA的考虑,又担心这二个实例会挂掉,这时可以启动4个(或者更多),这些实例中,只允许2个抢到锁的实例可以进行业务处理,其它实例处于standby状态(即:备胎),如果这二个抢到锁的实例挂了(比如异常退出),那么standby的实例会得到锁,即:备胎转正,开始正常业务处理,从而保证了系统的HA。
对于这些场景,我封装了一个抽象类,大家可在此基础上自行修改:(主要看明白思路就行,代码细节并不重要)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
|
这个类中,提供了三个抽象方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
用于处理抢锁成功、抢锁失败、及开抢前的一些对象初始化处理,子类继承后,只要实现这3个具体的方法即可,同时该抽象类默认还提供了心跳机制,用于定时向zk汇报自身的健康状态。
来源:oschina
链接:https://my.oschina.net/u/3935942/blog/2236866