分布式環境下 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.pswp.cn/web/86207.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86207.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86207.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋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;因為他們證明了在前線、創收的工藝操作中部署…