zookeepeer ID生成器 (一)

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩 Java 面试宝典》 持续更新 + 史上最全 + 面试必备 2000 页 + 面试必备 + 大厂必备 + 涨薪必备
免费赠送 经典图书:《Java 高并发核心编程(卷 1)加强版》 面试必备 + 大厂必备 + 涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java 高并发核心编程(卷 2)加强版》 面试必备 + 大厂必备 + 涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java 高并发核心编程(卷 3)加强版》 面试必备 + 大厂必备 + 涨薪必备 加尼恩免费领
免费赠送 经典图书:尼恩 Java 面试宝典 最新版 面试必备 + 大厂必备 + 涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000 元 加尼恩领取


疯狂创客圈 Java 分布式聊天室【 亿级流量】实战系列之 -25【 博客园 总入口


写在前面

​ 大家好,我是作者尼恩。目前和几个小伙伴一起,组织了一个高并发的实战社群【疯狂创客圈】。正在开始高并发、亿级流程的 IM 聊天程序 学习和实战

​ 前面,已经完成一个高性能的 Java 聊天程序的四件大事:

接下来,需要进入到分布式开发的环节了。 分布式的中间件,疯狂创客圈的小伙伴们,一致的选择了 zookeeper,不仅仅是由于其在大数据领域,太有名了。更重要的是,很多的著名框架,都使用了 zk。

​ ** 本篇介绍 ZK 的分布式命名服务 ** 中的 分布式 ID 生成器。

说明:本文会以 pdf 格式持续更新,更多最新尼恩 3 高 pdf 笔记,请从下面的链接获取:语雀 或者 码云

1.1. ZK 的分布式命名服务

zookeeper 的命名服务,主要是利用 zookeepeer 节点的树型分层结构和子节点的次序维护能力,为分布式系统中的资源命名与标识能力。

zookeeper 的分布式命名服务,典型的应用场景有:

(1)提供分布式 JNDI 的 API 目录服务功能。

可以把系统中各种 API 接口服务的名称、链接地址放在 zookeeper 的树形分层结果中,提供分布式的 API 调用能力。著名的分布式框架,就是应用了 zookeeper 的分布式的 JNDI 能力。

开源的分布式服务框架 Dubbo 中使用 ZooKeeper 来作为其命名服务,维护全局的服务接口 API 地址列表。在 Dubbo 实现中,provider 服务提供者在启动的时候,向 ZK 上的指定节点 /dubbo/${serviceName}/providers 文件夹下写入自己的 API 地址,这个操作就相当于服务的公开。

consumer 服务消费者启动的时候,订阅节点 /dubbo/{serviceName}/providers 文件夹下的 provider 服务提供者 URL 地址,获得所有的访问提供者的 API。

(2)制作分布式的 ID 生成器,为分布式系统中的每一个数据资源,提供的唯一的标识能力。

在单体服务环境下,我们唯一标识一个数据资源,通常利用数据库的主键自增功能。但是在大量服务器集群的场景下,依赖单体服务的数据库主键自增生成唯一 ID,没有办法满足高并发和高负载的需求。

(3)分布式节点的命名服务

一个分布式系统会有很多的节点组成,而且,节点的数量是不断动态变化的。根据业务的膨胀需要和迎接流量洪峰,可能会加入大量的动态很多节点。流量洪峰过去,就需要下线大量的节点。或者说,由于机器或者网络的原因,一些节点主动的离开的集群。

如何为大量的动态节点命名呢?一种简单的办法是,可以通过配置文件,手动的进行每一个节点的命名。但是如果节点数据量太大,或者说变动频繁,手动命名是不现实的,这就需要用到分布式节点的命名服务。

疯狂创客圈的分布式 IM 实战项目,也会使用分布式命名服务,为每一个 IM 节点动态命名。

说明:本文会以 pdf 格式持续更新,更多最新尼恩 3 高 pdf 笔记,请从下面的链接获取:语雀 或者 码云

1.1.1. 分布式 ID 生成器的类型

在分布式系统中,ID 生成器的使用场景,非常非常多:

(1)大量的数据记录,需要分布式 ID

(2)大量的系统消息,需要分布式 ID

