Spring Boot工程整合全域性唯一ID生成器 UidGenerator
Spring Boot中全域性唯一流水號ID生成器整合實驗
本文共 823字,閱讀大約需要 3分鐘 !
概述
流水號生成器(全域性唯一 ID生成器)是服務化系統的基礎設施,其在保障系統的正確執行和高可用方面發揮著重要作用。而關於流水號生成演算法首屈一指的當屬 ofollow,noindex">Snowflake 雪花演算法,然而 Snowflake本身很難在現實專案中直接使用,因此實際應用時需要一種可落地的方案。
UidGenerator 由百度開發,是Java實現的, 基於 Snowflake演算法的唯一ID生成器。UidGenerator以元件形式工作在應用專案中, 支援自定義workerId位數和初始化策略, 從而適用於 docker等虛擬化環境下例項自動重啟、漂移等場景。
本文就在專案中來整合 UidGenerator這一工程來作為專案的全域性唯一 ID生成器。
注:本文首發於 My Personal Blog ,歡迎光臨 小站
本文內容腦圖如下:
基礎工程建立
只需建立一個 Multi-Moudule的 Maven專案即可,然後我們整合進兩個 Module:
- uid-generator : 原始碼在此
- uid-consumer :消費者( 使用uid-generator產生全域性唯一的流水號 )
uid-generator
模組我就不多說了,原始碼拿過來即可,無需任何改動;而關於 uid-consumer
模組,先在 pom.xml中新增相關依賴如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--for Mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.12</version> </dependency> <!-- druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <!--必須放在最後--> <dependency> <groupId>cn.codesheep</groupId> <artifactId>uid-generator</artifactId> <version>1.0</version> </dependency> </dependencies>
然後在 application.properties配置檔案中新增一些配置(主要是 SQL/">MySQL和 MyBatis配置)
server.port=9999 spring.datasource.url=jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxx?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=xxx spring.datasource.driver-class-name=com.mysql.jdbc.Driver mybatis.mapper-locations=classpath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-case=true
完成之後工程縮影如下圖所示:
下面我們來一步步整合 UidGenerator
的原始碼。
資料庫建表
首先去 MySQL資料庫中建一個名為 WORKER_NODE
的資料表,其 sql如下:
DROP TABLE IF EXISTS WORKER_NODE; CREATE TABLE WORKER_NODE ( ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id', HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name', PORT VARCHAR(64) NOT NULL COMMENT 'port', TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER', LAUNCH_DATE DATE NOT NULL COMMENT 'launch date', MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time', CREATED TIMESTAMP NOT NULL COMMENT 'created time', PRIMARY KEY(ID) ) COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
Spring詳細配置
- CachedUidGenerator 配置
UidGenerator 有兩個具體的實現類,分別是 DefaultUidGenerator
和 CachedUidGenerator
,不過官方也推薦了對於效能比較敏感的專案應使用後者,因此本文也使用 CachedUidGenerator
,而對於 DefaultUidGenerator
不做過多闡述。
我們引入 UidGenerator原始碼中的 cached-uid-spring.xml
檔案,裡面都是預設配置,我目前沒有做任何修改
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- UID generator --> <bean id="disposableWorkerIdAssigner" class="com.baidu.fsg.uid.worker.DisposableWorkerIdAssigner" /> <bean id="cachedUidGenerator" class="com.baidu.fsg.uid.impl.CachedUidGenerator"> <property name="workerIdAssigner" ref="disposableWorkerIdAssigner" /> <!-- 以下為可選配置, 如未指定將採用預設值 --> <!-- RingBuffer size擴容引數, 可提高UID生成的吞吐量. --> <!-- 預設:3, 原bufferSize=8192, 擴容後bufferSize= 8192 << 3 = 65536 --> <!--<property name="boostPower" value="3"></property>--> <!-- 指定何時向RingBuffer中填充UID, 取值為百分比(0, 100), 預設為50 --> <!-- 舉例: bufferSize=1024, paddingFactor=50 -> threshold=1024 * 50 / 100 = 512. --> <!-- 當環上可用UID數量 < 512時, 將自動對RingBuffer進行填充補全 --> <!--<property name="paddingFactor" value="50"></property>--> <!-- 另外一種RingBuffer填充時機, 在Schedule執行緒中, 週期性檢查填充 --> <!-- 預設:不配置此項, 即不實用Schedule執行緒. 如需使用, 請指定Schedule執行緒時間間隔, 單位:秒 --> <!--<property name="scheduleInterval" value="60"></property>--> <!-- 拒絕策略: 當環已滿, 無法繼續填充時 --> <!-- 預設無需指定, 將丟棄Put操作, 僅日誌記錄. 如有特殊需求, 請實現RejectedPutBufferHandler介面(支援Lambda表示式) --> <!--<property name="rejectedPutBufferHandler" ref="XxxxYourPutRejectPolicy"></property>--> <!-- 拒絕策略: 當環已空, 無法繼續獲取時 --> <!-- 預設無需指定, 將記錄日誌, 並丟擲UidGenerateException異常. 如有特殊需求, 請實現RejectedTakeBufferHandler介面(支援Lambda表示式) --> <!--<property name="rejectedPutBufferHandler" ref="XxxxYourPutRejectPolicy"></property>--> </bean> </beans>
- Mybatis Mapper XML 配置
即原樣引入 UidGenerator原始碼中關於工作節點(Worker Node)操作的 mapper xml 檔案: WORKER_NODE.xml
,其內容如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.baidu.fsg.uid.worker.dao.WorkerNodeDAO"> <resultMap id="workerNodeRes" type="com.baidu.fsg.uid.worker.entity.WorkerNodeEntity"> <id column="ID" jdbcType="BIGINT" property="id" /> <result column="HOST_NAME" jdbcType="VARCHAR" property="hostName" /> <result column="PORT" jdbcType="VARCHAR" property="port" /> <result column="TYPE" jdbcType="INTEGER" property="type" /> <result column="LAUNCH_DATE" jdbcType="DATE" property="launchDate" /> <result column="MODIFIED" jdbcType="TIMESTAMP" property="modified" /> <result column="CREATED" jdbcType="TIMESTAMP" property="created" /> </resultMap> <insert id="addWorkerNode" useGeneratedKeys="true" keyProperty="id" parameterType="com.baidu.fsg.uid.worker.entity.WorkerNodeEntity"> INSERT INTO WORKER_NODE (HOST_NAME, PORT, TYPE, LAUNCH_DATE, MODIFIED, CREATED) VALUES ( #{hostName}, #{port}, #{type}, #{launchDate}, NOW(), NOW()) </insert> <select id="getWorkerNodeByHostPort" resultMap="workerNodeRes"> SELECT ID, HOST_NAME, PORT, TYPE, LAUNCH_DATE, MODIFIED, CREATED FROM WORKER_NODE WHERE HOST_NAME = #{host} AND PORT = #{port} </select> </mapper>
編寫業務程式碼
- config 類建立與配置
新建 UidConfig
類,為我們引入上文的 cached-uid-spring.xml
配置
@Configuration @ImportResource(locations = { "classpath:uid/cached-uid-spring.xml" }) public class UidConfig { }
- service 類建立與配置
新建 UidGenService
,引入 UidGenerator 生成 UID的業務介面
@Service public class UidGenService { @Resource private UidGenerator uidGenerator; public long getUid() { return uidGenerator.getUID(); } }
- controller 建立與配置
新建 UidTestController
,目的是方便我們用瀏覽器測試介面並觀察效果:
@RestController public class UidTestController { @Autowired private UidGenService uidGenService; @GetMapping("/testuid") public String test() { return String.valueOf( uidGenService.getUid() ); } }
實驗測試
我們每啟動一次 Spring Boot工程,其即會自動去 MySQL資料的 WORKER_NODE
表中插入一行關於工作節點的記錄,類似下圖所示:
接下來我們瀏覽器訪問: http://localhost:9999/testuid
OK,全域性唯一流水號ID已經成功生成並返回!
後 記
由於能力有限,若有錯誤或者不當之處,還請大家批評指正,一起學習交流!
- My Personal Blog:CodeSheep 程式羊
- 我的半年技術部落格之路
可 長按 或 掃描 下面的 小心心 來訂閱 CodeSheep ,獲取更多 務實、能看懂、可復現的 原創文 ↓↓↓
最後附上個人聯絡方式,歡迎大家交流學習:
個人部落格:www.codesheep.cn
簡 書: https://www.jianshu.com/u/d19536b0189b