问题来源:
微服务的框架,有的服务部署了多个实例,同时需要全局的自增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, API文档http://zookeeper.apache.org/doc/current/api/index.html,
PERSISTENT_SEQUENTIAL
public static final CreateMode PERSISTENT_SEQUENTIAL
The znode will not be automatically deleted upon client's disconnect, and its name will be appended with a monotonically increasing number.
2,zookeeper关于Sequence Nodes 介绍
http://zookeeper.apache.org/doc/current/zookeeperProgrammers.html#Sequence+Nodes+--+Unique+Naming
When creating a znode you can also request that ZooKeeper append a monotonically increasing counter to the end of path. This counter is unique to the parent znode. The counter has a format of %010d -- that is 10 digits with 0 (zero) padding (the counter is formatted in this way to simplify sorting), i.e. "<path>0000000001". See Queue Recipe for an example use of this feature. Note: the counter used to store the next sequence number is a signed int (4bytes) maintained by the parent node, the counter will overflow when incremented beyond 2147483647 (resulting in a name "<path>-2147483648").
程序简单介绍:。
程序每运行一次就或生成一个新的自增1的path, path就是sequece, 例如0000000001。sequence是10位,也就是最大可以达到10亿,一般我们系统针对单个对象10亿够用了,如果不够的可以设置两个序列,分别为主序列和副序列,这样就足够多了。
本文没有删除旧的sequence,实际项目中最好需要获取id后,删除旧的序列,要不然zk的path会越来越多.
网友评论