问题来源:

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

程序简单介绍:。
程序每运行一次就或生成一个新的自增1的path, path就是sequece, 例如0000000001。sequence是10位,也就是最大可以达到10亿,一般我们系统针对单个对象10亿够用了,如果不够的可以设置两个序列,分别为主序列和副序列,这样就足够多了。
本文没有删除旧的sequence,实际项目中最好需要获取id后,删除旧的序列,要不然zk的path会越来越多.

截图

在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