Zookeeper分布式命名服务实战
目录
分布式命名服务
分布式API目录
分布式节点的命名
分布式的ID生成器
分布式的ID生成器方案:
基于Zookeeper实现分布式ID生成器
基于Zookeeper实现SnowFlakeID算法
分布式命名服务
命名服务是为系统中的资源提供标识能力。ZooKeeper的命名服务主要是利用ZooKeeper节点的树形分层结构和子节点的顺序维护能力,来为分布式系统中的资源命名。需要用到分布式命名服务的应用场景典型的有:分布式API目录、分布式节点命名、分布式ID生成器。
分布式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。
分布式节点的命名
一个分布式系统通常会由很多的节点组成,节点的数量不是固定的,而是不断动态变化的。比如说, 当业务不断膨胀和流量洪峰到来时,大量的节点可能会动态加入到集群中。而一旦流量洪峰过去了, 就需要下线大量的节点。这就需要用到分布式节点的命名服务。
可用于生成集群节点的编号的方案:
1. 使用数据库的自增ID特性,用数据表存储机器的MAC地址或者IP来维护。
2. 使用ZooKeeper持久顺序节点的顺序特性来维护节点的NodeId编号。
在第2种方案中,集群节点命名服务的基本流程是:
启动节点服务,连接ZooKeeper,检查命名服务根节点是否存在,如果不存在,就创建系统的根节点。 在根节点下创建一个临时顺序ZNode节点,取回ZNode的编号把它作为分布式系统中节点的NODEID。 如果临时节点太多,可以根据需要删除临时顺序ZNode节点。
分布式的ID生成器
分布式系统中,分布式ID生成器的使用场景非常多,比如大量的数据记录、大量的系统消息 、大量的请求日志、分布式节点的命名服务等
传统的数据库自增主键已经不能满足需求。在分布式系统环境中,需要一种全新的唯一ID系统, 这种系统需要满足以下需求:
全局唯一:不能出现重复ID。
高可用: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。
基于Zookeeper实现分布式ID生成器
在ZooKeeper节点的四种类型中,其中有以下两种类型具备自动编号的能力:
PERSISTENT_SEQUENTIAL持久化顺序节点。
EPHEMERAL_SEQUENTIAL临时顺序节点。
ZooKeeper的每一个节点都会为它的第一级子节点维护一份顺序编号,会记录每个子节点创建的先后 顺序,这个顺序编号是分布式同步的,也是全局唯一的。可以通过创建ZooKeeper的临时顺序节点的方法,生成全局唯一的ID
@Slf4j
public class IDMaker extends CuratorBaseOperations {private String createSeqNode(String pathPefix) throws Exception {CuratorFramework curatorFramework = getCuratorFramework();//创建一个临时顺序节点String destPath = curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(pathPefix);return destPath;}public String makeId(String path) throws Exception {String str = createSeqNode(path);if(null != str){//获取末尾的序号int index = str.lastIndexOf(path);if(index>=0){index+=path.length();return index<=str.length() ? str.substring(index):"";}}return str;}
}
@Slf4j
public class IDMakerTest {@Testpublic void testMarkId() throws Exception {IDMaker idMaker = new IDMaker();idMaker.init();String pathPrefix = "/idmarker/id-";//模拟5个线程创建idfor(int i=0;i<5;i++){new Thread(()->{for (int j=0;j<10;j++){String id = null;try {id = idMaker.makeId(pathPrefix);log.info("线程{}第{}次创建id为{}",Thread.currentThread().getName(),j,id);} catch (Exception e) {e.printStackTrace();}}},"thread"+i).start();}}
}
执行结果如下 :
如果是每秒钟要几万、几十万的id,这种方案是不行的,受限于zookeeper的顺序节点写操作。
基于Zookeeper实现SnowFlakeID算法
Twitter(推特)的SnowFlake算法是一种著名的分布式服务器用户ID生成算法。SnowFlake算法所生成的ID是一个64bit的长整型数字。这个64bit被划分成四个部分,其中后面三个部分分别表示时间戳、工作机器ID、序列号。
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算法的优点:
1. 生成ID时不依赖于数据库,完全在内存生成,高性能和高可用性。
2. 容量大,每秒可生成几百万个ID。
3. ID呈趋势递增,后续插入数据库的索引树时,性能较高。
SnowFlake算法的缺点:
1. 依赖于系统时钟的一致性,如果某台机器的系统时钟回拨了,有可能造成ID冲突,或者ID乱序。 2. 在启动之前,如果这台机器的系统时间回拨过,那么有可能出现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生成的时间戳,说明系统时钟回退过,出现问题返回-1return -1;}if (current == lastTimestamp) {// 如果当前生成id的时间还是上次的时间,那么对sequence序列号进行+1sequence = (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;}}
相关文章:

Zookeeper分布式命名服务实战
目录 分布式命名服务 分布式API目录 分布式节点的命名 分布式的ID生成器 分布式的ID生成器方案: 基于Zookeeper实现分布式ID生成器 基于Zookeeper实现SnowFlakeID算法 分布式命名服务 命名服务是为系统中的资源提供标识能力。ZooKeeper的命名服务主要是利用Z…...

DEV-C++ ege.h库 绘图教程(六)
一、前情回顾 DEV-C ege.h库 绘图教程(一) DEV-C ege.h库 绘图教程(二) DEV-C ege.h库 绘图教程(三) DEV-C ege.h库 绘图教程(四) DEV-C ege.h库 绘图教程(五)…...

MySQL原理(一)架构组成之物理文件组成
目录 一、日志文件 1、错误日志 Error Log 1.1、作用: 1.2、开启关闭: 1.3、使用 2、二进制日志 Binary Log & Binary Log Index 2.1、作用: 2.2、开启关闭: 2.3、Binlog还有一些附加选项参数 (1&#x…...

代码随想录算法训练营第三十七天 | 738.单调递增的数字、 968.监控二叉树
题目链接:738.单调递增的数字 文章讲解:代码随想录 738.单调递增的数字讲解 视频讲解:贪心算法,思路不难想,但代码不好写!LeetCode:738.单调自增的数字 思路和解法 题目: 当且仅当每个相邻位…...

【Django-ninja】django-ninja的hello world
django-ninja简介 Django Ninja是一个用于使用Django和Python 3.6类型提示构建API的Web框架。 主要特点: 易用性:旨在易于使用和直观。 高性能执行:由于Pydantic和异步支持,具有非常高的性能。 编码效率高:类型提…...

ArrayList集合初始化长度是多少,初始化的时候分配内存空间吗
ArrayList一旦初始化,在内存中就会分配空间吗 是的,当ArrayList在Java中初始化时,即使它没有添加任何元素,也会立即分配内存空间。具体来说,对于默认构造函数创建的ArrayList(即不指定初始容量)…...

C语言数组:从入门到进阶
前言: 在这篇博客中,我们将学习如何使用C语言数组的基本知识。数组是C语言中的一种重要数据结构,它允许我们存储一系列相同类型的数据。我们将讨论数组的定义、初始化、访问元素、遍历数组以及数组的应用场景。此外,我们还将通过…...

9.回文数
回文数 将整型转换为字符型反转前一半是否等于后一半将数字本身反转输入一个整数 x,如果 x是一个回文整数,返回 true;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 例如,121 是回文,而 123 不是。 将整型转换为字符型 反转…...

一分钟在SpringBoot项目中使用EMQ
先展示最终的结果: 生产者端: RestController RequiredArgsConstructor public class TestController {private final MqttProducer mqttProducer;GetMapping("/test")public String test() {User build User.builder().age(100).sex(1).address("世界潍坊渤…...

SOME/IP 协议介绍(七)传输 CAN 和 FlexRay 帧
SOME/IP 不应仅用于传输 CAN 或 FlexRay 帧。但是,消息 ID 空间需要在两种用例之间进行协调。 传输 CAN/FlexRay 应使用完整的 SOME/IP 标头。 AUTOSAR Socket-Adapter 使用消息 ID 和长度来构建所需的内部 PDU,但不会查看其他字段。因此,必…...

与数组相关经典面试题
𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - :来于“云”的“羽球人”。…...

数据结构与算法面试系列-02
1. 一个整数,它加上100后是一个完全平方数,加上168又是一个完全平方数,请问该数是多少? 程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上168后再开方,如果开方后的结果满足如下条件,即是结果。请看具体分析: 程序代码如下: package com.yoodb.uti…...

CMake 完整入门教程(五)
CMake 使用实例 13.1 例子一 一个经典的 C 程序,如何用 cmake 来进行构建程序呢? //main.c #include <stdio.h> int main() { printf("Hello World!/n"); return 0; } 编写一个 CMakeList.txt 文件 ( 可看做 cmake 的…...

pgsql中with子句和直接查询差别
1、代码的可读性和维护性: 当查询较为复杂时,WITH子句可以将复杂的查询分解成多个简单的步骤,每个步骤都可以有一个易于理解的名字。这样做提高了代码的可读性,也便于后期维护。 2、代码的重用性: 在WITH子句中定义…...

Day 31 | 贪心算法 理论基础 、455.分发饼干 、 376. 摆动序列 、 53. 最大子序和
理论基础 文章讲解 455.分发饼干 题目 文章讲解 视频讲解 思路:从小饼干开始喂小胃口 class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g);Arrays.sort(s);int start 0;int count 0;for (int i 0; i < s.length &&a…...

vue3使用is动态切换组件报错Vue received a Component which was made a reactive object.
vue3使用is动态切换组件,activeComponent用ref定义报错 Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef ins…...

React16源码: React中LegacyContext的源码实现
LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了,就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢?因为它…...

Gin 框架之jwt 介绍与基本使用
文章目录 一.JWT 介绍二.JWT认证与session认证的区别2.1 基于session认证流程图2.2 基于jwt认证流程图 三. JWT 的构成3.1 header : 头部3.2 payload : 负载3.2.1 标准中注册的声明 (建议但不强制使用)3.2.2 公共的声明3.2.3 私有的声明3.2.4 定义一个payload 3.3 signatrue : …...

从[redis:LinkedList]中学习链表
文章目录 adlistlistNodelistmacros[宏定义]listCreatelistInitNodelistEmptylistReleaselistAddNodeHeadlistLinkNodeHeadlistAddNodeTaillistLinkNodeTaillistInsertNodelistDelNodelistUlinkNodelistIndexredis3.2.100quicklistredis7.2.2quicklist redis的基本数据类型之一…...

Prometheus+grafana配置监控系统
使用docker compose安装 方便拓展, 配置信息都放在在 /docker/prometheus 目录下 1.目录结构如下 . ├── conf │ └── prometheus.yml ├── grafana_data ├── prometheus_data └── prometheus_grafana.yaml2.创建目录文件 mkdir /docker/prometheus &&am…...

Linux之安装配置CentOS 7
一、CentOS简介 CentOS(Community Enterprise Operating System,中文意思是社区企业操作系统)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,…...
神经网络与深度学习Pytorch版 Softmax回归 笔记
Softmax回归 目录 Softmax回归 1. 独热编码 2. Softmax回归的网络架构是一个单层的全连接神经网络。 3. Softmax回归模型概述及其在多分类问题中的应用 4. Softmax运算在多分类问题中的应用及其数学原理 5. 小批量样本分类的矢量计算表达式 6. 交叉熵损失函数 7. 模型预…...

git学习及简单maven打包
前提: 已经有远程仓库地址 和账号密码了 已经安装git了 1.本地新建文件夹A用作本地仓库 2.在A文件夹下右键打开GIT BASH HERE 3.创建用户和密码,方便追踪提交记录 git config --global user.email “caoqingqing0108” //创建邮箱 git config --global …...

如何用MapTalks IDE来发布网站?
简介 MapTalks IDE 全称 MapTalks集成设计环境(Integrated Design Environment),是由MapTalks技术团队开发的新一代web地图设计软件。 通过MapTalks IDE,您可以自由的创建二维和三维地图,在其中载入或创建地理数据&a…...

我用selenium开发了一个自动创建任务,解放重复性工作
我用selenium开发了一个自动创建任务,大大解放了我做重复性工作带来的疲惫感,收获了更多的乐趣。 我司有100多个服务,运维忙不过来的时候,就会让我们自己创建云负载,你首先需要在云服务上创建负载,再创建容…...

安卓11修改HDMI自适应分辨率
客户需要hdmi自适应屏幕分辨率,没发现有相关的指令,我发现设置中有个hdmi的Auto选项,于是就试试选中这个选项,试下了可以自适应,于是就找到相关代码,在开机完成后执行这个代码,基本满足需求&…...

Linux实验记录:使用Apache的虚拟主机功能
前言: 本文是一篇关于Linux系统初学者的实验记录。 参考书籍:《Linux就该这么学》 实验环境: VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 正文: 目录 前言: 正文&…...

分布式空间索引了解与扩展
目录 一、空间索引快速理解 (一)区域编码 (二)区域编码检索 (三)Geohash 编码 (四)RTree及其变体 二、业内方案选取 三、分布式空间索引架构 (一)PG数…...

Set和Map的应用场景
Set: 1.成员不能重复 2.只有键值,没有键名,有点类似数组 3.可以遍历,方法 add,delete,has Map: 1.本质上是键值对的集合,类似集合; 2.可以遍历,方法很多,可以干跟各种数据格式转换 Set和…...

小白级教程,10秒开服《幻兽帕鲁》
在帕鲁的世界,你可以选择与神奇的生物「帕鲁」一同享受悠闲的生活,也可以投身于与偷猎者进行生死搏斗的冒险。帕鲁可以进行战斗、繁殖、协助你做农活,也可以为你在工厂工作。你也可以将它们进行售卖,或肢解后食用。 前言 马上过年…...