分布式环境下 Spring Boot 项目基于雪花算法的唯一 ID 生成方案

一、分布式系统

分布式系统是指将多个独立的计算节点通过网络连接,协同完成同一目标的系统架构。其核心特征是:

  • 多个独立节点:每个节点都是一个可独立运行的服务实例
  • 网络通信:节点间通过网络协议(如HTTP、RPC)交换数据
  • 协同工作:共同完成统一的业务目标(如处理请求、存储数据)

二、分布式环境下 Spring Boot 项目的部署方法

(一) Docker多节点部署
  • 实现方式:将同一个Spring Boot JAR包构建为Docker镜像,然后在多个服务器(物理机/虚拟机)上启动多个容器实例。
  • 示例场景
    服务器A:运行Docker容器实例1(端口8081)
    服务器B:运行Docker容器实例2(端口8082)
    服务器C:运行Docker容器实例3(端口8083)
    
  • 关键配置
    • 每个容器需分配唯一的雪花算法参数(dataCenterIdmachineId
    • 通过Nginx等负载均衡器将请求分发到不同容器
    • 示例雪花算法配置(动态获取容器ID):
      // 从Docker环境变量获取节点标识(需在docker run时通过-e参数传入)
      long dataCenterId = Long.parseLong(System.getenv("DATACENTER_ID"));
      long machineId = Long.parseLong(System.getenv("MACHINE_ID"));
      
(二)微服务架构下的多服务节点
  • 场景:一个大型系统拆分为多个独立服务,每个服务部署为分布式节点
    - 用户服务(user-service):部署3个节点
    - 订单服务(order-service):部署5个节点
    - 商品服务(product-service):部署2个节点
    
  • 雪花算法应用
    • 每个服务集群分配独立的dataCenterId(如用户服务为1,订单服务为2)
    • 每个服务节点分配唯一的machineId(如节点1、节点2)

三、分布式环境下雪花算法的使用问题

(一) 节点ID分配问题
  • 挑战:如何确保分布式节点的dataCenterIdmachineId唯一
  • 解决方案
    • 配置文件静态分配:适用于节点固定的场景(如手动编辑application.yml
      snowflake:data-center-id: 1  # 数据中心ID(0-31)machine-id: 2       # 机器ID(0-31)
      
    • 分布式协调服务动态分配:适用于动态扩缩容场景(如ZooKeeper)
      // 通过ZooKeeper获取唯一节点ID
      String nodePath = zkClient.createEphemeralSequential("/snowflake/nodes/", "node-");
      long machineId = Long.parseLong(nodePath.substring(nodePath.lastIndexOf("-") + 1));
      
(二)时钟回拨问题
  • 场景:某节点因硬件故障或时区调整导致系统时间回退
  • 解决方案
    • 雪花算法已内置时钟回拨处理(如示例中的waitNextMillis方法)
    • 更严格的方案:结合Redis等分布式锁,在时钟回拨时阻塞生成ID
      明白了!我将修改雪花算法的实现,使其适应你描述的分布式用户服务场景。关键是要为每个节点分配唯一的 dataCenterIdmachineId

四、示例:雪花算法实现

SnowflakeIdGenerator
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** 分布式环境下的雪花算法ID生成器* 支持从配置文件或环境变量读取dataCenterId和machineId*/
@Component
public class SnowflakeIdGenerator {// 起始时间戳 (2020-01-01)private final long startTimeStamp = 1577836800000L;// 各部分占用的位数private final long dataCenterIdBits = 5L;private final long machineIdBits = 5L;private final long sequenceBits = 12L;// 各部分的最大值private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); // 31private final long maxMachineId = -1L ^ (-1L << machineIdBits); // 31private final long maxSequence = -1L ^ (-1L << sequenceBits); // 4095// 各部分向左的位移private final long machineIdShift = sequenceBits;private final long dataCenterIdShift = sequenceBits + machineIdBits;private final long timestampShift = sequenceBits + machineIdBits + dataCenterIdBits;private final long dataCenterId;private final long machineId;private long sequence = 0L;private long lastTimestamp = -1L;/*** 构造函数,从配置中读取dataCenterId和machineId*/public SnowflakeIdGenerator(@Value("${snowflake.datacenter-id:1}") long dataCenterId,@Value("${snowflake.machine-id:1}") long machineId) {// 检查ID是否合法if (dataCenterId > maxDataCenterId || dataCenterId < 0) {throw new IllegalArgumentException("DataCenter ID must be between 0 and " + maxDataCenterId);}if (machineId > maxMachineId || machineId < 0) {throw new IllegalArgumentException("Machine ID must be between 0 and " + maxMachineId);}this.dataCenterId = dataCenterId;this.machineId = machineId;System.out.printf("初始化雪花算法ID生成器: datacenterId=%d, machineId=%d%n", dataCenterId, machineId);}// 生成ID的核心方法保持不变public synchronized long nextId() {long currentTimestamp = System.currentTimeMillis();// 处理时钟回拨if (currentTimestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - currentTimestamp) + " milliseconds");}if (currentTimestamp == lastTimestamp) {sequence = (sequence + 1) & maxSequence;if (sequence == 0) {// 当前毫秒内序列号已用完,等待下一毫秒currentTimestamp = waitNextMillis(lastTimestamp);}} else {// 不同毫秒,重置序列号sequence = 0L;}lastTimestamp = currentTimestamp;// 按位或组合生成IDreturn ((currentTimestamp - startTimeStamp) << timestampShift) |(dataCenterId << dataCenterIdShift) |(machineId << machineIdShift) |sequence;}private long waitNextMillis(long lastTimestamp) {long timestamp = System.currentTimeMillis();while (timestamp <= lastTimestamp) {timestamp = System.currentTimeMillis();}return timestamp;}
}    
User
@Data
public class User {private Long id;private String username;private String createTime;private String updateTime;
}   
UserController
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {@Autowiredprivate UserService userService;@PostMappingpublic User createUser(@RequestBody User user) {return userService.createUser(user);}@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return userService.getUserById(id);}
}    
UserService
public interface UserService {/*** 创建用户* @param user 用户信息* @return 创建后的用户对象(包含生成的ID)*/User createUser(User user);/*** 根据ID获取用户* @param id 用户ID* @return 用户对象*/User getUserById(Long id);
}    
UserServiceImpl

createUser方法:

  • 调用idGenerator.nextId()生成全局唯一 ID
  • 设置用户的创建时间和更新时间
  • 调用 MyBatis 的 Mapper方法将用户信息插入数据库

确保了在分布式环境下,即使多个用户服务节点同时处理创建用户请求,生成的 ID 也不会冲突。

@Service
@Transactional
public class UserServiceImpl implements UserService {@Autowiredprivate SnowflakeIdGenerator idGenerator;@Autowiredprivate UserMapper userMapper;@Overridepublic User createUser(User user) {// 生成全局唯一IDlong userId = idGenerator.nextId();user.setId(userId);// 设置创建时间和更新时间LocalDateTime now = LocalDateTime.now();user.setCreateTime(now);user.setUpdateTime(now);// 插入数据库userMapper.insert(user);return user;}@Overridepublic User getUserById(Long id) {return userMapper.selectById(id);}
}    
配置文件
application-node1.yml
server:port: 8081spring:datasource:url: jdbc:mysql://localhost:3306/user_db?useSSL=false&serverTimezone=UTCusername: rootpassword: yourpassworddriver-class-name: com.mysql.cj.jdbc.Driver# 雪花算法配置
snowflake:datacenter-id: 1  # 数据中心IDmachine-id: 1     # 机器ID (节点A)    
application-node2.yml
server:port: 8082spring:datasource:url: jdbc:mysql://localhost:3306/user_db?useSSL=false&serverTimezone=UTCusername: rootpassword: yourpassworddriver-class-name: com.mysql.cj.jdbc.Driver# 雪花算法配置
snowflake:datacenter-id: 1  # 数据中心IDmachine-id: 2     # 机器ID (节点B)    
application-node3.yml
server:port: 8083spring:datasource:url: jdbc:mysql://localhost:3306/user_db?useSSL=false&serverTimezone=UTCusername: rootpassword: yourpassworddriver-class-name: com.mysql.cj.jdbc.Driver# 雪花算法配置
snowflake:datacenter-id: 1  # 数据中心IDmachine-id: 3     # 机器ID (节点C)    

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

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

相关文章

如何在 Chrome 浏览器中保存从商店下载的扩展程序到本地

如何在 Chrome 浏览器中保存从商店下载的扩展程序到本地 方法一&#xff1a;通过扩展程序页面直接导出&#xff08;适用于已安装的扩展&#xff09; 打开 Chrome 扩展管理页面 在地址栏输入&#xff1a;chrome://extensions/或通过菜单&#xff1a;⋮ > 更多工具 > 扩展…

没有VISA怎么注册AWS?

没有VISA怎么注册AWS&#xff1f;跨境卖家、开发者与学生党必看的“AWS账号开通终极指南”&#xff01; 在云计算日益普及的今天&#xff0c;Amazon Web Services&#xff08;AWS&#xff09;作为全球领先的云服务提供商&#xff0c;以其服务广度、技术深度和生态系统成熟度&a…

华为服务器的选型指南

以下是华为服务器选型指南及推荐方案&#xff0c;综合性能、场景适配与成本优化&#xff1a; 一、核心选型维度 业务场景 通用计算&#xff08;Web/中间件&#xff09;&#xff1a;推荐通用型鲲鹏服务器&#xff08;如KH系列&#xff09;&#xff0c;支持多核并发&#xff08…

Python-3-数据结构(字典)

1 字典 特点 1.键-值成对出现 “键&#xff1a;值 ” 2.键不能重复 3.键不可更改&#xff0c;值可以修改 4.键来索引值 5.键只能是不可变的数据类型 dic_Python{the define:1,the age:2,the clude:[A,B] } #增删查改 dic_Python[the age] dic_Python[the define]77#赋值修改 di…

API访问Token的概念及解析

API 访问Token&#xff08;API Access Token&#xff09; 如大家所了解的&#xff0c;API访问Token是用于第三方应用调用服务的关键措施&#xff0c;如支付接口、地图 API等。 许多机构和安全指南&#xff08;例如 OWASP API Security Project&#xff09;建议采用短期 Token…

​​​​​​​[AI 工具] Dify 免费 GPT 调用详解:额度、付费与自托管方案全解

引言:Dify 是什么? Dify 是一个支持多种大模型(如 GPT-3.5、GPT-4、GPT-4o)的开源 AI 应用开发平台,支持 Web UI 快速搭建、多模态能力、团队协作等。其平台免费开放使用 GPT 模型,受到开发者和技术社区广泛关注。 我是Dify重度用户,大多数情况用本地部署,但是忽然发…

构建你的API防护盾 - 抵御恶意爬虫、注入与业务欺诈

现代App和Web应用的核心是API&#xff0c;它也是攻击者的首要目标。恶意爬虫窃取数据、SQL注入篡改数据库、精心构造的请求进行薅羊毛或欺诈… 这些业务逻辑层的攻击&#xff0c;往往能绕过传统防火墙。本文将分享几种实用的API防护技术&#xff0c;并提供可直接部署的代码示例…

从 “人工巡检” 到 “远程智控”,工业路由器实现变电站远程监控

能源电力行业加速数字化转型&#xff0c;负责电力输送与分配的变电站智能化升级迫在眉睫。工业路由器在变电站远程监控领域成功应用&#xff0c;是能源电力物联网建设必不可少的核心通讯设备。 变电站远程监控项目背景 传统变电站监控依赖人工巡检与有线通信&#xff0c;效率低…

xss利用meta强制跳转 CPS report-uri 报错泄露利用 -- GPN CTF 2025 Free Parking Network 1 2

part 1 在此题目中,我们可以指定html与标头 <sCrIpt>alert(1)</ScRipt>A5rz: A5rz服务器会返回如下内容 HTTP/1.1 200 OK X-Powered-By: Express A5rz: A5rz Content-Type: text/html; charsetutf-8 Content-Length: 619 ETag: W/"26b-14GnlOyaaXJ3CEkd0rBJ/m…

1 Web vue环境搭建

1 下载好node.js 用node -v和npm -v看是否环境配置好&#xff0c;看到如下结果就是配置好了 2 安装vue脚手架 输入这个代码 npm i vue/cli -g 查看到如下&#xff0c;说明安装成功 3 下载vue初始模板 输入 vue ui 会打开一个网页 点击创建&#xff0c;然后点击编辑路径&…

太理IM即时通讯软件开发

easyQQ ♻️项目基本介绍 easyQQ是基于electron(vue2)和nodejs实现的简单聊天软件,其中用websocket和http进行通讯传递,数据库使用了mysql数据库,该项目功能简单,界面简洁,每个功能都会添加相应的逻辑 &#x1f9e7; 作者自己的配置环境 数据库 nodejs npm &#x1f9e8; 部…

BERT 模型准备与转换详细操作流程

在尝试复现极客专栏《PyTorch 深度学习实战|24 | 文本分类&#xff1a;如何使用BERT构建文本分类模型&#xff1f;》时候&#xff0c;构建模型这一步骤专栏老师一笔带过&#xff0c;对于新手有些不友好&#xff0c;经过一阵摸索&#xff0c;终于调通了&#xff0c;现在总结一下…

doris 和StarRocks 导入导出数据配置

一、StarRocks 导数据到hdfs EXPORT TABLE database.table TO “hdfs://namenode/tmp/demo/table” WITH BROKER ( “username”“username”, “password”“password” ); 二、StarRocks 导数据到oss EXPORT TABLE database.table TO “oss://broke/aa/” WITH BROKER ( “…

【HTTP】取消已发送的请求

场景 在页面中&#xff0c;可能会因为某些操作多次触发某个请求&#xff0c;如多次点击某按钮触发请求&#xff0c;实际上我们只需要最后一次请求的返回值&#xff0c;但是由于请求的耗时不一&#xff0c;请求未必会按发送的顺序返回&#xff0c;导致我们最终获取到的值 ≠ 最后…

JSON框架转化isSuccess()为sucess字段

在您的描述中&#xff0c;BankInfoVO子类返回的JSON中出现了"success": true字段&#xff0c;但类本身没有定义这个字段。这通常是由以下原因之一造成的&#xff1a; 原因分析及解决方案 序列化框架的Getter自动推导 Java序列化框架&#xff08;如Jackson/Gson&…

Ragflow 源码:task_executor.py

目录 介绍主要功能核心组件 流程图核心代码解释1. 系统架构与核心组件2. 核心处理流程3. 高级处理能力4. 关键创新点5. 容错与监控机制6. 性能优化技巧 介绍 task_executor.py 是RAGFlow系统中的任务执行器(Task Executor)核心部分&#xff0c;主要负责文档的解析、分块(chunk…

创客匠人联盟生态:重构家庭教育知识变现的底层逻辑

在《家庭教育促进法》推动行业刚需化的背景下&#xff0c;单一个体 IP 的增长天花板日益明显。创客匠人提出的 “联盟生态思维”&#xff0c;正推动家庭教育行业从 “单打独斗” 转向 “矩阵作战”&#xff0c;其核心在于通过工具整合资源&#xff0c;将 “同行竞争” 转化为 “…

【Docker基础】Docker容器管理:docker stop详解

目录 1 Docker容器生命周期概述 2 docker stop命令深度解析 2.1 命令基本语法 2.2 命令执行流程 2.3 stop与kill的区别 3 docker stop的工作原理 3.1 工作流程 3.2 详细工作流程 3.3 信号处理机制 4 docker stop的使用场景与最佳实践 4.1 典型使用场景 场景1&#…

rules写成动态

拖拽排序和必填校验联动(rules写到computed里) computed: {rules() {const rules {};this.form.feedList.forEach((item, idx) > {rules[feedList.${idx}] [{ required: true, message: 路线评价动态${idx 1}待填写&#xff0c;请填写完毕提交, trigger: change }];});re…

The Open Group开放流程自动化™ 论坛(OPAF)发布组织最新进展报告

除埃克森美孚&#xff08;ExxonMobil&#xff09;的成就外&#xff0c;开放流程自动化™ 论坛&#xff08;OPAF&#xff09;的最新论坛报告显示&#xff0c;该组织其他成员也在多个领域取得进展。 “我们祝贺埃克森美孚&#xff0c;因为他们证明了在前线、创收的工艺操作中部署…