[Done]SnowFlake 分布式环境下基于ZK构WorkId
Twitter 的 Snowflake 大家应该都熟悉的,先上个图:

时间戳 序列号一般不会去改造,主要是工作机器id,大家会进行相关改造,我厂对工作机器进行了如下改造(估计大家都差不多吧,囧~~~):

机房号,不同的机房搞个初始化配置即可(当然若机房数量多也可考虑分布式动态获取),
主要是机器编号,在如何动态获取,以下workId获取方式供参考:
public class WorkIdBuilder {
private final static Logger logger = LoggerFactory.getLogger(WorkIdBuilder.class);
// 省略字段和get set 方法public void close() {
if (null != client && null == client.getState()) {
client.close();
}
client = null;
}
public void init() {
if (StringUtils.isBlank(this.appName)) {
logger.error("应用名称不能为空!");
throw new RuntimeException("应用名称不能为空!");
}
if (client != null) {
close();
}
client = CuratorFrameworkFactory.builder()
.connectString(address)
.connectionTimeoutMs(connectionTimeout)
.sessionTimeoutMs(sessionTimeout)
.canBeReadOnly(false)
.retryPolicy(new ExponentialBackoffRetry(baseSleepTimeOut, Integer.MAX_VALUE))
.build();
client.start();
buildWorkId(this.appName);
}
// 序号集,当前最大支持 256 个节点,每个节点去占用编号,通过InterProcessLock来控制分布式环境下的获取
private static Set<Integer> OrderIdSet ;
static {
OrderIdSet = new HashSet<>();
for(int i = 0; i < MAX_ORDER; i++){
OrderIdSet.add(i);
}
}
/***
* 获取workId
* @param appPath 应用名称
*/
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(lockTimeOut, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("ZK分布式锁 加锁超时,超时时间: " + lockTimeOut);
}
// nodePath 第一次需初始化,永久保存, 或者节点路径为临时节点,则设置为永久节点
if (null == client.checkExists().forPath(nodePath)) {
client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(nodePath);
}
// 获取nodePath下已经创建的子节点
List<String> childPath = client.getChildren().forPath(nodePath);
Set<Integer> nodeIdSet = new LinkedHashSet<>();
if(CollectionUtils.isNotEmpty(childPath)){
for(String path : childPath){
try {
nodeIdSet.add(Integer.valueOf(path));
}
catch (Exception e){
logger.warn("路径由不合法操作创建,注意["+nodePath+"]仅用于构建workId");
// ignore
}
}
}
// 遍历所有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]);
// 事务提交, 应用断开zk连接时候,删除该节点数据,此处CreateMode = EPHEMERAL (非常重要)
// 当本节点zk断开时,其他client.getChildren().forPath(nodePath)进行操作时,子节点逻辑已释放,orderId可复用
client.inTransaction()
.create().withMode(CreateMode.EPHEMERAL).forPath(currentNodePath)
.and().setData().forPath(currentNodePath,nodeDate.getBytes("UTF-8"))
.and().commit();
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) {
logger.info("从ZK断开,再次注册...") ;
// 关闭之前旧的treeCache
try{
treeCache.close();
}
catch (Exception e){
logger.warn("treeCache关闭失败");
}
// 再次注册
finally {
buildWorkId(appPath);
}
}
}
});
treeCache.start();
this.workerId = order;
logger.info("基于ZK成功构建 workId:{}",this.workerId);
return;
}
}
throw new RuntimeException("获取WorkId失败,共["+this.MAX_ORDER+"]个可用WorkId, 已全部用完。 ");
} catch (Exception e) {
logger.error("获取分布式WorkId异常",e);
} finally {
// 构建成功后释放锁
if(interProcessLock != null) {
try {
interProcessLock.release();
} catch (Exception e) {
logger.warn("释放锁失败");
}
}
}
}
}
供参考。
以上。
[Done]SnowFlake 分布式环境下基于ZK构WorkId的更多相关文章
- 分布式环境下Unique ID生成方法
ID即标示符,在某个搜索域内能唯一标示其中某个对象.在关系型数据库中每个表都需要定义一个主键来唯一标示一条记录.为了方便一般都会使用一个auto_increment属性的整形数做为ID.因为数据库本身 ...
- 分布式环境下的id生成方法
分布式环境下的id生成方法 前几天研究数据库分表分库的问题,其中有一个关键的地方就是生成唯一键的问题,假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash ...
- 集群/分布式环境下5种session处理策略
转载自:http://blog.csdn.net/u010028869/article/details/50773174?ref=myread 前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访 ...
- 【架构师之路】集群/分布式环境下5种session处理策略
[架构师之路]集群/分布式环境下5种session处理策略 转自:http://www.cnblogs.com/jhli/p/6557929.html 在搭建完集群环境后,不得不考虑的一个问题就是 ...
- 【转】分布式环境下5种session处理策略(大型网站技术架构:核心原理与案例分析 里面的方案)
前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...
- 【转】集群/分布式环境下5种session处理策略
转载至:http://blog.csdn.net/u010028869/article/details/50773174 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处 ...
- 集群/分布式环境下,Session处理策略
前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象.比如集中中存在A.B两台服务器,用户在第一次访问网站是,Ngin ...
- Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享
首发地址:https://www.guitu18.com/post/2019/07/28/44.html 本篇是Shiro系列第二篇,使用Shiro基于Redis实现分布式环境下的Session共享. ...
- .NET环境下基于RBAC的访问控制
.NET环境下基于RBAC的访问控制 Access Control of Application Based on RBAC model in .NET Environment 摘 要:本文从目前信息 ...
随机推荐
- 模拟登陆CSDN——就是这么简单
工具介绍 本篇文章主要是解说怎样模拟登陆CSDN.使用的工具是HttpClient+Jsoup 当中HttpClient主要是负责发送请求,而Jsoup主要是解析HTML 你可能对HttpClient ...
- 如何在IE11中开启WebGL暨微软和WebGL的恩怨情仇录
正如我们上周报道的,国外开发者Francois Remy在泄露版Windows Blue附带的Internet Explorer 11中发现,WebGL接口已经封装完成,但功能上还未能开放支持.在这之 ...
- ImageView 设置图片来自:http://blog.csdn.net/lincyang/article/details/6562163
android doc中是这样描述的: public void setImageResource (int resId) 这是其中的一个方法,参数resld是这样: ImageView.setImag ...
- AutoMapper: Mapper.Initialize() 只能调用一次,Why?
最开始的代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...
- Derby设置密码教程
方法一: 配置derby.propertites文件: 文件内容: derby.connection.requireAuthentication=truederby.authentication ...
- Java中间件:淘宝网系统高性能利器
[TechTarget中国原创]淘宝网是亚太最大的网络零售商圈,其知名度毋庸置疑,吸引着越来越多的消费者从街头移步这里,成为其忠实粉丝.如此多的用户和交易量,也意味着海量的信息处理,其背后的IT架构的 ...
- org-mode入门教程
org-mode 入门教程By Z.H. Fu 切问录 www.fuzihao.org org-mode 入门教程 org-mode是Emacs提供的一个强大的编辑模式,可以用于做会议笔记以及制作各种 ...
- 如何设置nginx日志格式来查看负载分担结果
转载:http://www.cnblogs.com/LoveJulin/p/5082363.html nginx配置好负载分担后,测试的时候,如何查看负载分担情况:通过设置nginx日志显示: ng ...
- [leetcode]Insertion Sort List @ Python
原题地址:http://oj.leetcode.com/problems/insertion-sort-list/ 题意:对链表进行插入排序. 解题思路:首先来对插入排序有一个直观的认识,来自维基百科 ...
- xUtils如何通过注解对FindViewById进行封装
之前讲到了介绍了一下xUtils的基本使用方法,今天我们就来详细介绍一下关于xUtils中的ViewUtils模块. 在ViewUtils模块中我们首先看到的是它采用了一种注解的方式进行声明,那么我们 ...