分布式ID生成-雪花算法实现无状态

雪花算法这里不再赘述,其缺点是有状态(多副本隔离时,依赖手动配置workIddatacenterId),代码如下:

/*** 雪花算法ID生成器*/
public class SnowflakeIdWorker {/*** 开始时间截 (2017-01-01)*/private static final long twepoch = 1483200000000L;/*** 机器id所占的位数*/private static final long workerIdBits = 5L;/*** 数据标识id所占的位数*/private static final long datacenterIdBits = 5L;/*** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)*/private static final long maxWorkerId = ~(-1L << workerIdBits);/*** 支持的最大数据标识id,结果是31*/private static final long maxDatacenterId = ~(-1L << datacenterIdBits);/*** 序列在id中占的位数*/private final long sequenceBits = 12L;/*** 机器ID向左移12位*/private final long workerIdShift = sequenceBits;/*** 数据标识id向左移17位(12+5)*/private final long datacenterIdShift = sequenceBits + workerIdBits;/*** 时间截向左移22位(5+5+12)*/private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/*** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)*/private final long sequenceMask = ~(-1L << sequenceBits);/*** 工作机器ID(0~31)*/private long workerId;/*** 数据中心ID(0~31)*/private long datacenterId;/*** 毫秒内序列(0~4095)*/private long sequence = 0L;/*** 上次生成ID的时间截*/private long lastTimestamp = -1L;/*** 构造函数** @param workerId     工作ID (0~31)* @param datacenterId 数据中心ID (0~31)*/public SnowflakeIdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获得下一个ID (该方法是线程安全的)** @return SnowflakeId*/public synchronized String getId() {long id = nextId();return id+"";}/*** 获得下一个ID (该方法是线程安全的)** @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift)| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳** @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间** @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}/*** 测试*/public static void main(String[] args) throws ParseException {SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new SynchronousQueue<>());for (int i = 0; i < 10000; i++) {threadPoolExecutor.execute(() -> {long start = System.currentTimeMillis();long id = idWorker.nextId();System.out.println("id:"+id);});}threadPoolExecutor.shutdown();}
}

我们可以利用redis分布式锁,在服务启动时获取,实现无状态:
在这里插入图片描述

1、利用redis分布式锁获取,解决多副本冲突。


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.net.InetAddress;
import java.net.UnknownHostException;@Configuration
public class SnowBeanConfig {private static final Logger logger = LoggerFactory.getLogger(SnowBeanConfig.class);private final static String OK = "OK";private final static int DATA_SIZE = 31;@Value("${cus.snowflake.expire:40}")private int snowflakeExpireTime;@Autowiredprivate IRedis redis;@Beanpublic SnowflakeIdWorker getIdWorker() {try {/** 防止水平扩展的其他机器保活失败(如0-0,但是仍然在用),故这里等35s,让其在redis中过期*/logger.info("Get snow work by redis and wait for {}s...",snowflakeExpireTime);Thread.sleep(snowflakeExpireTime * 1000);getSnowWorkIdByRedis();Integer dataCenterId = SnowWorkIdLocalCache.getDataCenterId();Integer workerId = SnowWorkIdLocalCache.getWorkId();logger.info("Host: {} SnowFlake success get dataCenterId: {}, workerId: {}", getHostName(), dataCenterId, workerId);if (dataCenterId == null || workerId == null) {throw new RuntimeException("get SnowflakeIdWorker error");}return new SnowflakeIdWorker(workerId, dataCenterId);} catch (Exception e) {throw new RuntimeException("get SnowflakeIdWorker error", e);}}private void getSnowWorkIdByRedis() throws UnknownHostException {for (int dataCenterId = 0; dataCenterId <= DATA_SIZE; dataCenterId++) {for (int workId = 0; workId <= DATA_SIZE; workId++) {String key = Const.Cache.SERVER_NAME + ":snow:id:" + dataCenterId + "_" + workId;String value = getHostName();String result = redis.setNx(key, value, snowflakeExpireTime);logger.info("redis setNx key:[{}],value:[{}],seconds:[{}]", key, value, snowflakeExpireTime);if (OK.equals(result)) {SnowWorkIdLocalCache.setCache(workId, dataCenterId);return;}}}throw new RuntimeException("get SnowflakeIdWorker error");}public static String getHostName() throws UnknownHostException {InetAddress addr = InetAddress.getLocalHost();String hostName = addr.getHostName();if (hostName == null) {hostName = StringUtils.EMPTY;}return hostName;}}

2、获取后缓存到本地:


