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的更多相关文章

  1. 分布式环境下Unique ID生成方法

    ID即标示符,在某个搜索域内能唯一标示其中某个对象.在关系型数据库中每个表都需要定义一个主键来唯一标示一条记录.为了方便一般都会使用一个auto_increment属性的整形数做为ID.因为数据库本身 ...

  2. 分布式环境下的id生成方法

    分布式环境下的id生成方法   前几天研究数据库分表分库的问题,其中有一个关键的地方就是生成唯一键的问题,假如数据表有1亿条数据,而且还在不断的增加,这里我们就需要考虑到分表分库,假设我们采用Hash ...

  3. 集群/分布式环境下5种session处理策略

    转载自:http://blog.csdn.net/u010028869/article/details/50773174?ref=myread 前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访 ...

  4. 【架构师之路】集群/分布式环境下5种session处理策略

    [架构师之路]集群/分布式环境下5种session处理策略   转自:http://www.cnblogs.com/jhli/p/6557929.html 在搭建完集群环境后,不得不考虑的一个问题就是 ...

  5. 【转】分布式环境下5种session处理策略(大型网站技术架构:核心原理与案例分析 里面的方案)

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...

  6. 【转】集群/分布式环境下5种session处理策略

    转载至:http://blog.csdn.net/u010028869/article/details/50773174 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处 ...

  7. 集群/分布式环境下,Session处理策略

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象.比如集中中存在A.B两台服务器,用户在第一次访问网站是,Ngin ...

  8. Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享

    首发地址:https://www.guitu18.com/post/2019/07/28/44.html 本篇是Shiro系列第二篇,使用Shiro基于Redis实现分布式环境下的Session共享. ...

  9. .NET环境下基于RBAC的访问控制

    .NET环境下基于RBAC的访问控制 Access Control of Application Based on RBAC model in .NET Environment 摘 要:本文从目前信息 ...

随机推荐

  1. 无法完成你的itunes store 请求发生未知错误50

    装上itunes登陆itunes store时遂发现"无法完成您的itunes store的请求,发生未知错误(-50)"跃入眼帘,卸载重装数次还是不见效果,难道是WIN7和itu ...

  2. 6. python 字符串格式化表达式

    6. python 字符串格式化表达式 字符串格式化允许在一个单个的步骤中对一个字符串执行多个特定类型的替换 特别是给用户提示的时候,格式化非常方便 实现方法: 1.格式化表达式,类似于c语言的pri ...

  3. 什么是软件project?

    Normal 0 7.8 pt 0 2 false false false MicrosoftInternetExplorer4 /* Style Definitions */ table.MsoNo ...

  4. C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性

    1.赋值兼容规则 (1)派生类对象能够给基类对象赋值,这样的情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:可是不同意将一个基类的对象赋值给一个派生类. (2)能够将派生类对象的地址赋给基类 ...

  5. cgroup子系统2_devices子系统

    devices子系统用于控制cgroup中全部进程能够訪问哪些设备,三个控制文件:devices.allow,devices.deny,devices.list. devices.allow用于指定c ...

  6. Objective-C:NSNumber类的常见用法

    NSNumber基本数据类型包装类: // //  main.m //  04-NSNumber // //  Created by ma c on 15/8/17. //  Copyright (c ...

  7. Objective-C:NSMutableString类的常见操作

    NSMutableString可变字符串的主要的操作是创建.增加.删除.插入.替换 代码操作如下: // // main.m // 03-NSMutableString // // Created b ...

  8. Gh0st整理资料1

    题首 Gh0st是一款开源的远程控制软件.界面友好,性能高效.网上流传很多版本,比如红狼,饭客,败笔,大灰狼版本以及多如牛毛的个人修改的如外星人,Drat等个人修改版本.但内核都是基于Gh0st3.6 ...

  9. PCL源码剖析之MarchingCubes算法

    原文:http://blog.csdn.net/lming_08/article/details/19432877 MarchingCubes算法简介 MarchingCubes(移动立方体)算法是目 ...

  10. fatal error C1853: '<filename>' is not a precompiled header file

    当编译c和c++混合的项目时,会出现如下类似的错误 fatal error C1853: '<filename>' is not a precompiled header file 解决方 ...