ID生成方案榆芦,基于twitter的snowflake分布式ID生成改造,在集群環(huán)境中多實例自動分配snowflake的workId,dataCenterId喘鸟;如果是小項目小業(yè)務(wù)不需要考慮匆绣,直接基本snowflake在配置文件中配置即可,本項目將會在docker swarm 和 k8s中部署測試迷守。
源碼
# 單機(jī)多實例不同端口
git clone -b single-v1.0 https://git.dev.tencent.com/liangxiaobo/uuid-generate.git
# Docker Swarm集群模式
git clone -b cluster-v1.0 https://git.dev.tencent.com/liangxiaobo/uuid-generate.git
# k8s集群模式
git clone -b cluster-k8s-v1.0 https://git.dev.tencent.com/liangxiaobo/uuid-generate.git
# 單機(jī)多實例不同端口
git clone -b single-v1.0 https://github.com/liangxiaobo/uuid-generate.git
# Docker Swarm集群模式
git clone -b cluster-v1.0 https://github.com/liangxiaobo/uuid-generate.git
# k8s集群模式
git clone -b cluster-k8s-v1.0 https://git.dev.tencent.com/liangxiaobo/uuid-generate.git
項目思路
- 以Eureka為注冊中心
- Redis 存儲
- ID-Server 配置種子數(shù)據(jù)的初始化并處理過期的配置種子
- ID-Client ID生成服務(wù)
定義簡稱
- Redis中自定義的三個存儲簡稱 R1(未全用池)犬绒、R2(已使用池)旺入、R3(id-client實例和R2中的數(shù)據(jù)關(guān)聯(lián)池)
- workId和dataCenterId簡稱WD
大概思路是:
- 由一個應(yīng)用ID-Server主動創(chuàng)建足夠量的workId和DataCenterId兑凿,放到Redis中的R1中
- ID-Client是ID生成服務(wù)凯力,需要配置workId和dataCenterId,所以啟動時第一步從Redis的R1中讀取可用的worId和dataCenterId礼华,
第二步把拿到的可用WD放到放到R2中咐鹤,第三步獲取當(dāng)前Eureka-client客戶端的實例ID和剛才放入R2的數(shù)據(jù)關(guān)聯(lián)放R3中,在此同時ID-Client也向Eureka-Server注冊 - ID-Server會獲取Eureka-Server中ID-Client的注冊實例列表圣絮,定時判斷祈惶,R3中的列表數(shù)據(jù)和Eureka-Server中的實例列表的差異,如果有差異表示有機(jī)器實例Down了扮匠,ID-Server主動收回R2中的數(shù)據(jù)給R1捧请,并刪除R3中無效應(yīng)用實例的數(shù)據(jù)
ID-Server
當(dāng)前設(shè)計ID-Server只能跑一個實例;
- 當(dāng)ID-Server第一次運(yùn)行棒搜,這時候Redis中的R1是空的疹蛉,需要初始化配置數(shù)據(jù)(workId,dataCenterId 后面簡稱WD),配置文件中自定義參數(shù)
spring:
application:
name: uuid-server
redis: # redis配置 ========================
host: localhost
port: 6379
timeout: 5000ms
server:
port: 8082
eureka:
client:
registryFetchIntervalSeconds: 5
service-url:
defaultZone: http://localhost:8761/eureka/
fetch-registry: true # 只需要拉取注冊表就可以
register-with-eureka: false # 不需要向Eureka注冊
uuid:
# ID生成器的應(yīng)用名稱
client-application-name: uuid-snowflake-client
# 機(jī)器數(shù)量力麸,默認(rèn)2臺可款,最大31*31=961臺
machine-count: 10
# 同步檢測已使用池中的數(shù)據(jù)和eureka注冊表中的實例數(shù)30秒,生產(chǎn)環(huán)境設(shè)置10分鐘以上
task-execution-interval: 30
R1 中存的是一個Map數(shù)據(jù)格式[{1:{workId:1,dataCenterId:1}}, {2:{workId:1,dataCenterId:2}}, ...]
R2 中存的數(shù)據(jù)格式Map [{1:{workId:1,dataCenterId:1}}, {2:{workId:1,dataCenterId:2}}, ...]
R3 中的數(shù)據(jù)格式 Map [{"實例1": 1}, {"實例2": 2}]
- 如果是重啟ID-Server會主動去讀取Redis中R1池中未被使用的WD
- 監(jiān)控Redis中R3的數(shù)據(jù)列表克蚂,與Eureka-Server中的ID-Client實例列表相對比差異
ID-Client
- 啟動時從Redis中的R1中取一個WD放到R2中闺鲸,并和取出來的WD的KEY 關(guān)聯(lián)后放到R3中記錄
配置文件中需要注意的是:
......
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
# 如果是集群模式部署,instance-id值必須設(shè)置
instance-id: ${spring.application.name}:${server.port}:${random.int}
......
- snowflake以單例模式運(yùn)行
運(yùn)行-單機(jī)
注意:運(yùn)行順序 Eureka-Server => ID-Server => ID-Client
單機(jī)多實例不同端口測試埃叭,跑兩個實例端口分別是8121和8123
2019-06-10 19:25:55.910 INFO 39078 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
2019-06-10 19:25:55.914 INFO 39078 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : The response status is 200
2019-06-10 19:25:56.201 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 未使用DataPoolMap 10
2019-06-10 19:25:56.215 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 1. 應(yīng)用 uuid-snowflake-client:8121:-1696405068 抽到的 key 8 value 為 UuidDataItemModel{workId=1, dataCenterId=8}
2019-06-10 19:25:56.215 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 2. 將抽到的數(shù)據(jù)到放已經(jīng)使用的池子里
2019-06-10 19:25:56.228 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 3. 查詢已使用池子數(shù)據(jù) {8=UuidDataItemModel{workId=1, dataCenterId=8}}
2019-06-10 19:25:56.234 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 4. 查詢應(yīng)用和數(shù)據(jù)種子關(guān)聯(lián)的池子 {uuid-snowflake-client:8121:-1696405068=8}
2019-06-10 19:25:56.234 INFO 39078 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : ============== 設(shè)置完成 workId=1, dataCenterId=8
第二個應(yīng)用啟動
2019-06-10 19:27:59.992 INFO 39088 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library
2019-06-10 19:27:59.993 INFO 39088 --- [ main] io.lettuce.core.KqueueProvider : Starting without optional kqueue library
2019-06-10 19:28:05.117 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 未使用DataPoolMap 9
2019-06-10 19:28:05.129 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 1. 應(yīng)用 uuid-snowflake-client:8123:-70110274 抽到的 key 1 value 為 UuidDataItemModel{workId=1, dataCenterId=1}
2019-06-10 19:28:05.129 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 2. 將抽到的數(shù)據(jù)到放已經(jīng)使用的池子里
2019-06-10 19:28:05.138 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 3. 查詢已使用池子數(shù)據(jù) {1=UuidDataItemModel{workId=1, dataCenterId=1}, 8=UuidDataItemModel{workId=1, dataCenterId=8}}
2019-06-10 19:28:05.147 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : 4. 查詢應(yīng)用和數(shù)據(jù)種子關(guān)聯(lián)的池子 {uuid-snowflake-client:8121:-1696405068=8, uuid-snowflake-client:8123:-70110274=1}
2019-06-10 19:28:05.147 INFO 39088 --- [ main] c.l.u.s.s.u.Runner.SnowflakeRunner : ============== 設(shè)置完成 workId=1, dataCenterId=1
訪問 http://localhost:8121/generator/id 和 http://localhost:8123/generator/id
生成ID服務(wù)的端口:8121, workId: 1, dateCenterId: 8
生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 1
運(yùn)行-集群模式-docker swarm
在現(xiàn)有的docker swarm環(huán)境中部署摸恍,這里不講如何打包成docker image
docker swarm 的編排配置
https://github.com/liangxiaobo/uuid-generate/blob/cluster-v1.0/docker-swarm-compose.yml測試結(jié)果
[root@manager uuid-generate]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
gjjf0oaou5zg id-app_uuid-eureka-server replicated 1/1 172.16.10.192:5000/bobo/uuid-eureka-server:latest *:8761->8761/tcp
wmwusdo737pm id-app_uuid-server replicated 1/1 172.16.10.192:5000/bobo/uuid-server:latest *:8082->8082/tcp
y97ceng663iz id-app_uuid-snowflake-client replicated 2/2 172.16.10.192:5000/bobo/uuid-snowflake-client:latest *:8123->8123/tcp
部署完成后可訪問,你集群IP:端口赤屋,http://ip:8123/generator/id 多刷新幾次
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 2019-06-12 17:16:50.522 INFO 1 --- [nio-8123-exec-2] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 2019-06-12 17:16:50.559 INFO 1 --- [nio-8123-exec-2] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 3
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 單例模式 ===== 進(jìn)入
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 2019-06-12 17:19:16.838 INFO 1 --- [nio-8123-exec-4] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 3
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 2019-06-12 17:19:17.056 INFO 1 --- [nio-8123-exec-6] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 3
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2 | 2019-06-12 17:19:17.238 INFO 1 --- [nio-8123-exec-8] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 3
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:21:23.632 INFO 1 --- [nio-8123-exec-7] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:21:23.632 INFO 1 --- [nio-8123-exec-7] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:21:23.640 INFO 1 --- [nio-8123-exec-7] o.s.web.servlet.DispatcherServlet : Completed initialization in 8 ms
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:21:23.673 INFO 1 --- [nio-8123-exec-7] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 9
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 單例模式 ===== 進(jìn)入
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:22:35.978 INFO 1 --- [nio-8123-exec-6] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 9
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:22:36.057 INFO 1 --- [nio-8123-exec-8] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 9
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:22:36.143 INFO 1 --- [io-8123-exec-10] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 9
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager | 2019-06-12 17:22:36.225 INFO 1 --- [nio-8123-exec-2] c.l.u.s.s.u.c.SnowflakeController : 生成ID服務(wù)的端口:8123, workId: 1, dateCenterId: 9
運(yùn)行-集群模式 k8s 部署
需要注意
1. 本實例在k8s中 uuid-eureka-server和uuid-server 只部署一個實例
2. 使用的docker私有庫误墓,請參考[k8s 從私有倉庫拉取鏡像](http://www.reibang.com/p/3f24bbee72ad)
重新打包上傳docker image
cd uuid-eureka-server/
mvn package docker:build -Dmaven.test.skip=true
cd uuid-server/
mvn package docker:build -Dmaven.test.skip=true
cd uuid-snowflake-client/
mvn package docker:build -Dmaven.test.skip=true
重新 docker push image
同上
執(zhí)行
- 先發(fā)布Eureka-Server, 執(zhí)行根目錄的 eureka-k8s.yaml
kubectl create -f eureka-k8s.yaml
- 執(zhí)行發(fā)布 uuid-server和uuid-snowflake-client 執(zhí)行根目錄的 uuid-k8s.yaml
kubectl create -f uuid-k8s.yaml
結(jié)果
查看pod
[root@master work]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-65f88748fd-s9727 1/1 Running 1 27h 10.244.1.4 node1 <none> <none>
uuid-eureka-server-0 1/1 Running 0 52m 10.244.1.37 node1 <none> <none>
uuid-server-68c4b867c-7qt8p 1/1 Running 0 50m 10.244.1.38 node1 <none> <none>
uuid-snowflake-client-866764b58-ltdc6 1/1 Running 0 50m 10.244.1.39 node1 <none> <none>
uuid-snowflake-client-866764b58-nh6gl 1/1 Running 0 50m 10.244.2.25 node2 <none> <none>
查看service
[root@master work]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 29h
nginx NodePort 10.1.193.136 <none> 80:30057/TCP 27h
uuid-eureka-server NodePort 10.1.8.102 <none> 8761:32297/TCP 56m
uuid-snowflake-client NodePort 10.1.103.35 <none> 8123:31968/TCP 54m
其中訪問 eureka-server地址 http://IP:32297 訪問id生成服務(wù) http://IP:31968/generator/id
- uuid-server的日志
INFO 1 --- [pool-7-thread-1] c.l.u.s.u.UuidServerApplication : 應(yīng)用關(guān)聯(lián)的種子 2
INFO 1 --- [pool-7-thread-1] c.l.u.s.u.UuidServerApplication : 服務(wù)應(yīng)用注冊數(shù)量: 2
應(yīng)用關(guān)聯(lián)的種子 和 服務(wù)應(yīng)用注冊數(shù)量 相等時,表示正常益缎,否則表示有異常
注意
uuid-server項目主要工作是初始化 workid和dataCenterId到redis中谜慌,并監(jiān)控redis中的關(guān)聯(lián)池與實際Eureka-server中注冊服務(wù)的變化,
監(jiān)控的任務(wù)task-execution-interval: 30
默認(rèn)30秒執(zhí)行一次莺奔,由于在生產(chǎn)環(huán)境中欣范,uuid-snowflake-client注冊的數(shù)量不確定,所以需要設(shè)置足夠大的時長令哟,10分鐘以上恼琼,
30秒為測試環(huán)境。