分布式id生成器
这破玩意儿能叫分布式?
最近老项目升级到cloud,用到了 snowflake,苦于手动配置workerId,撸了个没用的轮子---基于配置中心的雪花ID
分布式 ID 之 SNOWFLAKE
说起分布式id,首推 SNOWFLAKE(雪花算法),它具有以下优势:
- 高效:性能好
- 分布式唯一:与DB无关,适应分布式应用
- 趋势递增:与时间相关,保持一定的时序性,但又不是绝对递增,可防止id泄漏业务增长信息。
缺点大概就是:
- 与时间相关,时间错乱可能会出现重复id,即时钟回拨问题。
- 多应用服务之间,可能会产生id碰撞,这个可以通过设置workerId来解决。
实际使用中,时钟回拨概率很低,比较麻烦的通常是多服务的workerId配置问题:
- 每一个应用的workerId都要不相同从而避免ID碰撞
- 多个应用之间workerId差异化配置难以集中管理
- 对于多实例的应用,可能除了workerId,其他配置都是相同的,但不得不想办法进行差异化配置、打包、发布。
何不将每一个实例使用的workerId记录到一个地方?既方便管理,又能避免重复。
注册中心正好可以解决这个问题。
快速实现
这小玩意儿咱设计简陋点,就包含三个主要功能:
- 注册id
- 注销id
- 自动加载
注册id
这里主要考虑两点:
- 如何保证服务之间区分
- 如何确保服务注册的id是唯一
- 如何保证服务之间区分
要能区分服务,即是要有唯一的服务标识,
我选取的标识是:数据中心+ip+应用标识
当然我也是可以用随机生成UUID就能保证唯一的服务标识,但使用上面这种有两个好处:
- 幂等性:每次启动生成的标识是不变的
- 可读性:能从配置中心直接看到每个服务的配置状况。
- 如何确保服务注册的id是唯一
要保证注册id唯一,只需要在注册前检查所有注册值,如果已注册就换一个就行了。
即:
但这还不够,假如两个应用贼有缘,同时启动,随到同一个id,同时检查 id 是否已使用并得到否定结果,然后两人就注册了同一个id!这就是一个典型的多线程一票多卖的问题,看来还是要用锁才行啊。
经过一番修改,它看起来是这样:
用分布式锁锁住id,将检查、注册这部分功能变成串行。
注销id
利用 @PreDestroy 注释一个destroy方法(名字不限哈) ,在服务中断(非 kill -9)时,调用该方法注销配置。
关于 @PreDestroy 原理 和 Spring Boot 应用中断,这个有空也得自说自话一下。
自动装载
配置越少、越简单,才能用的方便不是,不然要被同事吐槽啦(👴:虽然这功能挺鸡肋,但它配置丰富啊🐶)
总结了一下,得实现以下4个自动化:
- yaml配置得有提示。
- 能根据不同类型配置中心配置自动装载配置中心服务。
- 无需额外的@ComponentScan、@Import 或java配置,自动装载。
- yaml配置得有提示。
使用 spring-boot-configuration-processor 生成 spring-configuration-metadata.json 文件
- 能根据不同类型配置中心配置自动装载配置中心服务。
使用@ConditionalOnProperty、@ConditionalOnClass 即可做到
- 无需额外的@ComponentScan、@Import 或java配置,自动装载。
创建 spring.factories 并打包即可
关于 Spring 自动装载 和 相关注解,emmm.. 下次再说道说道好了。
缺陷
- 不能响应强制中断,从而造成id抢占,如果此时该应用永久下线或者网络配置发生变化,就造成了id泄漏,产生无效的配置数据。
- 如果注册中心不可用时,将使用兜底的随机id方式,会有id碰撞风险。
- 不能即时修改生效。