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. MEF(Managed Extensibility Framework)有选择性地使用扩展组件

    在"MEF(Managed Extensibility Framework)使用全部扩展组件"中,客户端应用程序调用了所有的扩展组件,而且如果有新的扩展组件加入,必须先关闭程序,再 ...

  2. 无损转换Image为Icon z

    如题,市面上常见的方法是: var handle = bmp.GetHicon(); //得到图标句柄 return Icon.FromHandle(handle); //通过句柄得到图标 此法的问题 ...

  3. C#,数据类型扩展 z

    MACD的公式 DIFF : EMA(CLOSE,SHORT) - EMA(CLOSE,LONG);DEA  : EMA(DIFF,M); MACD : 2*(DIFF-DEA), COLORSTIC ...

  4. java执行ping命令

    public static void get() throws IOException{ String address="10.132.118.110"; Process proc ...

  5. recyclerView 列表类控件卡顿优化

    1.使用ConstraintLayout减少布局层级. 2.可以的话,设置RecyclerView布局等高,然后设置recyclerView.setHasFixedSize(true)这样可以避免每次 ...

  6. mongo 3.0 备份和还原数据库 ,及too many positional arguments错误

    在mongo 3.0的操作 备份示例 ./mongodump -h localhost -d liongo -o ./ 错误方式: ./mongorestore -h 127.0.0.1 -d lio ...

  7. Android之批量加载图片OOM问题解决方案

    一.OOM问题出现的场景和原因 一个好的app总少不了精美的图片,所以Android开发中图片的加载总是避免不了的,而在加载图片过程中,如果处理不当则会出现OOM的问题.那么如何彻底解决这个问题呢?本 ...

  8. WdColor 枚举 (Word)

    指定要应用的 位颜色. 名称 值 说明 wdColorAqua 水绿色. wdColorAutomatic - 自动配色.默认值:通常为黑色. wdColorBlack 黑色. wdColorBlue ...

  9. STM8S AD转换

    //不说那么多了,直接上程序 void ADC1_DeInit(void) { ADC1->CSR = ADC1_CSR_RESET_VALUE; ADC1->CR1 = ADC1_CR1 ...

  10. U盘去保护方法

    一.基本信息 U盘大小是16G的,估计用了2G的空间存储,没有任何开关设置,格式化或写入时提示被写保护: U盘放到任何一台电脑上都是只能读不能写,说明与电脑无关,用了各种U盘修复程序都无效: 二.一般 ...