这破玩意儿能叫分布式?

最近老项目升级到cloud,用到了 snowflake,苦于手动配置workerId,撸了个没用的轮子---基于配置中心的雪花ID

分布式 ID 之 SNOWFLAKE

说起分布式id,首推 SNOWFLAKE(雪花算法),它具有以下优势:

  • 高效:性能好
  • 分布式唯一:与DB无关,适应分布式应用
  • 趋势递增:与时间相关,保持一定的时序性,但又不是绝对递增,可防止id泄漏业务增长信息。

缺点大概就是:

  1. 与时间相关,时间错乱可能会出现重复id,即时钟回拨问题。
  2. 多应用服务之间,可能会产生id碰撞,这个可以通过设置workerId来解决。

实际使用中,时钟回拨概率很低,比较麻烦的通常是多服务的workerId配置问题:

  1. 每一个应用的workerId都要不相同从而避免ID碰撞
  2. 多个应用之间workerId差异化配置难以集中管理
  3. 对于多实例的应用,可能除了workerId,其他配置都是相同的,但不得不想办法进行差异化配置、打包、发布。

何不将每一个实例使用的workerId记录到一个地方?既方便管理,又能避免重复。

注册中心正好可以解决这个问题。

快速实现

这小玩意儿咱设计简陋点,就包含三个主要功能:

  • 注册id
  • 注销id
  • 自动加载

注册id

这里主要考虑两点:

  1. 如何保证服务之间区分
  2. 如何确保服务注册的id是唯一
  1. 如何保证服务之间区分

要能区分服务,即是要有唯一的服务标识,
我选取的标识是:数据中心+ip+应用标识
当然我也是可以用随机生成UUID就能保证唯一的服务标识,但使用上面这种有两个好处:

  1. 幂等性:每次启动生成的标识是不变的
  2. 可读性:能从配置中心直接看到每个服务的配置状况。
  1. 如何确保服务注册的id是唯一

要保证注册id唯一,只需要在注册前检查所有注册值,如果已注册就换一个就行了。
即:

但这还不够,假如两个应用贼有缘,同时启动,随到同一个id,同时检查 id 是否已使用并得到否定结果,然后两人就注册了同一个id!这就是一个典型的多线程一票多卖的问题,看来还是要用锁才行啊。

经过一番修改,它看起来是这样:

用分布式锁锁住id,将检查、注册这部分功能变成串行。

注销id

利用 @PreDestroy 注释一个destroy方法(名字不限哈) ,在服务中断(非 kill -9)时,调用该方法注销配置。

关于 @PreDestroy 原理 和 Spring Boot 应用中断,这个有空也得自说自话一下。

自动装载

配置越少、越简单,才能用的方便不是,不然要被同事吐槽啦(👴:虽然这功能挺鸡肋,但它配置丰富啊🐶)

总结了一下,得实现以下4个自动化:

  1. yaml配置得有提示。
  2. 能根据不同类型配置中心配置自动装载配置中心服务。
  3. 无需额外的@ComponentScan、@Import 或java配置,自动装载。
  1. yaml配置得有提示。

使用 spring-boot-configuration-processor 生成 spring-configuration-metadata.json 文件

  1. 能根据不同类型配置中心配置自动装载配置中心服务。

使用@ConditionalOnProperty、@ConditionalOnClass 即可做到

  1. 无需额外的@ComponentScan、@Import 或java配置,自动装载。

创建 spring.factories 并打包即可

关于 Spring 自动装载 和 相关注解,emmm.. 下次再说道说道好了。

缺陷

  1. 不能响应强制中断,从而造成id抢占,如果此时该应用永久下线或者网络配置发生变化,就造成了id泄漏,产生无效的配置数据。
  2. 如果注册中心不可用时,将使用兜底的随机id方式,会有id碰撞风险。
  3. 不能即时修改生效。

项目地址

id-generators