[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 摘 要:本文从目前信息 ...
随机推荐
- “System.InvalidOperationException”类型的未经处理的异常在 ESRI.ArcGIS.AxControls.dll 中发生
问题描述: 新手们进行ArcGIS ArcObject开发时经常会遇到各种十分古怪的问题,比如下面的这个问题: “System.InvalidOperationException”类型的未经处理的异常 ...
- 5-05. QQ帐户的申请与登陆(25)(map运用)(ZJU_PAT)
题目链接:http://pat.zju.edu.cn/contests/ds/5-05 实现QQ新帐户申请和老帐户登陆的简化版功能. 最大挑战是:据说如今的QQ号码已经有10位数了. 输入格式说明: ...
- Velocity中的ComparisonDateTool、MathTool、NumberT...
Velocity中的ComparisonDateTool.MathTool.NumberTool.SortTool.EscapeTool工具 可以从Apache官方网站自带的例子中查找,位置\velo ...
- 用Spark查询HBase中的表数据
java代码如下: package db.query; import org.apache.commons.logging.Log; import org.apache.commons.logging ...
- Objective-C:协议protocol
六.协议(protocol) 关键字:@optional.@required (1)是一个类共享的一个方法列表 (2)它声明了一系列的方法而不进行实现 (3)遵从某个协议,就是需要实现协议中的方法 ( ...
- 混沌数学之二维logistic模型
上一节讲了logistic混沌模型,这一节对其扩充一下讲二维 Logistic映射.它起着从一维到高维的衔接作用,对二维映射中混沌现象的研究有助于认识和预测更复杂的高维动力系统的性态.通过构造一次藕合 ...
- 第一章 EL表达式常见用法
el最常用的几种使用场景: 从配置文件中读取属性 缺失值情况下,配置默认值 el内部字符串使用String的方法 三目运算符 正则表达式 注入系统属性(system properties) 调用系统原 ...
- HDU4183 起点到终点再到起点 除起点每点仅经过一次 网络流
题意: T个测试数据 n个圆 下面 fre x y r 表示圆的频率 坐标和半径 要求: 从频率为400(最小的) 圆 走到频率为789(最大)的圆,再走回来,除起点每个点只能经过一次 问这样的路径是 ...
- MD5 SHA1 哈希 签名 碰撞 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- AngularJs 阻止事件运行,防止冒泡穿透事件
ng-click 低啊用方法后 添加语句$event.stopPropagation(); <button type="button" ng-click="doSo ...