Zookeeper 分布式命名服务

zookeeper的命名服务有两个应用方向,一个是提供类似JNDI的功能,利用zookeepeer的树型分层结构,可以把系统中各种服务的名称、地址以及目录信息存放在zookeeper,需要的时候去zookeeper中读取。

另一个,是利用zookeeper顺序节点的特性,制作分布式的ID生成器,写过数据库应用的朋友都知道,我们在往数据库表中插入记录时,通常需要为该记录创建唯一的ID,在单机环境中我们可以利用数据库的主键自增功能。但在分布式环境则无法使用,有一种方式可以使用UUID,但是它的缺陷是没有规律,很难理解。利用zookeeper顺序节点的特性,我们可以生成有顺序的,容易理解的,同时支持分布式环境的序列号。

代码实现

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
public class IdMaker {

private ZkClient client = null;
private final String server;
// zookeeper顺序节点的父节点
private final String root;
// 顺序节点的名称
private final String nodeName;
// 标识当前服务是否正在运行
private volatile boolean running = false;
private ExecutorService cleanExector = null;

public enum RemoveMethod{
NONE,IMMEDIATELY,DELAY

}

public IdMaker(String zkServer,String root,String nodeName){

this.root = root;
this.server = zkServer;
this.nodeName = nodeName;

}

// 启动服务
public void start() throws Exception {

if (running)
throw new Exception("server has stated...");
running = true;

init();

}

// 停止服务
public void stop() throws Exception {

if (!running)
throw new Exception("server has stopped...");
running = false;

freeResource();

}

// 初始化服务资源
private void init(){

client = new ZkClient(server,5000,5000,new BytesPushThroughSerializer());
cleanExector = Executors.newFixedThreadPool(10);
try{
client.createPersistent(root,true);
}catch (ZkNodeExistsException e){
//ignore;
}

}

// 释放服务器资源
private void freeResource(){

// 释放线程池
cleanExector.shutdown();
try{
cleanExector.awaitTermination(2, TimeUnit.SECONDS);
}catch(InterruptedException e){
e.printStackTrace();
}finally{
cleanExector = null;
}

if (client!=null){
client.close();
client=null;

}
}

// 检测当前服务是否正在运行
private void checkRunning() throws Exception {
if (!running)
throw new Exception("请先调用start");

}

// 从顺序节点名中提取我们要的ID值
private String ExtractId(String str){
int index = str.lastIndexOf(nodeName);
if (index >= 0){
index+=nodeName.length();
return index <= str.length()?str.substring(index):"";
}
return str;

}

// 生成ID
public String generateId(RemoveMethod removeMethod) throws Exception{
checkRunning();

// 构造顺序节点的完整路径
final String fullNodePath = root.concat("/").concat(nodeName);
// 创建持久化顺序节点
final String ourPath = client.createPersistentSequential(fullNodePath, null);

// 避免zookeeper的顺序节点暴增,直接删除掉刚创建的顺序节点
if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){ // 立即删除
client.delete(ourPath);
}else if (removeMethod.equals(RemoveMethod.DELAY)){ // 延迟删除
cleanExector.execute(new Runnable() { // 用线程池执行删除,让generateId()方法尽快返回
public void run() {
client.delete(ourPath);
}
});

}
//node-0000000000, node-0000000001
return ExtractId(ourPath);
}

}
public class TestIdMaker {

public static void main(String[] args) throws Exception {

IdMaker idMaker = new IdMaker("192.168.1.105:2181",
"/NameService/IdGen", "ID");
idMaker.start();

try {
for (int i = 0; i < 10; i++) {
String id = idMaker.generateId(RemoveMethod.DELAY);
System.out.println(id);
}
} finally {
idMaker.stop();

}
}

}