基于ZooKeeper实现——分布式锁与实现

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

ZooKeeper的架构通过冗余服务实现高可用性。因此,如果第一次无应答,客户端就可以询问另一台ZooKeeper主机。ZooKeeper节点将它们的数据存储于一个分层的命名空间,非常类似于一个文件系统或一个前缀树结构。客户端可以在节点读写,从而以这种方式拥有一个共享的配置服务。更新是全序的。

基于ZooKeeper分布式锁的流程

  • 在zookeeper指定节点(locks)下创建临时顺序节点node_n
  • 获取locks下所有子节点children
  • 对子节点按节点自增序号从小到大排序
  • 判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
  • 若监听事件生效,则回到第二步重新进行判断,直到获取到锁

具体实现

下面就具体使用java和zookeeper实现分布式锁,操作zookeeper使用的是apache提供的zookeeper的包。

  • 通过实现Watch接口,实现process(WatchedEvent event)方法来实施监控,使CountDownLatch来完成监控,在等待锁的时候使用CountDownLatch来计数,等到后进行countDown,停止等待,继续运行。
  • 以下整体流程基本与上述描述流程一致,只是在监听的时候使用的是CountDownLatch来监听前一个节点。

分布式锁

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
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
 
/**
  * Created by liuyang on 2017/4/20.
  */
public  class  DistributedLock implements Lock, Watcher {
     private  ZooKeeper zk =  null ;
     // 根节点
     private  String ROOT_LOCK =  "/locks" ;
     // 竞争的资源
     private  String lockName;
     // 等待的前一个锁
     private  String WAIT_LOCK;
     // 当前锁
     private  String CURRENT_LOCK;
     // 计数器
     private  CountDownLatch countDownLatch;
     private  int  sessionTimeout = 30000;
     private  List<Exception> exceptionList =  new  ArrayList<Exception>();
 
     /**
      * 配置分布式锁
      * @param config 连接的url
      * @param lockName 竞争资源
      */
     public  DistributedLock(String config, String lockName) {
         this .lockName = lockName;
         try  {
             // 连接zookeeper
             zk =  new  ZooKeeper(config, sessionTimeout,  this );
             Stat stat = zk.exists(ROOT_LOCK,  false );
             if  (stat ==  null ) {
                 // 如果根节点不存在,则创建根节点
                 zk.create(ROOT_LOCK,  new  byte [0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
             }
         catch  (IOException e) {
             e.printStackTrace();
         catch  (InterruptedException e) {
             e.printStackTrace();
         catch  (KeeperException e) {
             e.printStackTrace();
         }
     }
 
     // 节点监视器
     public  void  process(WatchedEvent  event ) {
         if  ( this .countDownLatch !=  null ) {
             this .countDownLatch.countDown();
         }
     }
 
     public  void  lock () {
         if  (exceptionList.size() > 0) {
             throw  new  LockException(exceptionList. get (0));
         }
         try  {
             if  ( this .tryLock()) {
                 System. out .println(Thread.currentThread().getName() +  " "  + lockName +  "获得了锁" );
                 return ;
             else  {
                 // 等待锁
                 waitForLock(WAIT_LOCK, sessionTimeout);
             }
         catch  (InterruptedException e) {
             e.printStackTrace();
         catch  (KeeperException e) {
             e.printStackTrace();
         }
     }
 
     public  boolean tryLock() {
         try  {
             String splitStr =  "_lock_" ;
             if  (lockName.contains(splitStr)) {
                 throw  new  LockException( "锁名有误" );
             }
             // 创建临时有序节点
             CURRENT_LOCK = zk.create(ROOT_LOCK +  "/"  + lockName + splitStr,  new  byte [0],
                     ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
             System. out .println(CURRENT_LOCK +  " 已经创建" );
             // 取所有子节点
             List<String> subNodes = zk.getChildren(ROOT_LOCK,  false );
             // 取出所有lockName的锁
             List<String> lockObjects =  new  ArrayList<String>();
             for  (String node : subNodes) {
                 String _node = node.split(splitStr)[0];
                 if  (_node. equals (lockName)) {
                     lockObjects.add(node);
                 }
             }
             Collections.sort(lockObjects);
             System. out .println(Thread.currentThread().getName() +  " 的锁是 "  + CURRENT_LOCK);
             // 若当前节点为最小节点,则获取锁成功
             if  (CURRENT_LOCK. equals (ROOT_LOCK +  "/"  + lockObjects. get (0))) {
                 return  true ;
             }
 
             // 若不是最小节点,则找到自己的前一个节点
             String prevNode = CURRENT_LOCK.substring(CURRENT_LOCK.lastIndexOf( "/" ) + 1);
             WAIT_LOCK = lockObjects. get (Collections.binarySearch(lockObjects, prevNode) - 1);
         catch  (InterruptedException e) {
             e.printStackTrace();
         catch  (KeeperException e) {
             e.printStackTrace();
         }
         return  false ;
     }
 
     public  boolean tryLock( long  timeout, TimeUnit unit) {
         try  {
             if  ( this .tryLock()) {
                 return  true ;
             }
             return  waitForLock(WAIT_LOCK, timeout);
         catch  (Exception e) {
             e.printStackTrace();
         }
         return  false ;
     }
 
     // 等待锁
     private  boolean waitForLock(String prev,  long  waitTime) throws KeeperException, InterruptedException {
         Stat stat = zk.exists(ROOT_LOCK +  "/"  + prev,  true );
 
         if  (stat !=  null ) {
             System. out .println(Thread.currentThread().getName() +  "等待锁 "  + ROOT_LOCK +  "/"  + prev);
             this .countDownLatch =  new  CountDownLatch(1);
             // 计数等待,若等到前一个节点消失,则precess中进行countDown,停止等待,获取锁
             this .countDownLatch.await(waitTime, TimeUnit.MILLISECONDS);
             this .countDownLatch =  null ;
             System. out .println(Thread.currentThread().getName() +  " 等到了锁" );
         }
         return  true ;
     }
 
     public  void  unlock() {
         try  {
             System. out .println( "释放锁 "  + CURRENT_LOCK);
             zk.delete(CURRENT_LOCK, -1);
             CURRENT_LOCK =  null ;
             zk.close();
         catch  (InterruptedException e) {
             e.printStackTrace();
         catch  (KeeperException e) {
             e.printStackTrace();
         }
     }
 
     public  Condition newCondition() {
         return  null ;
     }
 
     public  void  lockInterruptibly() throws InterruptedException {
         this . lock ();
     }
 
 
     public  class  LockException extends RuntimeException {
         private  static  final  long  serialVersionUID = 1L;
         public  LockException(String e){
             super(e);
         }
         public  LockException(Exception e){
             super(e);
         }
     }
}

  测试代码:

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
public  class  Test {
     static  int  n = 500;
 
     public  static  void  secskill() {
         System. out .println(--n);
     }
 
     public  static  void  main(String[] args) {
         
         Runnable runnable =  new  Runnable() {
             public  void  run() {
                 DistributedLock  lock  null ;
                 try  {
                     lock  new  DistributedLock( "127.0.0.1:2181" "test1" );
                     lock . lock ();
                     secskill();
                     System. out .println(Thread.currentThread().getName() +  "正在运行" );
                 finally  {
                     if  ( lock  !=  null ) {
                         lock .unlock();
                     }
                 }
             }
         };
 
         for  ( int  i = 0; i < 10; i++) {
             Thread t =  new  Thread(runnable);
             t.start();
         }
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值