[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 摘 要:本文从目前信息 ...
随机推荐
- linux列出一个目录及其子目录下面的某种类型的文件
linux列出一个目录及其子目录下面的某种类型的文件 作者:smarteng ⁄ 时间:2009年07月09日 ⁄ 分类: Linux命令 ⁄ 评论:0 怎么样把,一个目录及其所有的子目录下面的某种类 ...
- iPhone应用提交流程:如何将App程序发布到App Store-转
对于刚加入iOS应用开发行列的开发者来说,终于经过艰苦的Coding后完成了第一个应用后最重要的历史时刻就是将应用程序提交到iTunes App Store.Xcode 4.2开发工具已经把App提交 ...
- Vi 编辑
1.vi的基本概念 基本上vi可以分为三种状态,分别是命令模式(command mode).插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下: 1) ...
- ubuntu下如何查看自己的外网IP
1.1 安装使用curl命令实现 sudo apt-get install curl1.2 输入命令 curl ifconfig.me
- Android图片加载框架最全解析(八),带你全面了解Glide 4的用法
本篇将是我们这个Glide系列的最后一篇文章. 其实在写这个系列第一篇文章的时候,Glide就推出4.0.0的RC版了.那个时候因为我一直研究的都是Glide 3.7.0版本,再加上RC版本还不太稳定 ...
- 使用cxf开发webservice接口
项目中经常用到开发webservice接口,及调用webService接口.这里讲解如何使用cxf开发webService接口. 一.webservice介绍及理解 webservice是一种跨平台, ...
- 【BZOJ】【3612】【HEOI 2014】平衡
DP 唉我还是too naive 这是个整数划分题…… 我想的DP方式是f[i][j][k]表示前 i 个数拼出 j 用了 k 个数的方案数…… 转移当然是比较直观…… 但是只能得30分QAQ 正确的 ...
- 谁能用通俗的语言解释一下什么是 RPC 框架
转载自知乎:https://www.zhihu.com/question/25536695 知乎上很多问题的答案还是很好的,R大就经常在上面回答问题~ 谁能用通俗的语言解释一下什么是 RPC 框架? ...
- 什么是C++虚函数、虚函数的作用和使用方法
我们知道,在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是“重复定义”.但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数.例如在例 ...
- Informatica 常用组件Source Qualifier之九 创建SQ转换
可以配置 Designer 在您将源拖到映射中时默认创建源限定符转换,您也可以手动创建源限定符转换. 默认创建源限定符转换 可以配置 Designer 在您将源拖到映射中时自动创建一个源限定符转换. ...