问题来源:
微服务的框架,有的服务部署了多个实例,同时需要全局的自增id,当然使用uuid作为id是可以不用关系id的生成,但是如果要是序列sequence就需要自己实现id的生成,但是分布式环境下如何保证不会重复生成相同的id?
解决办法
1, 使用分布式锁,每次生成sequence时先获取全局锁,然后获取sequence,接着sequence+1并保存到持久存储中,最后释放锁。
2,使用已有的框架提供分布式锁的,例如Redis,ZooKeeper。
本文选择使用zookeeper作为例子, 具体代码在这里,欢迎加星,fork
具体实现
我使用zk的官方client,zk还有非常好用的curator客户端。
因此pom文件需要添加如下依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
具体示例代码如下,本代码非生产级别代码,只是作为示例。
package com.yq;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Simple to Introduction
* className: ZKDemoOneApplication
*
* @author EricYang
* @version 2018/8/24 23:43
*/
public class ZKClientSequenceApp {
private static ZooKeeper zk = null;
private static final Logger log = LoggerFactory.getLogger(ZKClientSequenceApp.class);
private static final String ZK_SERVERS = "127.0.0.1:2181";
private static final int SESSION_TIME = 2000;
private static final String PARENT_PATH_NAME_TEST = "/yqseq_test001";
private static final String CHILD_PATH_NAME_TEST = "/yqseq_test001/";
public static void main(String[] args) throws Exception {
zk = new ZooKeeper(ZK_SERVERS,SESSION_TIME, null);
Stat stat = zk.exists(PARENT_PATH_NAME_TEST, null);
if (stat == null) {
String zkParentPath = zk.create(PARENT_PATH_NAME_TEST, "helloWorld".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
log.info("zkParentPath={}", zkParentPath);
}
String zkPath = zk.create(CHILD_PATH_NAME_TEST, "helloWorld".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
log.info("zkPath={}", zkPath);
List<String> children = zk.getChildren(PARENT_PATH_NAME_TEST, null);
for(String seq : children) {
log.info("seq={}", seq);
}
log.info("start done.");
}
}
关于zookeeper如何保证CreateMode.PERSISTENT_SEQUENTIAL path不会重复后面会加上解释。
程序每运行一次就或生成一个新的自增1的path, path就是sequece, 例如0000000001。 本文没有删除旧的sequence,实际项目中最好需要获取id后,删除旧的序列,要不然zk的path会越来越多.