分布式ID生成-雪花算法实现无状态
雪花算法这里不再赘述,其缺点是有状态(多副本隔离时,依赖手动配置workId和datacenterId),代码如下:
/*** 雪花算法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);}}}相关文章:
分布式ID生成-雪花算法实现无状态
雪花算法这里不再赘述,其缺点是有状态(多副本隔离时,依赖手动配置workId和datacenterId),代码如下: /*** 雪花算法ID生成器*/ public class SnowflakeIdWorker {/*** 开始时间截 (2017-01-01)*/private st…...
【问题】配置 Conda 与 Pip 源
通常情况下,使用 conda 命令或者 pip 命令都是从国外的服务器上下载需要的模块包的,这在网速不佳的情况下会消耗大量的时间。所以这里建议更换国内的源来进行模块下载,速度会大大提升。 具体方法如下: 打开命令行 cmd 工具,输入以下命令。 ① Conda 换源 conda config…...
Zookeeper是如何保证事务的顺序一致性的?
大家好,我是锋哥。今天分享关于【Zookeeper是如何保证事务的顺序一致性的?】面试题。希望对大家有帮助; Zookeeper是如何保证事务的顺序一致性的? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Zookeeper 通过多个机制来保证事务的顺序一…...
东土科技参股广汽集团飞行汽车初创公司,为低空经济构建新型产业生态
近日,广汽集团旗下专注于飞行汽车领域的初创公司广东高域科技有限公司于2024年12月31日正式成立,在穿透后的股东信息中,东土科技通过广州瓴云科技投资合伙企业(有限合伙)赫然在列。 此前12月18日,广汽集团…...
Oracle 中的各种名称(*_name)参数的含义与作用
Oracle 中的各种名称(*_name)参数的含义与作用 目录 Oracle 中的各种名称(*_name)参数的含义与作用一、数据库名:db_name二、数据库所在的域:db_domain三、全局数据库名:global_name四、服务名&…...
前端页面的内容格式
TaskBuilder可以编辑的前端页面类型分为HTML和tfp(Taskmsg Front Page)两种格式,HTML格式只能用代码编辑器编辑,这种模式基本很少有人用,大家都用tfp格式,因为使用TaskBuilder向导创建的前端页面都是采用的…...
数据库1-4讲
各种名词区分 内模式也叫物理模式、存储模式。 概念模式也叫全局模式、逻辑模式。 外模式也叫用户模式。 笛卡尔积:D1、D2、D3集合中任取一个的所有可能情况。 因此上述笛卡尔积的基数22312 关系模型的三个完整性: 实体完整性&#x…...
设计模式 行为型 命令模式(Command Pattern)与 常见技术框架应用 解析
命令模式(Command Pattern)是一种行为型设计模式,它旨在将请求发送者和接收者解耦,通过将一个请求封装为一个对象,从而允许参数化客户端对象以进行不同的请求、排队请求或记录请求,并支持可撤销操作。 在软…...
【Redis】简介|优点|使用场景|为什么Redis快
目录 一、简介 二、特性(优点) 三、使用场景 一、简介 内存中存储数据的中间件,用于数据库,数据缓存,在分布式系统中能够大展拳脚 中间件:应用程序可以直接从 Redis 中获取数据,而不必频繁地…...
Clisoft SOS与CAD系统集成
Clisoft SOS与CAD系统集成 以下内容大部分来自官方文档,目前只用到与Cadence Virtuoso集成,其他还未用到,如有问题或相关建议,可以留言。 与Keysight ADS集成 更新SOS客户端配置文件sos.cfg,以包含支持ADS的模板&am…...
【linux系统之redis6】redis的安装与初始化
下载redis的linux对应的安装包,并上传到linux虚拟机里面 解压压缩包 tar -zxzf redis-6.2.6.tar.gz解压后,进入redis文件 cd redis-6.2.6执行编译 make && make install看到下图,就说明redis安装成功了 默认的安装路径,…...
Backend - EF Core(C# 操作数据库 DB)
目录 一、EF Core 1. 使用的ORM框架(对象关系映射) 2. EFCore 常见两种模式 3. EFCore 提供程序 二、 EF 操作数据库(Code First) 1. 下载NuGet插件 2.创建模型类文件 3.创建DBContext文件 4.Programs.cs文件 5.appsettings.Devel…...
WebSocket 性能优化:从理论到实践
在前四篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现和安全实践。今天,让我们把重点放在性能优化上,看看如何构建一个高性能的 WebSocket 应用。我曾在一个直播平台项目中,通过一系列优化措施,将单台服务器的并发连接数从 1 万提升到 10 万。 性能挑…...
我用AI学Android Jetpack Compose之入门篇(2)
我跑成功了第一个Compose应用,但我还是有很多疑问,请人工智能来解释一下吧。答案来自 通义千问 文章目录 1.请解释一下Compose项目的目录结构。根目录模块目录(通常是app)app/build.gradleapp/src/mainapp/src/main/uiapp/src/ma…...
以太网协议在汽车应用中的动与静
为了让网络中的设备能够随时或随地接入网络或离开网络,做到:接入时无需多余的配置就能和其他设备互相通信;离开时又不会导致设备或网络崩溃。以太网从物理层到协议层展现出多方面的灵活性,,使其成为连接各种设备和系统…...
【C语言】_指针与数组
目录 1. 数组名的含义 1.1 数组名与数组首元素的地址的联系 1.3 数组名与首元素地址相异的情况 2. 使用指针访问数组 3. 一维数组传参的本质 3.1 代码示例1:函数体内计算sz(sz不作实参传递) 3.2 代码示例2:sz作为实参传递 3…...
Selenium 的四种等待方式及使用场景
Selenium 的四种等待方式及使用场景 隐式等待(Implicit Wait)显式等待(Explicit Wait)自定义等待(Custom Wait)固定等待(Sleep) 1. 隐式等待 定义: 隐式等待是为 WebD…...
React知识盲点——组件通信、性能优化、高级功能详解(大纲)
组件通信 React 组件通信详解 在 React 中,组件通信是一个核心概念,主要指的是如何让不同的组件共享和传递数据。React 提供了多种机制来实现组件间的数据传递和状态共享。以下是几种常见的组件通信方式,包括:父子组件通信&…...
Vue 按键生成多个表单
本文通过 vueele,通过循环ref的方法生成多个表单,代码如下: <template><div><el-button click"addText" style"margin: 15px 0; ">添加字段</el-button><div v-for"item, index in dataList"…...
网络安全:交换机技术
单播,组播广播 单播(unicast): 是指封包在计算机网络的传输中,目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛,通常所使用的网络协议或服务大多采用单播传输,例如一切基于TCP的协议。组播(multicast): 也叫多播&am…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
