背景分析
分布式系统中,我们要保证ID唯一,一般情况下会选择UUID来作为全局唯一的ID。在需求变更时,需要有序的且唯一的全局ID,这时候我们可以考虑使用zookeeper来作为全局ID的生成器。
原理介绍
- zookeeper是一个分布式的,高可用的服务
- zookeeper自带生成顺序节点的功能,可以使用其生成顺序节点的编号来作为ID
例如一下zookeeper中顺序节点的结构
每个文件节点都有一个顺序编号,每次新建顺序节点都是在上一个顺序编号上加1来作为新的顺序编号,保证了生成ID的有序。
Java客户端代码
首先我们引入zkCLient作为zookeeper的客户端,在pom.xml文件中加入
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
下面是java代码
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.serialize.BytesPushThroughSerializer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class IdMaker {
private ZkClient zkClient;
//服务器地址
private final String server;
//根目录
private final String root;
//顺序节点名称
private final String nodeName;
//服务运行状态
private volatile boolean running = false;
private ExecutorService executorService;
public enum RemoveMethod{
NONE,IMMEDIATELY,DELAY
}
public IdMaker(String server, String root, String nodeName) {
this.server = server;
this.root = root;
this.nodeName = nodeName;
}
public void start() throws Exception{
if(running){
throw new Exception("server is running!");
}
running = true;
init();
}
public void stop() throws Exception{
if(!running){
throw new Exception("server is not running!");
}
running = false;
freeResource();
}
private void init(){
//实例化zookeeper对象
zkClient = new ZkClient(server, 5000, 5000, new BytesPushThroughSerializer());
executorService = Executors.newFixedThreadPool(10);
try {
zkClient.createPersistent(root, true);
}catch (ZkNoNodeException e){
//igore
}
}
private void freeResource(){
executorService.shutdown();
executorService = null;
if(zkClient != null){
zkClient.close();
zkClient = null;
}
}
private void checkRunning() throws Exception{
if(!running){
throw new Exception("server is not running, please call start()");
}
}
public String generateId(RemoveMethod removeMethod) throws Exception {
checkRunning();
final String fullNodePath = root.concat("/").concat(nodeName);
final String ourPath = zkClient.createPersistentSequential(fullNodePath, null);
if(removeMethod.equals(RemoveMethod.IMMEDIATELY)){
zkClient.delete(ourPath);
}else if(removeMethod.equals(RemoveMethod.DELAY)){
executorService.execute(new Runnable() {
public void run() {
zkClient.delete(ourPath);
}
});
}
//node-0000000001
return extratId(ourPath);
}
private String extratId(String ourPath){
int index = ourPath.indexOf(nodeName);
if(index > 0){
index += nodeName.length();
return index <= ourPath.length() ? ourPath.substring(index) : "";
}
return ourPath;
}
}