基于Twitter的snowflake分布式ID生成,改成可集群应用模式

ID生成方案,基于twitter的snowflake分布式ID生成改造,在集群环境中多实例自动分配snowflake的workId,dataCenterId;如果是小项目小业务不需要考虑,直接基本snowflake在配置文件中配置即可,本项目将会在docker swarm 和 k8s中部署测试。

源码

  1. coding
# 单机多实例不同端口
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
  1. github
# 单机多实例不同端口
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

项目思路

1.jpeg
  • 以Eureka为注册中心
  • Redis 存储
  • ID-Server 配置种子数据的初始化并处理过期的配置种子
  • ID-Client ID生成服务

定义简称

  • Redis中自定义的三个存储简称 R1(未全用池)、R2(已使用池)、R3(id-client实例和R2中的数据关联池)
  • workId和dataCenterId简称WD

大概思路是:

  1. 由一个应用ID-Server主动创建足够量的workId和DataCenterId,放到Redis中的R1中
  2. ID-Client是ID生成服务,需要配置workId和dataCenterId,所以启动时第一步从Redis的R1中读取可用的worId和dataCenterId,
    第二步把拿到的可用WD放到放到R2中,第三步获取当前Eureka-client客户端的实例ID和刚才放入R2的数据关联放R3中,在此同时ID-Client也向Eureka-Server注册
  3. ID-Server会获取Eureka-Server中ID-Client的注册实例列表,定时判断,R3中的列表数据和Eureka-Server中的实例列表的差异,如果有差异表示有机器实例Down了,ID-Server主动收回R2中的数据给R1,并删除R3中无效应用实例的数据

ID-Server

当前设计ID-Server只能跑一个实例;

  1. 当ID-Server第一次运行,这时候Redis中的R1是空的,需要初始化配置数据(workId,dataCenterId 后面简称WD),配置文件中自定义参数
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生成器的应用名称
  client-application-name: uuid-snowflake-client
  # 机器数量,默认2台,最大31*31=961台
  machine-count: 10
  # 同步检测已使用池中的数据和eureka注册表中的实例数30秒,生产环境设置10分钟以上
  task-execution-interval: 30

R1 中存的是一个Map数据格式[{1:{workId:1,dataCenterId:1}}, {2:{workId:1,dataCenterId:2}}, ...]
R2 中存的数据格式Map [{1:{workId:1,dataCenterId:1}}, {2:{workId:1,dataCenterId:2}}, ...]
R3 中的数据格式 Map [{"实例1": 1}, {"实例2": 2}]

  1. 如果是重启ID-Server会主动去读取Redis中R1池中未被使用的WD
  2. 监控Redis中R3的数据列表,与Eureka-Server中的ID-Client实例列表相对比差异

ID-Client

  1. 启动时从Redis中的R1中取一个WD放到R2中,并和取出来的WD的KEY 关联后放到R3中记录
    配置文件中需要注意的是:
......

eureka:
  instance:
    leaseRenewalIntervalInSeconds: 10
    # 如果是集群模式部署,instance-id值必须设置
    instance-id: ${spring.application.name}:${server.port}:${random.int}
......
  1. snowflake以单例模式运行

运行-单机

注意:运行顺序 Eureka-Server => ID-Server => ID-Client
单机多实例不同端口测试,跑两个实例端口分别是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. 应用 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. 将抽到的数据到放已经使用的池子里
2019-06-10 19:25:56.228  INFO 39078 --- [           main] c.l.u.s.s.u.Runner.SnowflakeRunner       : 3. 查询已使用池子数据 {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. 查询应用和数据种子关联的池子 {uuid-snowflake-client:8121:-1696405068=8}
2019-06-10 19:25:56.234  INFO 39078 --- [           main] c.l.u.s.s.u.Runner.SnowflakeRunner       : ============== 设置完成 workId=1, dataCenterId=8

第二个应用启动

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. 应用 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. 将抽到的数据到放已经使用的池子里
2019-06-10 19:28:05.138  INFO 39088 --- [           main] c.l.u.s.s.u.Runner.SnowflakeRunner       : 3. 查询已使用池子数据 {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. 查询应用和数据种子关联的池子 {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       : ============== 设置完成 workId=1, dataCenterId=1

访问 http://localhost:8121/generator/idhttp://localhost:8123/generator/id

生成ID服务的端口:8121, workId: 1, dateCenterId: 8
生成ID服务的端口:8123, workId: 1, dateCenterId: 1

运行-集群模式-docker swarm

在现有的docker swarm环境中部署,这里不讲如何打包成docker image

  1. docker swarm 的编排配置
    https://github.com/liangxiaobo/uuid-generate/blob/cluster-v1.0/docker-swarm-compose.yml

  2. 测试结果

[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服务的端口:8123, workId: 1, dateCenterId: 3
id-app_uuid-snowflake-client.2.0tkgecz302jw@work2    | 单例模式 ===== 进入
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服务的端口: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服务的端口: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服务的端口: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服务的端口:8123, workId: 1, dateCenterId: 9
id-app_uuid-snowflake-client.1.gxkc7k0xuyph@manager    | 单例模式 ===== 进入
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服务的端口: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服务的端口: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服务的端口: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服务的端口:8123, workId: 1, dateCenterId: 9

运行-集群模式 k8s 部署

需要注意

1. 本实例在k8s中 uuid-eureka-server和uuid-server 只部署一个实例
2. 使用的docker私有库,请参考[k8s 从私有仓库拉取镜像](https://www.jianshu.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

同上

执行

  1. 先发布Eureka-Server, 执行根目录的 eureka-k8s.yaml
kubectl create -f eureka-k8s.yaml
  1. 执行发布 uuid-server和uuid-snowflake-client 执行根目录的 uuid-k8s.yaml
kubectl create -f uuid-k8s.yaml 

结果

查看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生成服务 http://IP:31968/generator/id

  • uuid-server的日志
INFO 1 --- [pool-7-thread-1] c.l.u.s.u.UuidServerApplication          : 应用关联的种子 2
INFO 1 --- [pool-7-thread-1] c.l.u.s.u.UuidServerApplication          : 服务应用注册数量: 2

应用关联的种子 和 服务应用注册数量 相等时,表示正常,否则表示有异常

注意

uuid-server项目主要工作是初始化 workid和dataCenterId到redis中,并监控redis中的关联池与实际Eureka-server中注册服务的变化,
监控的任务 task-execution-interval: 30默认30秒执行一次,由于在生产环境中,uuid-snowflake-client注册的数量不确定,所以需要设置足够大的时长,10分钟以上,
30秒为测试环境。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271