zoukankan      html  css  js  c++  java
  • zookeepeer ID生成器 (一)

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


    写在前面

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

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

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

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

    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节点动态命名。

    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的方案,是最为合适的解决方案之一。

    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 算法 。


    疯狂创客圈 亿级流量 高并发IM 实战 系列

    • Java (Netty) 聊天程序【 亿级流量】实战 开源项目实战

    
    
  • 相关阅读:
    WPF之感触
    C# WinForm 给DataTable中指定位置添加列
    MyEclipse 8.6 download 官方下载地址
    将博客搬至CSDN
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
    Building Microservices with Spring Cloud
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/10226849.html
Copyright © 2011-2022 走看看