话不多说,直接上代码

代码

POM

  1. <!-- Web -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <!-- MySQL驱动 -->
  7. <dependency>
  8. <groupId>com.mysql</groupId>
  9. <artifactId>mysql-connector-j</artifactId>
  10. <scope>runtime</scope>
  11. </dependency>
  12. <!-- MybatisPlus -->
  13. <dependency>
  14. <groupId>com.baomidou</groupId>
  15. <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
  16. <version>3.5.9</version>
  17. </dependency>
  18. <!-- Redisson -->
  19. <dependency>
  20. <groupId>org.redisson</groupId>
  21. <artifactId>redisson-spring-boot-starter</artifactId>
  22. <version>3.39.0</version>
  23. </dependency>

自定义ID生成器

  1. import java.util.concurrent.TimeUnit;
  2. import org.redisson.api.RAtomicLong;
  3. import org.redisson.api.RLock;
  4. import org.redisson.api.RedissonClient;
  5. import org.springframework.data.redis.core.StringRedisTemplate;
  6. import org.springframework.scheduling.annotation.EnableScheduling;
  7. import org.springframework.scheduling.annotation.Scheduled;
  8. import org.springframework.stereotype.Component;
  9. import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
  10. import com.baomidou.mybatisplus.core.toolkit.Sequence;
  11. import jakarta.annotation.Resource;
  12. import lombok.extern.slf4j.Slf4j;
  13. @Slf4j
  14. @EnableScheduling
  15. @Component
  16. public class SnowflakeIdGenerator implements IdentifierGenerator {
  17. @Resource
  18. private RedissonClient redissonClient;
  19. @Resource
  20. private StringRedisTemplate stringRedisTemplate;
  21. @Override
  22. public Number nextId(Object entity) {
  23. return getSequence().nextId();
  24. }
  25. private volatile Sequence sequence;
  26. private Long id;
  27. public Sequence getSequence() {
  28. if (sequence == null) {
  29. synchronized (SnowflakeIdGenerator.class) {
  30. if (sequence == null) {
  31. RLock lock = redissonClient.getLock("snowflake:lock");
  32. try {
  33. if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {
  34. RAtomicLong atomicLong = redissonClient.getAtomicLong("snowflake:id");
  35. // 最终获取的序号ID
  36. long id;
  37. // 累计获取的次数
  38. int i = 0;
  39. do {
  40. i++;
  41. // 如果累计获取超过1024次,则说明没有可以使用的ID了,抛出异常
  42. if (i > 1024) {
  43. log.error("获取不到可以使用的雪花ID序号");
  44. throw new RuntimeException("获取不到可以使用的雪花ID序号");
  45. }
  46. // 获取ID
  47. id = atomicLong.incrementAndGet();
  48. if (id >= 1024) {
  49. id = 0;
  50. atomicLong.set(0);
  51. }
  52. // 检查是否已被占用
  53. String watchDogKey = "snowflake:id:" + id;
  54. if (stringRedisTemplate.hasKey(watchDogKey)) {
  55. log.warn("当前雪花ID序号:{}已被使用", id);
  56. } else {
  57. // 未使用,设置占用标记
  58. stringRedisTemplate.opsForValue().set(watchDogKey, "", 65, TimeUnit.SECONDS);
  59. this.id = id;
  60. break;
  61. }
  62. } while (true);
  63. long workerId = id / 32;
  64. long datacenterId = id % 32;
  65. log.info("当前雪花ID序号为:{},对应的workerId为:{},对应的datacenterId为:{}", id, workerId, datacenterId);
  66. sequence = new Sequence(workerId, datacenterId);
  67. }
  68. } catch (InterruptedException e) {
  69. throw new RuntimeException(e);
  70. } finally {
  71. if (lock.isLocked() && lock.isHeldByCurrentThread()) {
  72. lock.unlock();
  73. log.debug("lock released");
  74. }
  75. }
  76. }
  77. }
  78. }
  79. return sequence;
  80. }
  81. @Scheduled(fixedDelay = 20000)
  82. public void scheduledTask() {
  83. if (id == null) {
  84. log.debug("雪花ID序号未初始化,无需执行");
  85. return;
  86. }
  87. // 更新占用
  88. stringRedisTemplate.opsForValue().set("snowflake:id:" + id, "", 65, TimeUnit.SECONDS);
  89. }
  90. }

测试

实体

  1. import java.io.Serializable;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableId;
  4. import com.baomidou.mybatisplus.annotation.TableName;
  5. import com.baomidou.mybatisplus.extension.activerecord.Model;
  6. import lombok.Getter;
  7. import lombok.NoArgsConstructor;
  8. import lombok.Setter;
  9. import lombok.ToString;
  10. import lombok.experimental.Accessors;
  11. /**
  12. * 示例
  13. *
  14. * @author Max_Qiu
  15. */
  16. @Getter
  17. @Setter
  18. @NoArgsConstructor
  19. @ToString
  20. @Accessors(chain = true)
  21. @TableName("demo")
  22. public class Demo extends Model<Demo> {
  23. /**
  24. * 主键ID
  25. */
  26. @TableId(value = "id", type = IdType.ASSIGN_ID)
  27. private Long id;
  28. @Override
  29. public Serializable pkVal() {
  30. return this.id;
  31. }
  32. }

测试类

  1. import java.util.concurrent.ExecutorService;
  2. import java.util.concurrent.Executors;
  3. import java.util.concurrent.TimeUnit;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. import com.maxqiu.demo.entity.Demo;
  7. /**
  8. * @author Max_Qiu
  9. */
  10. @SpringBootTest
  11. class DemoServiceTest {
  12. @Test
  13. void insert() throws InterruptedException {
  14. ExecutorService executorService = Executors.newCachedThreadPool();
  15. for (int i = 0; i < 3; i++) {
  16. executorService.execute(() -> {
  17. Demo demo = new Demo();
  18. demo.insert();
  19. System.out.println(demo.getId());
  20. });
  21. }
  22. executorService.shutdown();
  23. executorService.awaitTermination(5, TimeUnit.SECONDS);
  24. }
  25. }

输出

  1. 2024-12-15T18:58:00.882+08:00 INFO 15392 --- [pool-2-thread-3] c.m.demo.config.SnowflakeIdGenerator : 当前雪花ID序号为:26,对应的workerId为:0,对应的datacenterId为:26
  2. 2024-12-15T18:58:00.902+08:00 INFO 15392 --- [pool-2-thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
  3. 2024-12-15T18:58:01.074+08:00 INFO 15392 --- [pool-2-thread-3] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@9706daa
  4. 2024-12-15T18:58:01.076+08:00 INFO 15392 --- [pool-2-thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
  5. 1868249143658676227
  6. 1868249143658676228
  7. 1868249143658676226
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。