IdGeneratorConfig.java 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package jnpf.database.config;
  2. import com.github.yitter.contract.IdGeneratorOptions;
  3. import com.github.yitter.idgen.YitIdHelper;
  4. import jakarta.annotation.PreDestroy;
  5. import jnpf.util.CacheKeyUtil;
  6. import jnpf.util.ThreadPoolExecutorUtil;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.springframework.beans.factory.InitializingBean;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.support.atomic.RedisAtomicLong;
  11. import org.springframework.stereotype.Component;
  12. import java.util.Random;
  13. import java.util.concurrent.ScheduledThreadPoolExecutor;
  14. import java.util.concurrent.TimeUnit;
  15. @Slf4j
  16. @Component
  17. public class IdGeneratorConfig implements InitializingBean {
  18. private static final String ID_IDX = CacheKeyUtil.IDGENERATOR + "Index:";
  19. private RedisTemplate<String, Long> redisTemplate;
  20. //ID缓存有效时间 定时刷新有效期
  21. private static final long CacheTimeout = 60L * 60 * 24;
  22. //30分钟续期一次 如果Redis被清空可以早点续期
  23. private static final long ScheduleTimeout = 60L * 30;
  24. private static final byte WorkerIdBitLength = 16;
  25. //65535 参数为shot 最大值为Short.MAX_VALUE
  26. private static final int MaxWorkerIdNumberByMode = (1 << WorkerIdBitLength) -1 >Short.MAX_VALUE?Short.MAX_VALUE : (1 << WorkerIdBitLength) -1 ;
  27. private static ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
  28. private short workerId = -1;
  29. private String cacheKey;
  30. public IdGeneratorConfig(RedisTemplate redisTemplate) {
  31. this.redisTemplate = redisTemplate;
  32. }
  33. /**
  34. * 初始化雪花生成器WorkerID, 通过Redis实现集群获取不同的编号, 如果相同会出现ID重复
  35. */
  36. private void initIdWorker(){
  37. if(redisTemplate != null) {
  38. RedisAtomicLong redisAtomicLong = new RedisAtomicLong(ID_IDX, redisTemplate.getConnectionFactory());
  39. for (int i = 0; i <= MaxWorkerIdNumberByMode; i++) {
  40. long andInc = redisAtomicLong.getAndIncrement();
  41. long result = andInc % (MaxWorkerIdNumberByMode + 1);
  42. //计数超出上限之后重新计数
  43. if (andInc >= MaxWorkerIdNumberByMode) {
  44. redisAtomicLong.set(andInc % (MaxWorkerIdNumberByMode));
  45. }
  46. cacheKey = ID_IDX + result;
  47. boolean useSuccess = redisTemplate.opsForValue().setIfAbsent(cacheKey, System.currentTimeMillis(), CacheTimeout, TimeUnit.SECONDS);
  48. if (useSuccess) {
  49. workerId = (short) result;
  50. break;
  51. }
  52. }
  53. if (workerId == -1) {
  54. throw new RuntimeException(String.format("已尝试生成%d个ID生成器编号, 无法获取到可用编号", MaxWorkerIdNumberByMode + 1));
  55. }
  56. }else{
  57. workerId = (short) new Random().nextInt(MaxWorkerIdNumberByMode);
  58. }
  59. log.info("当前ID生成器编号: " + workerId);
  60. IdGeneratorOptions options = new IdGeneratorOptions(workerId);
  61. options.WorkerIdBitLength = WorkerIdBitLength;
  62. YitIdHelper.setIdGenerator(options);
  63. scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1, ThreadPoolExecutorUtil.getExecutor().getThreadPoolExecutor().getThreadFactory());
  64. //提前一分钟续期
  65. scheduledThreadPoolExecutor.scheduleWithFixedDelay(resetExpire, ScheduleTimeout, ScheduleTimeout, TimeUnit.SECONDS);
  66. }
  67. private Runnable resetExpire = ()->{
  68. //重新设值, 如果Redis被意外清空或者掉线可以把当前编号重新锁定
  69. redisTemplate.opsForValue().set(cacheKey, System.currentTimeMillis(), CacheTimeout, TimeUnit.SECONDS);
  70. };
  71. @PreDestroy
  72. private void onDestroy(){
  73. //正常关闭时删除当前生成器编号
  74. if(redisTemplate != null) {
  75. redisTemplate.delete(cacheKey);
  76. }
  77. }
  78. @Override
  79. public void afterPropertiesSet() {
  80. initIdWorker();
  81. }
  82. }