实战:10 分钟掌握分布式 ID 之雪花算法
JAVA葵花宝典
共 396字,需浏览 1分钟
· 2020-08-22
实战:10 分钟掌握分布式 ID 之雪花算法
一个在生产每天经过1亿+数据量验证的id生成器
背景
1.为什么要使用雪花算法生成 ID
-- 保证 id 全局唯一
-- 保证 id 自增长
-- uuid 无序且过长
雪花算法 ID 组成
1: 1位标识部分:
--- 在 java 中由于 long 的最高位是符号位,正数是 0,负数是 1,一般生成的 ID 为正数,所以为 0;
2: 41 位时间戳部分:
--- 这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),这样可以使产生的 ID 从更小值开始;41 位的时间戳可以使用 69 年,(1L<< 41) / (1000L _ 60 _ 60 _ 24 _ 365) = 69 年;
3: 10 位workid:
Twitter 实现中使用前 5 位作为数据中心标识,后 5 位作为机器标识,可以部署 1024 个节点。我这里的实现根据服务名生产的,意思就是说每个服务只要不超过 1024 个节点就不会有问题,实际生产中我也没有见过某个服务有 1024 个节点的。
4: 12 位序列号部分:
--- 支持同一毫秒内同一个节点可以生成 4096 个 ID。意思就是说某个服务 1ms 能生成 4096 个 id,如果你单表的 TPS 超过 4096\*60s,那可能就会出问题了,实际生产这么大的 TPS 我是没有见过的。
Zookeeper生成workid
雪花算法生成ID网络上方法很多,很多重复的东西我就不赘述了,这里简明扼要的说一下ZK生成workid。
这是生成的ID的截图。父节点是workid,只要有引用id生成器jar的服务都会在workid下面生成一个文件夹,以服务名命名,有多少个节点该节点下就会有多个文件,该节点下的id一定是不会重复的。下面我贴一下java版生成workid的核心代码
private void buildWorkId(final String appPath) {
// 检测client是否已经连接上
if (null == client) {
throw new RuntimeException("本节点注册到ZK异常。");
}
// lockPath,用于加锁,注意要与nodePath区分开
final String lockPath = this.ROOT_NAME + "/" + this.appName;
// nodePath 用于存放集群各节点初始路径
final String nodePath = this.ROOT_NAME + "/" + this.appName + this.NODE_NAME;
// InterProcessMutex 分布式锁(加锁过程中lockPath会自动创建)
InterProcessLock interProcessLock = new InterProcessMutex(client, lockPath);
try {
if (!interProcessLock.acquire(5000, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("ZK分布式锁 加锁超时,超时时间: " + 5000);
}
// nodePath 第一次需初始化,永久保存, 或者节点路径为临时节点,则设置为永久节点
if (null == client.checkExists().forPath(nodePath)) {
client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(nodePath);
}
// 获取nodePath下已经创建的子节点
List
childPath = client.getChildren().forPath(nodePath); Set
nodeIdSet = new LinkedHashSet<>(); if (!CollectionUtils.isEmpty(childPath)) {
for (String path : childPath) {
try {
nodeIdSet.add(Integer.valueOf(path));
} catch (Exception e) {
log.warn("路径由不合法操作创建,注意[" + nodePath + "]仅用于构建workId");
}
}
}
// 遍历所有id,构建workId,主要是判断可用id是否已经被集群中其他节点占用
for (Integer order : OrderIdSet) {
if (!nodeIdSet.contains(order)) {
final String currentNodePath = nodePath + "/" + order;
String nodeDate = String.format("[ip:%s,hostname:%s,pid:%s]",
InetAddress.getLocalHost().getHostAddress(),
InetAddress.getLocalHost().getHostName(),
ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
try {
client.create().withMode(CreateMode.EPHEMERAL).forPath(currentNodePath, nodeDate.getBytes("UTF-8"));
} catch (Exception e) {
log.debug("节点[{}]无法创建,可能是已存在", currentNodePath);
continue;
}
long pathCreateTime = client.checkExists().forPath(currentNodePath).getCtime();
// 以下逻辑主要用于检测断开重连情况
TreeCache treeCache = new TreeCache(client, currentNodePath);
// 添加监听器
treeCache.getListenable().addListener(new TreeCacheListener() {
public void childEvent(CuratorFramework curatorFramework,
TreeCacheEvent treeCacheEvent) throws Exception {
long pathTime;
try {
pathTime = curatorFramework.checkExists().forPath(currentNodePath).getCtime();
} catch (Exception e) {
pathTime = 0;
}
// 如果pathTime != pathCreateTime, 那么只能一种情况:
// 当前应用与zk失去联系,且/nodePath/{currentNodePath}不存在或者被其它应用占据了(表象为pathCreateTime变化)
// 无论哪种情况,当前应用都要重新注册节点
if (pathCreateTime != pathTime) {
log.info("从ZK断开,再次注册...");
// 关闭之前旧的treeCache
try {
treeCache.close();
} catch (Exception e) {
log.warn("treeCache关闭失败");
}
// 再次注册
finally {
buildWorkId(appPath);
}
}
}
});
treeCache.start();
workerId = order;
log.info("基于ZK成功构建 workId:{}", workerId);
return;
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("获取分布式WorkId异常", e);
} finally {
// 构建成功后释放锁
if (interProcessLock != null) {
try {
interProcessLock.release();
} catch (Exception e) {
log.warn("释放锁失败");
}
}
}
}
核心代码我已经贴出来了,对雪花算法有一定了解的同学,使用的时候在需要id生成器的地方引用就好了,还有部分非核心代码,这个ID生成器已经在生产验证了每天1亿+的数据量,如果你真的需要可以联系我,把代码都给你。zookeeper其实是一个很实用的工具,还有分布式锁的实现及应用,下篇文章会给大家带来用zk生成分布式锁。
END
喜欢请扫码关注
评论
轻松掌握开源项目的二次开发技巧
大厂技术 高级前端 Node进阶点击上方 程序员成长指北,关注公众号回复1,加入高级Node交流群本文作者:@方长_beezen 原文链接:https://juejin.cn/post/7358647992608489535前言随着软件行业的迅速
程序员成长指北
0
【每周一课#06】MidJourney应用实战
#AI绘画# #MJ# #文生图#时间:4月24日周三 21:00课程大纲:1、关于AIGC:概念、发展历程、就业前景2、MJ基础认识:如何使用、底层逻辑、MJ与SD优缺点比较3、MJ基础功能介绍:任务指令、后缀参数、图生图、图生文、垫图、局部修改等4、MJ应用场景与变现方向
Python涨薪研究所
0
轻松掌握开源项目的二次开发技巧
点击上方 前端Q,关注公众号回复加群,加入前端Q技术交流群本文作者:@方长_beezen 原文链接:https://juejin.cn/post/7358647992608489535前言随着软件行业的迅速发展,开源项目的重要性已经成为不言而喻的事实。它能够为开发人员节省大量时间和成本,避
前端Q
0
【每周一课#06】MidJourney 应用实战
#AI绘画# #MJ# #文生图#时间:4月24日周三 21:00课程大纲:1、关于AIGC:概念、发展历程、就业前景2、MJ基础认识:如何使用、底层逻辑、MJ与SD优缺点比较3、MJ基础功能介绍:任务指令、后缀参数、图生图、图生文、垫图、局部修改等4、MJ应用场景与变现方向
Python涨薪研究所
0
面试官:限流的常见算法有哪些?
限流的实现算法有很多,但常见的限流算法有三种:计数器算法、漏桶算法和令牌桶算法。1.计数器算法计数器算法是在一定的时间间隔里,记录请求次数,当请求次数超过该时间限制时,就把计数器清零,然后重新计算。当请求次数超过间隔内的最大次数时,拒绝访问。计数器算法的实现比较简单,但存在“突刺现象”。突刺现象是指
Stephen
0
Java项目实战——打造一款股票区间交易盯盘系统
点击上方“Java进阶学习交流”,进行关注后台回复“Java”即可获赠Java学习资料今日鸡汤身无彩凤双飞翼,心有灵犀一点通。一、简介大家好,我是Snowball。今天给大家分享的内容是基于Java编程,实现股票交易相关功能开发,如果读者对股票或金融衍生物交易不太了解,又比较感兴趣的话可自行查询相关
Java进阶学习交流
0
985 本硕,秋招上岸阿里算法岗!
↓推荐关注↓节前,我们星球举办了技术&面试交流会,邀请了一些互联网大厂好友以及今年参加社招和校招面试的同学。会上探讨了一系列热门话题,包括大模型发展趋势、算法落地实践、面经总结,以及如何做好面试准备和应对常见考点。基于经验交流与实战经验,我们总结如下:《机器学习算法面试宝典》1.0 发布!今
Python学习与数据挖掘
0
神作《凤凰架构:构建可靠的大型分布式系统》PDF来了
今天给大家带来了一本大作:周志明老师的 《凤凰架构:构建可靠的大型分布式系统》PDF版来了,文末直接获取周志明是谁?这可是真大佬,或许有些朋友没有听过,但是你们一定听过:《深入理解 Java 虚拟机》这本神书,没错就是这本书的作者,出过多本书,豆瓣评分如下,全是高分,尤其是《深入理解Java虚拟机》
路人甲Java
0