import java.util.HashMap;
import java.util.Map;public class SnowWorkIdLocalCache {private SnowWorkIdLocalCache() {}private static final String DATA_CENTER_ID = "data_center_id";private static final String WORK_ID = "work_id";private static Map<String, Integer> cacheMap = new HashMap<>(2);static void setCache(int workId, int dataCenterId) {cacheMap.put(WORK_ID, workId);cacheMap.put(DATA_CENTER_ID, dataCenterId);}public static Integer getWorkId() {return cacheMap.get(WORK_ID);}public static Integer getDataCenterId() {return cacheMap.get(DATA_CENTER_ID);}}

3、定期保活


import com.test.common.Const;
import com.test.config.jedis.core.IRedis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@EnableScheduling
@Component
public class SnowWorkHealth {private static final Logger logger = LoggerFactory.getLogger(SnowWorkHealth.class);private static final int MAX_RETRY_COUNT = 2;private int failCount = 0;@Value("${cus.snowflake.expire:40}")private int snowflakeExpireTime;@Autowiredprivate IRedis redis;@Scheduled(cron = "0/10 * * * * *")public void keepAlive() {Integer dataCenterId = SnowWorkIdLocalCache.getDataCenterId();Integer workId = SnowWorkIdLocalCache.getWorkId();if (dataCenterId == null || workId == null) {logger.error(".....keep error and system exit!!!");System.exit(0);}String key = Const.Cache.SERVER_NAME + ":snow:id:" + dataCenterId + "_" + workId;if (redis.expire(key, snowflakeExpireTime)) {String hostName = redis.get(key);logger.info("keep alive of snow work host:{}, dataCenterId: {},workId: {}", hostName, dataCenterId, workId);} else {logger.error("keep snow work id active exception of redis");failCount++;}if (failCount >= MAX_RETRY_COUNT) {logger.error(".....keep error and system exit!!!");System.exit(0);}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891942.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(六)vForm 动态表单(数据量大,下拉选卡顿问题)

系列文章目录 (一)vForm 动态表单设计器之使用 (二)vForm 动态表单设计器之下拉、选择 (三)vForm 动态表单解决下拉框无数据显示id问题 (四)vForm 动态表单自定义组件、属性 (五)vForm 动态表单文件上传、下载 文章目录 目录 前言 一、组件改造 1.添加分页所需参…

Edge SCDN高效防护与智能加速

当今数字化时代&#xff0c;网络安全和内容分发效率已成为企业业务发展的关键因素。酷盾安全推出了Edge SCDN解决方案&#xff0c;为企业提供全方位的安全防护和高效的内容分发服务。 一、卓越的安全防护能力 1.DDoS攻击的精准防御&#xff1a;Edge SCDN具备强大的DDoS攻击检测…

【银河麒麟高级服务器操作系统】服务器异常重启故障分析及处理建议

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://document.kylinos.cn 服务器环境以及配置 系统环境 物理机/虚拟机/云…

Go语言中的 os.Stat() 与 os.Lstat() 实际应用中,你该如何选择?

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…

少儿编程:开启多元成长,引领未来之路

今天&#xff0c;想跟大家深入聊聊一个当下热度颇高&#xff0c;却又让不少家长心存疑惑的话题 —— 少儿为什么要学习编程。 咱们先把目光投向国家层面的教育政策风向。近几年&#xff0c;国家对科技创新人才的培养予以了前所未有的重视&#xff0c;一系列重磅政策相继出台&a…

在vscode上

第一步 安装插件 &#xff08;1&#xff09;从菜单处打开vscode&#xff0c;之后点击左侧“拓展”&#xff0c;在搜索栏输入“platform”&#xff0c;安装这个插件。 注&#xff1a;安装过程可能会慢一点&#xff0c;可以尝试连接自己的热点 &#xff08;2&#xff09;安装完…

产品心、用户脑、押重注......解读vivo穿越周期之道

出品 | 何玺 排版 | 叶媛 国内科技企业中&#xff0c;vivo绝对算个“异类”。给人以平和谦逊、稳健踏实的印象&#xff0c;却极具实力&#xff01; 回望vivo发展历程&#xff0c;这家拥有近30年历史的超大型全球化产业科技生态型公司&#xff0c;从功能机到智能机一路走来&am…

jenkins入门4 --window执行execute shell

1、启动关闭jenkins 在Windows环境下&#xff0c;如果你需要关闭Jenkins服务&#xff0c;可以通过以下几种方式&#xff1a; 1、使用Windows服务管理器&#xff1a; 打开“运行”对话框&#xff08;Win R&#xff09;&#xff0c;输入services.msc&#xff0c;然后回车。 在服…

矩阵碰一碰发视频源码搭建全解析,支持OEM

在数字化营销与互动体验需求日益增长的当下&#xff0c;矩阵碰一碰发视频功能以其独特的交互性和高效的信息传播能力&#xff0c;正逐渐成为吸引用户、提升品牌影响力的有力工具。本文将深入探讨如何搭建矩阵碰一碰发视频的源码&#xff0c;帮助开发者实现这一创新功能。 一、技…

软件确认测试和验收测试有什么区别?

在当今快速发展的软件行业中&#xff0c;软件确认测试与验收测试是软件产品生产周期中的重要步骤&#xff0c;但很多人容易混淆&#xff0c;那么这两者之间究竟有什么区别呢? 软件确认测试是一个旨在确保软件产品符合用户需求规格的过程。它对软件的功能、性能和可用性进行深…

Oracle Dataguard 需要配置的参数详解

Oracle Dataguard 需要配置的参数详解 目录 Oracle Dataguard 需要配置的参数详解一、数据库名&#xff1a;DB_NAME二、数据库唯一名&#xff1a;DB_UNIQUE_NAME三、LOG_ARCHIVE_CONFIG四、LOG_ARCHIVE_DEST_1五、LOG_ARCHIVE_DEST_2六、LOG_ARCHIVE_DEST_3七、LOG_ARCHIVE_DES…

cat命令详解

cat 是 Linux/Unix 中的一个非常常用的命令&#xff0c;主要用于 连接 文件并显示文件内容。它的名称来源于 concatenate&#xff08;连接&#xff09;&#xff0c;不仅可以查看文件内容&#xff0c;还能将多个文件合并为一个文件&#xff0c;或用作其他数据流操作。 以下是对 …

php 多进程那点事,用 swoole 如何解决呢 ?

在 PHP 中&#xff0c;多进程的处理通常会遇到一些挑战&#xff0c;比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架&#xff0c;旨在为 PHP 提供异步、并发、协程等功能&#xff0c;解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…

[sdx12] Qualcomm SDX12查看基线版本

about.html文件 Build部分 Product SDX12.LE.1.0-00263-NBOOT.NEFS.PROD-1.90789.1 Distribution SDX12.LE.1.0|AMSS|Standard|OEM: Build Components部分 从以上截图可以看到以下模块的版本号及格式 BOOT 基线版本号 BOOT.BF.3.1.c3-00010-SDX12AAAAANAZB-1 Distr…

基于CLIP和DINOv2实现图像相似性方面的比较

概述 在人工智能领域&#xff0c;CLIP和DINOv2是计算机视觉领域的两大巨头。CLIP彻底改变了图像理解&#xff0c;而DINOv2为自监督学习带来了新的方法。 在本文中&#xff0c;我们将踏上一段旅程&#xff0c;揭示定义CLIP和DINOv2的优势和微妙之处。我们的目标是发现这些模型…

HC32L160FCUA-QFN32TR

HC32L16x 系列具有灵活的功耗管理系统&#xff0c;超低功耗性能 – 0.8μA3V 深度休眠模式&#xff1a;所有时钟关闭&#xff0c;上电复位有效&#xff0c;IO 状态保持&#xff0c;IO 中断有效&#xff0c;所有寄存器、RAM 和CPU 数据保存状态时的功耗 ● 最大256KB Flash 存…

后台运行 Python

后台运行 Python 如何运行 在后台运行 Python 程序的方式取决于操作系统以及具体需求&#xff08;是否需要退出后继续运行、查看输出等&#xff09;。以下是几种常用方法&#xff1a; 1. 使用 & (Linux/Unix/MacOS) 在命令末尾加上 &&#xff0c;可以让程序在后台运…

LS1046 XFI网口接近10Gbps

硬件平台&#xff1a; CPU LS1046A 1.8GHZ 软件平台&#xff1a; LINUX 4.19.32 BUILDROOT 测试软件&#xff1a; ipferf 整个过程比较曲折&#xff0c;网口默认不能达到这个速度&#xff0c;只有2Gbps以内。需要FMC配置后才能达到9.4Gbps。

逆向安卓抓包

打开Mumu网易&#xff0c;打开设置&#xff0c;打开其他&#xff0c;开启root权限 打开Mumu网易&#xff0c;找到apk安装藏航准备网.apk charles配置&#xff1a;proxy setting 端口9888 查看当地IP:help--->local IP address SSL Proxying Setting--->Add---->IP…

大数据安全需求分析与安全保护工程

26.1 威胁与需求分析 1&#xff09;概念发展 2&#xff09;威胁分析 数据集 安全边界日渐模糊&#xff0c;安全保护难度提升 敏感数据泄漏安全风险增大 数据失真与大数据污染安全风险 大数据处理平台业务连续性与拒绝服务 个人数据广泛分布于多个数据平台&#xff0c;隐…