(3)大量的请求日志,如 http 请求记录,需要唯一标识,以便进行后续的用户行为分析和调用链路分析,等等等等。

传统的数据库自增主键,或者单体的自增主键,已经不能满足需求。在分布式系统环境中,迫切需要一个全新的唯一 ID 的系统,这个系统需要满足以下需求:

(1)全局唯一:不能出现重复 ID

(2)高可用:ID 生成系统是基础系统,被许多关键系统调用,一旦宕机,会造成严重影响。

分布式唯一 ID 生成分案有很多种:

(1) java 的 UUID

(2) 利用分布式缓存 Redis 生成 ID

利用 Redis 的原子操作 INCR 和 INCRBY,生成全局唯一的 ID。

(3) Twitter 的 snowflake 算法

(4) ZooKeeper 生成 ID

利用 ZooKeeper 的顺序节点,生成全局唯一的 ID。

(5) MongoDb 的 ObjectId

利用分布式 Nosql MongDB,生成全局唯一的 ID。

首先分析一下 java 语言中的 UUID 方案。

UUID 方案

UUID 是 Universally Unique Identifier 的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID 在其他语言中也叫 GUID,在 java 中,生成 UUID 的代码很简单:

String uuid = UUID.randomUUID().toString()

一个 UUID 是 16 字节长的数字,一共 128 位。通常以 36 字节的字符串表示,比如:3F2504E0-4F89-11D3-9A0C-0305E82C3301。 使用的时候,可以把中间的 4 个中划线去掉,剩下 32 位字符串。

UUID 经由一定的算法机器生成,为了保证 UUID 的唯一性,规范定义了包括网卡 MAC 地址、时间戳、名字空间 (Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成 UUID 的算法。UUID 的只能由计算机生成。

UUID 的优点:本地生成 ID,不需要进行远程调用,时延低,性能高。

UUID 的缺点:UUID 过长,16 字节 128 位,通常以 36 长度的字符串表示,很多场景不适用,比如,由于 UUID 没有排序,无法保证趋势递增,用做数据库索引字段的效率就很低,新增记录存储入库时性能差

从高并发,高可用的角度出发,通过 ZooKeeper 实现分布式系统唯一 ID 的方案,是最为合适的解决方案之一。

说明:本文会以 pdf 格式持续更新,更多最新尼恩 3 高 pdf 笔记,请从下面的链接获取:语雀 或者 码云

1.1.2. ZK 生成分布式 ID

通过创建 ZK 的顺序模式的节点,可以生成全局唯一的 ID。

代码如下:


    private String createSeqNode(String pathPefix) {
      try {
            // 创建一个 ZNode 顺序节点
            String destPath = client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.*EPHEMERAL_SEQUENTIAL*)
// 避免 zookeeper 的顺序节点暴增,可以删除创建的顺序节点
                    .forPath(pathPefix);
            return destPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

节点创建完成后,会返回节点的完整的层次路径,生成的序号,放置在路径的末尾。一般为 10 位数字字符。

通过截取路径末尾的数字,作为新生成的 ID。截取数字的代码如下:

public String makeId(String nodeName) {
    String str = createSeqNode(nodeName);
    if (null == str) {
        return null;
    }
    int index = str.lastIndexOf(nodeName);
    if (index >= 0) {
        index += nodeName.length();
        return index <= str.length() ? str.substring(index) : "";
    }
    return str;
}

调用的代码如下:

*/\*** ** create by 尼恩 @ 疯狂创客圈 * **\*/*@Slf4j
public class IDMakerTester {
    @Test
    public void testMakeId() {
        IDMaker idMaker = new IDMaker();
        idMaker.init();
        String nodeName = "/test/IDMaker/ID-";
        for (int i = 0; i < 10; i++) {
            String id = idMaker.makeId(nodeName);
            log.info("第"+ i + "个创建的 id 为:" + id);
        }
        idMaker.destroy();
    }
}

下面是部分的运行输出:

第0个创建的id为:0000000010

第1个创建的id为:0000000011

写在最后

​ 下一篇:基于 zookeeper 实现 snowflake 算法 。


说明:本文会以 pdf 格式持续更新,更多最新尼恩 3 高 pdf 笔记,请从下面的链接获取:语雀 或者 码云