Zookeeper在分布式命名服务中的实践

Java学习+面试指南:https://javaxiaobear.cn

命名服务是为系统中的资源提供标识能力。ZooKeeper的命名服务主要是利用ZooKeeper节点的树形分层结构和子节点的顺序维护能力,来为分布式系统中的资源命名。

哪些应用场景需要用到分布式命名服务呢?典型的有:

  • 分布式API目录

  • 分布式节点命名

  • 分布式ID生成器

1、分布式API目录

为分布式系统中各种API接口服务的名称、链接地址,提供类似JNDI(Java命名和目录接口)中的文件系统的功能。借助于ZooKeeper的树形分层结构就能提供分布式的API调用功能。

著名的Dubbo分布式框架就是应用了ZooKeeper的分布式的JNDI功能。在Dubbo中,使用ZooKeeper维护的全局服务接口API的地址列表。大致的思路为:

  • 服务提供者(Service Provider)在启动的时候,向ZooKeeper上的指定节点/dubbo/${serviceName}/providers写入自己的API地址,这个操作就相当于服务的公开。

  • 服务消费者(Consumer)启动的时候,订阅节点/dubbo/{serviceName}/providers下的服务提供者的URL地址,获得所有服务提供者的API。

image-20231217200800728

2、分布式节点命名

一个分布式系统通常会由很多的节点组成,节点的数量不是固定的,而是不断动态变化的。比如说,当业务不断膨胀和流量洪峰到来时,大量的节点可能会动态加入到集群中。而一旦流量洪峰过去了,就需要下线大量的节点。再比如说,由于机器或者网络的原因,一些节点会主动离开集群。

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

可用于生成集群节点的编号的方案:

(1)使用数据库的自增ID特性,用数据表存储机器的MAC地址或者IP来维护。

(2)使用ZooKeeper持久顺序节点的顺序特性来维护节点的NodeId编号。

在第2种方案中,集群节点命名服务的基本流程是:

  • 启动节点服务,连接ZooKeeper,检查命名服务根节点是否存在,如果不存在,就创建系统的根节点。

  • 在根节点下创建一个临时顺序ZNode节点,取回ZNode的编号把它作为分布式系统中节点的NODEID。

  • 如果临时节点太多,可以根据需要删除临时顺序ZNode节点。

3、分布式ID生成器

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

  • 大量的数据记录,需要分布式ID。

  • 大量的系统消息,需要分布式ID。

  • 大量的请求日志,如restful的操作记录,需要唯一标识,以便进行后续的用户行为分析和调用链路分析。

  • 分布式节点的命名服务,往往也需要分布式ID。

  • 。。。

传统的数据库自增主键已经不能满足需求。在分布式系统环境中,迫切需要一种全新的唯一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:MongoDB是一个分布式的非结构化NoSQL数据库,每插入一条记录会自动生成全局唯一的一个“_id”字段值,它是一个12字节的字符串,可以作为分布式系统中全局唯一的ID。

前面我写过一篇关于分布式ID的设计与实现,关于其他的实现可参考这篇哈

1、基于Zookeeper实现分布式ID生成器

在ZooKeeper节点的四种类型中,其中有以下两种类型具备自动编号的能力

  • PERSISTENT_SEQUENTIAL持久化顺序节点。

  • EPHEMERAL_SEQUENTIAL临时顺序节点。

ZooKeeper的每一个节点都会为它的第一级子节点维护一份顺序编号,会记录每个子节点创建的先后顺序,这个顺序编号是分布式同步的,也是全局唯一的。

可以通过创建ZooKeeper的临时顺序节点的方法,生成全局唯一的ID

/**
 * @author 小熊学Java
 * @version 1.0
 * @description: TODO
 * @date 2023/12/17 21:08
 */
public class IDMaker {
    private static final String ZOOKEEPER_ADDRESS = "ip:2181";
    private static final int SESSION_TIMEOUT = 3000;

    public CuratorFramework client;
    public IDMaker() {
        // 重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        client = CuratorFrameworkFactory.newClient(ZOOKEEPER_ADDRESS, retryPolicy);
        client.start();
    }

    /**
     * 根据路径创建
     * @param path
     * @return
     * @throws Exception
     */
    public String createSeqNode(String path) throws Exception{
        return client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath(path);
    }

