public class DistributedIdGenerator {
private ZooKeeper zooKeeper;
private String idNodePath;
private String idNodePrefix;
private AtomicInteger counter;
public DistributedIdGenerator(ZooKeeper zooKeeper, String idNodePath, String idNodePrefix) {
this.zooKeeper = zooKeeper;
this.idNodePath = idNodePath;
this.idNodePrefix = idNodePrefix;
this.counter = new AtomicInteger(0);
}
public long nextId() throws KeeperException, InterruptedException {
while (true) {
// 创建顺序节点
String idNode = zooKeeper.create(idNodePath + "/" + idNodePrefix, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// 获取序号
int seq = Integer.parseInt(idNode.substring(idNodePrefix.length()));
// 判断是否是第一个节点
if (seq == 0) {
// 获取计数器的值
int count = counter.getAndIncrement();
// 如果计数器的值小于等于0,则说明该节点是第一个节点,可以返回ID
if (count <= 0) {
// 生成ID
long timestamp = System.currentTimeMillis();
long nodeId = zooKeeper.getSessionId();
long id = (timestamp << 32) | nodeId;
return id;
}
// 如果计数器的值大于0,则说明该节点不是第一个节点,需要删除节点并重试
zooKeeper.delete(idNode, -1);
} else {
// 获取前一个节点的名称
String prevIdNode = idNodePath + "/" + idNodePrefix + String.format("%010d", seq - 1);
// 判断前一个节点是否存在,如果不存在则重试
if (zooKeeper.exists(prevIdNode, false) == null) {
zooKeeper.delete(idNode, -1);
} else {
// 等待前一个节点被删除
zooKeeper.exists(prevIdNode, true);
}
}
}
}
}
上述代码中,使用ZooKeeper实现了一个分布式ID生成器。在构造函数中指定ZooKeeper客户端和ID节点的路径和前缀,调用nextId方法可以生成一个唯一的ID。生成ID的过程如下:
- 在ZooKeeper中创建一个顺序节点,节点名称为ID节点前缀加上一个数字序号。
- 获取该节点的数字序号,如果是第一个节点,则通过计数器判断是否可以返回ID;如果不是第一个节点,则等待前一个节点被删除。
- 如果可以返回ID,则根据当前时间戳和ZooKeeper的会话ID生成一个64位的long类型数据作为ID。
需要注意的是,这个实现方式并不是绝对高效,因为每个节点都需要等待前一个节点被删除才能生成ID,会有一定的等待时间。但是,由于ZooKeeper已经实现了分布式锁和顺序节点等功能,这个实现方式比较简单,并且可以保证生成的ID是全局唯一的。