美文网首页
分布式自增序列的实现(一) ---分布式序号生成器

分布式自增序列的实现(一) ---分布式序号生成器

作者: 不1见2不3散4 | 来源:发表于2018-11-25 00:04 被阅读0次

    问题来源:

    微服务的框架,有的服务部署了多个实例,同时需要全局的自增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会越来越多.

    截图

    zkSequence.png

    相关文章

      网友评论

          本文标题:分布式自增序列的实现(一) ---分布式序号生成器

          本文链接:https://www.haomeiwen.com/subject/mxuuqqtx.html