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();
}
}
}
|