    public String createId(String path) throws Exception{
        String seqNode = createSeqNode(path);
        if (!StringUtils.isBlank(seqNode)){
            //获取末尾的序号
            int i = seqNode.lastIndexOf(path);
            if (i > 0){
                i += path.length();
                return i <= seqNode.length() ? seqNode.substring(i) : "";
            }
        }
        return seqNode;
    }

    public static void main(String[] args) throws InterruptedException {
        IDMaker idMaker = new IDMaker();
        String nodePath = "/javaxiaobear";
        for(int i=0;i<5;i++){
            new Thread(()->{
                for (int j=0;j<10;j++){
                    String id = null;
                    try {
                        id = idMaker.createId(nodePath);
                        System.out.println(Thread.currentThread().getName() + "线程第" + j + "个创建的id为"+ id);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            },"thread"+i).start();
        }

        Thread.sleep(Integer.MAX_VALUE);
    }
}

执行结果

image-20231217222047186

2、基于Zookeeper实现SnowFlakeID算法

Twitter(推特)的SnowFlake算法是一种著名的分布式服务器用户ID生成算法。SnowFlake算法所生成的ID是一个64bit的长整型数字,如图10-2所示。这个64bit被划分成四个部分,其中后面三个部分分别表示时间戳、工作机器ID、序列号。

image-20231218155206354

SnowFlakeID的四个部分,具体介绍如下:

(1)第一位 占用1 bit,其值始终是0,没有实际作用。

(2)时间戳 占用41 bit,精确到毫秒,总共可以容纳约69年的时间。

(3)工作机器id占用10 bit,最多可以容纳1024个节点。

(4)序列号 占用12 bit。这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。

在工作节点达到1024顶配的场景下,SnowFlake算法在同一毫秒最多可以生成的ID数量为: 1024 * 4096 =4194304,在绝大多数并发场景下都是够用的。

SnowFlake算法的优点:

  • 生成ID时不依赖于数据库,完全在内存生成,高性能和高可用性。

  • 容量大,每秒可生成几百万个ID。

  • ID呈趋势递增,后续插入数据库的索引树时,性能较高。

SnowFlake算法的缺点:

  • 依赖于系统时钟的一致性,如果某台机器的系统时钟回拨了,有可能造成ID冲突,或者ID乱序。

  • 在启动之前,如果这台机器的系统时间回拨过,那么有可能出现ID重复的危险。

基于zookeeper实现雪花算法:

public class SnowflakeIdGenerator {
 
    /**
     * 单例
     */
    public static SnowflakeIdGenerator instance =
            new SnowflakeIdGenerator();
 
 
    /**
     * 初始化单例
     *
     * @param workerId 节点Id,最大8091
     * @return the 单例
     */
    public synchronized void init(long workerId) {
        if (workerId > MAX_WORKER_ID) {
            // zk分配的workerId过大
            throw new IllegalArgumentException("woker Id wrong: " + workerId);
        }
        instance.workerId = workerId;
    }
 
    private SnowflakeIdGenerator() {
 
    }
 
 
    /**
     * 开始使用该算法的时间为: 2017-01-01 00:00:00
     */
    private static final long START_TIME = 1483200000000L;
 
    /**
     * worker id 的bit数,最多支持8192个节点
     */
    private static final int WORKER_ID_BITS = 13;
 
    /**
     * 序列号,支持单节点最高每毫秒的最大ID数1024
     */
    private final static int SEQUENCE_BITS = 10;
 
    /**
     * 最大的 worker id ,8091
     * -1 的补码(二进制全1)右移13位, 然后取反
     */
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
 
    /**
     * 最大的序列号,1023
     * -1 的补码(二进制全1)右移10位, 然后取反
     */
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
 
    /**
     * worker 节点编号的移位
     */
    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
 
    /**
     * 时间戳的移位
     */
    private final static long TIMESTAMP_LEFT_SHIFT = WORKER_ID_BITS + SEQUENCE_BITS;
 
    /**
     * 该项目的worker 节点 id
     */
    private long workerId;
 
    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;
 
    /**
     * 当前毫秒生成的序列
     */
    private long sequence = 0L;
 
    /**
     * Next id long.
     *
     * @return the nextId
     */
    public Long nextId() {
        return generateId();
    }
 
    /**
     * 生成唯一id的具体实现
     */
    private synchronized long generateId() {
        long current = System.currentTimeMillis();
 
        if (current < lastTimestamp) {
            // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,出现问题返回-1
            return -1;
        }
 
        if (current == lastTimestamp) {
            // 如果当前生成id的时间还是上次的时间,那么对sequence序列号进行+1
            sequence = (sequence + 1) & MAX_SEQUENCE;
 
            if (sequence == MAX_SEQUENCE) {
                // 当前毫秒生成的序列数已经大于最大值,那么阻塞到下一个毫秒再获取新的时间戳
                current = this.nextMs(lastTimestamp);
            }
        } else {
            // 当前的时间戳已经是下一个毫秒
            sequence = 0L;
        }
 
        // 更新上次生成id的时间戳
        lastTimestamp = current;
 
        // 进行移位操作生成int64的唯一ID
 
        //时间戳右移动23位
        long time = (current - START_TIME) << TIMESTAMP_LEFT_SHIFT;
 
        //workerId 右移动10位
        long workerId = this.workerId << WORKER_ID_SHIFT;
 
        return time | workerId | sequence;
    }
 
    /**
     * 阻塞到下一个毫秒
     */
    private long nextMs(long timeStamp) {
        long current = System.currentTimeMillis();
        while (current <= timeStamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/272753.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是工厂方法模式,工厂方法模式解决了什么问题?

工厂方法模式是一种创建型设计模式&#xff0c;它定义了一个用于创建对象的接口&#xff0c;但将实际的实例化过程延迟到子类中。这样&#xff0c;客户端代码在不同的子类中实例化具体对象&#xff0c;而不是直接实例化具体类。工厂方法模式允许一个类的实例化延迟到其子类&…

[Angular] 笔记 9:list/detail 页面以及@Output

1. Output input 好比重力&#xff0c;向下传递数据&#xff0c;list 传给 detail&#xff0c;smart 组件传给 dumb 组件&#xff0c;父组件传给子组件。input 顾名思义&#xff0c;输入数据给组件。 output 与之相反&#xff0c;好比火箭&#xff0c;向上传递数据或事件。ou…

5.微服务代码模型

1.微服务代码模型 代码分层 在微服务代码模型里&#xff0c;我们分别定义了用户接口层、并分别为它们建立了interfaces、application、domain和infrastructure四个一级代码目录&#xff1b; interfaces(用户接口层): 它主要存放用户接口层与前端应用交互、数据转换和交互相关…

一起玩儿物联网人工智能小车(ESP32)——16. 用ESP32的GPIO控制智能小车运动起来(MicroPython)

摘要&#xff1a;本文介绍用MicroPython实现ESP32的GPIO控制智能小车朝各个方向运动 前边的Mixly开发之后&#xff0c;对应生成的代码是C语言的。可能很多人都觉得C语言很难学&#xff0c;现在学Python的人很多&#xff0c;觉得学起来更容易一些。其实&#xff0c;语言本身的难…

探索前端开发趋势:2023年的新兴技术与发展方向

随着科技的不断发展&#xff0c;前端开发领域也在不断演进。本文将详细介绍2023年前端开发的新兴技术和发展趋势&#xff0c;为开发者们指明前端技术的发展方向和面临的挑战。从WebAssembly、PWA到低代码开发&#xff0c;激动人心的全新前景等你探索。 随着科技的快速发展&…

计算机图形图像技术复习资料

一、考试题型 1、简述题&#xff08;10分4题&#xff0c;共40分&#xff09; 2、计算题&#xff08;共10分&#xff09; 3、编程题&#xff08;共30分&#xff09; 4、问答题&#xff08;共20分&#xff09; 二、复习提纲 1、简答题 &#xff08;1&#xff09;第1章的基本…

平面灯阵中寻找最大正方形边界 - 华为机试真题题解

分值: 300分 题解: Java / Python / C++ 题目描述 现在有一个二维数组来模拟一个平面灯阵,平面灯阵中每个位置灯处于点亮或熄灭,分别对应数组每个元素取值只能为1或0,现在需要找一个正方形边界,其每条边上的灯都是点亮(对应数组中元素的值为1)的,且该正方形面积最大。 …

【hcie-cloud】【12】华为云Stack故障处理【故障处理通用处理原则、常见华为云Stack故障处理(计算域故障场景)】【上】

文章目录 前言故障处理通用处理原则故障处理流程故障信息收集及故障范围、类型识别ManageOne运维面收集告警信息AutoOps工具故障场景信息收集AutoOps工具自动化采集HCS信息 (1)AutoOps工具自动化采集HCS信息 (2)故障初期定位方向故障恢复例行维护讨论: 哪一环比较重要&#xff…

Android studio 花式按键

一、activity_main.xml代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.a…

Xmake v2.8.6 发布,新的打包插件:XPack

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。 它非常的轻量&#xff0c;没有任何依赖&#xff0c;因为它内置了 Lua 运行时。 它使用 xmake.lua 维护项目构建&#xff0c;相比 makefile/CMakeLists.txt&#xff0c;配置语法更加简洁直观&#xff0c;对新手非常友好&#x…

企业级实战项目:基于 pycaret 自动化预测公司是否破产

本文系数据挖掘实战系列文章&#xff0c;我跟大家分享一个数据挖掘实战&#xff0c;与以往的数据实战不同的是&#xff0c;用自动机器学习方法完成模型构建与调优部分工作&#xff0c;深入理解由此带来的便利与效果。 1. Introduction 本文是一篇数据挖掘实战案例&#xff0c;…

深信服技术认证“SCCA-C”划重点:云计算基础

为帮助大家更加系统化地学习云计算知识&#xff0c;高效通过云计算工程师认证&#xff0c;深信服特推出“SCCA-C认证备考秘笈”&#xff0c;共十期内容。“考试重点”内容框架&#xff0c;帮助大家快速get重点知识。 划重点来啦 *点击图片放大展示 深信服云计算认证&#xff08…

华锐视点为广汽集团打造VR汽车在线展厅,打破地域限制,尽享购车乐趣

随着科技的飞速发展&#xff0c;我们正在进入一个全新的时代——元宇宙时代。元宇宙是一个虚拟的世界&#xff0c;它不仅能够模拟现实世界&#xff0c;还能够创造出现实世界无法实现的事物。而汽车行业作为人类生活的重要组成部分&#xff0c;也在积极探索与元宇宙的融合&#…

如何使用ArcGIS Pro将Excel表转换为SHP文件

有的时候我们得到的数据是一张张的Excel表格&#xff0c;如果想要在ArcGIS Pro中进行分析或者制图则需要先转换为SHP格式&#xff0c;这里为大家介绍一下转换方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的POI数据&#xff0c;除了P…

Jenkins 自动设置镜像版本号

使用Jenkins环境变量当作镜像版本号 这样version变量就是版本号,在镜像构建的过程中可以使用 docker build 之后&#xff0c;如果有自己的镜像库&#xff0c;肯定要docker push 一下 至于部署的步骤&#xff0c;一般需要stop并删除原有的容器.我这里用的是docker-compose。同样…

OKCC语音机器人的人机耦合来啦

目前市场上语音机器人的外呼形式基本就分为三种&#xff0c;一种纯AI外呼&#xff0c;第二种也是目前主流的AI外呼转人工。那么第三种也可能是未来的一种趋势&#xff0c;人机耦合&#xff0c;或者也叫人机协同。 那么什么是人机耦合呢&#xff1f; 人机耦合是为真人坐席创造相…

线性代数基础【3】向量

第一节 向量的概念与运算 一、基本概念 ①向量 ②向量的模(长度) ③向量的单位化 ④向量的三则运算 ⑤向量的内积 二、向量运算的性质 (一)向量三则运算的性质 α β β αα (β γ) (α β) γk (α β) kα kβ(k l) α kα lα (二)向量内积运…

什么是GeoTrust?

在当今数字化时代&#xff0c;网络安全是至关重要的。GeoTrust&#xff0c;作为全球领先的SSL证书提供商&#xff0c;致力于为用户提供卓越的数字安全解决方案。 产品与服务&#xff1a; 域名验证证书&#xff1a; 提供快速简便的验证&#xff0c;是保护网站和用户数据的基础。…

华清远见嵌入式学习——ARM——作业4

作业要求&#xff1a; 代码运行效果图&#xff1a; 代码&#xff1a; do_irq.c: #include "key_it.h" extern void printf(const char *fmt, ...); unsigned int i 0;//延时函数 void delay(int ms) {int i,j;for(i0;i<ms;i){for(j0;j<2000;j);} }void do_i…

【linux】Linux重定向

在Linux操作系统中&#xff0c;命令行界面是一个强大的工具&#xff0c;它允许用户与系统进行高效的交互。重定向是命令行中一个非常重要的概念&#xff0c;它可以改变命令输入和输出的默认路径。通过重定向&#xff0c;用户可以将数据从一个程序传递到另一个程序&#xff0c;或…
最新文章