当前位置: 首页 > news >正文

ShardingSphere-jdbc 5.5.0 + spring boot 基础配置 - 实战篇

ShardingSphere-jdbc 5.5.0 + spring boot 基础配置
  • 环境准备
    • 版本
    • 数据库说明
      • 集群
  • 配置
    • 配置文件
    • Maven依赖
    • spring boot配置
    • shardingsphere-jdbc配置
      • 自定义配置1:SM4加解密存储数据
      • 完整的基础配置
  • 其他
    • 雪花算法自定义worker.id

环境准备

版本

spring boot 2.7.17
shardingsphere-jdbc 5.5.0
druid 1.2.23

数据库说明

本示例数据库为单机多库schema的架构,以一主一从作为集群演示,转为一主多从数据库集群时,可自定义修改配置。

集群

一主一从:
逻辑主库:ds_basic、ds0000、ds0001(ds_basic为数据简单、量少的元数据库)
逻辑从库:ds0000_slave、ds0001_slave

配置

配置文件

Maven依赖

<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc</artifactId> <version>5.5.0</version> <exclusions> <exclusion> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-test-util</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>2.2</version> </dependency>

spring boot配置

application.yml

spring: application: name: demo main: allow-bean-definition-overriding: true datasource: driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver url: jdbc:shardingsphere:classpath:sharding.yaml

shardingsphere-jdbc配置

自定义配置1:SM4加解密存储数据

shardingsphere 5.5.0移除了sm4算法,两种方式解决:
1、使用官方shardingpshere plugin,找到相关组件依赖引用即可,
2、自己添加sm4算法代码SPI实现。
本文使用方式2(注意:ShardingSphere-jdbc 5.5.1版本加密算法接口升级了,以下sm4的SPI代码不适用,后续推出适配版本)。

  1. 增加spi扩展

    org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm增加以下代码:

    com.demo.core.encrypt.SM4EncryptAlgorithm
  2. SM4算法代码

    package com.demo.core.encrypt;

    import lombok.EqualsAndHashCode;
    import lombok.Getter;
    import lombok.SneakyThrows;
    import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
    import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithmMetaData;
    import org.apache.shardingsphere.infra.algorithm.core.context.AlgorithmSQLContext;
    import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
    import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.GeneralSecurityException;
    import java.security.Security;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Optional;
    import java.util.Properties;
    import java.util.Set;

    /**

    • shardingsphere SM4 encrypt algorithm.

    • @author Robin Wang
      */
      @EqualsAndHashCode
      public final class SM4EncryptAlgorithm implements EncryptAlgorithm {

      static {
      Security.addProvider(new BouncyCastleProvider());
      }

      @Getter
      private final EncryptAlgorithmMetaData metaData = new EncryptAlgorithmMetaData(true, true, false);

      private static final String SM4_KEY = “sm4-key”;

      private static final String SM4_IV = “sm4-iv”;

      private static final String SM4_MODE = “sm4-mode”;

      private static final String SM4_PADDING = “sm4-padding”;

      private static final int KEY_LENGTH = 16;

      private static final int IV_LENGTH = 16;

      private static final Set MODES = new HashSet<>(Arrays.asList(“ECB”, “CBC”));

      private static final Set PADDINGS = new HashSet<>(Arrays.asList(“PKCS5Padding”, “PKCS7Padding”));

      private byte[] sm4Key;

      private byte[] sm4Iv;

      private String sm4ModePadding;

      @Override
      public void init(final Properties props) {
      String sm4Mode = createSm4Mode(props);
      String sm4Padding = createSm4Padding(props);
      sm4ModePadding = “SM4/” + sm4Mode + “/” + sm4Padding;
      sm4Key = createSm4Key(props);
      sm4Iv = createSm4Iv(props, sm4Mode);
      }

      private String createSm4Mode(final Properties props) {
      ShardingSpherePreconditions.checkState(props.containsKey(SM4_MODE), () -> new AlgorithmInitializationException(this, “%s can not be null or empty”, SM4_MODE));
      String result = String.valueOf(props.getProperty(SM4_MODE)).toUpperCase();
      ShardingSpherePreconditions.checkState(MODES.contains(result), () -> new AlgorithmInitializationException(this, “Mode must be either CBC or ECB”));
      return result;
      }

      private byte[] createSm4Key(final Properties props) {
      ShardingSpherePreconditions.checkState(props.containsKey(SM4_KEY), () -> new AlgorithmInitializationException(this, “%s can not be null”, SM4_KEY));
      byte[] result = ByteUtils.fromHexString(String.valueOf(props.getProperty(SM4_KEY)));
      ShardingSpherePreconditions.checkState(KEY_LENGTH == result.length,
      () -> new AlgorithmInitializationException(this, “Key length must be " + KEY_LENGTH + " bytes long”));
      return result;
      }

      private byte[] createSm4Iv(final Properties props, final String sm4Mode) {
      if (!“CBC”.equalsIgnoreCase(sm4Mode)) {
      return null;
      }
      ShardingSpherePreconditions.checkState(props.containsKey(SM4_IV), () -> new AlgorithmInitializationException(this, “%s can not be null”, SM4_IV));
      String sm4IvValue = String.valueOf(props.getProperty(SM4_IV));
      byte[] result = ByteUtils.fromHexString(sm4IvValue);
      ShardingSpherePreconditions.checkState(IV_LENGTH == result.length, () -> new AlgorithmInitializationException(this, “Iv length must be " + IV_LENGTH + " bytes long”));
      return result;
      }

      private String createSm4Padding(final Properties props) {
      ShardingSpherePreconditions.checkState(props.containsKey(SM4_PADDING), () -> new AlgorithmInitializationException(this, “%s can not be null”, SM4_PADDING));
      String result = String.valueOf(props.getProperty(SM4_PADDING)).toUpperCase().replace(“PADDING”, “Padding”);
      ShardingSpherePreconditions.checkState(PADDINGS.contains(result), () -> new AlgorithmInitializationException(this, “Padding must be either PKCS5Padding or PKCS7Padding”));
      return result;
      }

      @Override
      public String encrypt(Object plainValue, AlgorithmSQLContext algorithmSQLContext) {
      return null == plainValue ? null : ByteUtils.toHexString(encrypt(String.valueOf(plainValue).getBytes(StandardCharsets.UTF_8)));
      }

      private byte[] encrypt(final byte[] plainValue) {
      return handle(plainValue, Cipher.ENCRYPT_MODE);
      }

      @Override
      public Object decrypt(Object cipherValue, AlgorithmSQLContext algorithmSQLContext) {
      return null == cipherValue ? null : new String(decrypt(ByteUtils.fromHexString((String) cipherValue)), StandardCharsets.UTF_8);
      }

      private byte[] decrypt(final byte[] cipherValue) {
      return handle(cipherValue, Cipher.DECRYPT_MODE);
      }

      @SneakyThrows(GeneralSecurityException.class)
      private byte[] handle(final byte[] input, final int mode) {
      Cipher cipher = Cipher.getInstance(sm4ModePadding, BouncyCastleProvider.PROVIDER_NAME);
      SecretKeySpec secretKeySpec = new SecretKeySpec(sm4Key, “SM4”);
      Optional<byte[]> sm4Iv = Optional.ofNullable(this.sm4Iv);
      if (sm4Iv.isPresent()) {
      cipher.init(mode, secretKeySpec, new IvParameterSpec(sm4Iv.get()));
      } else {
      cipher.init(mode, secretKeySpec);
      }
      return cipher.doFinal(input);
      }

      @Override
      public String getType() {
      return “SM4”;
      }
      }

  3. sm4算法yaml配置

    encryptors:
    sm4_encryptor:
    type: SM4
    props:
    sm4-key: 86C63180C2806ED1F43A859DE501215C
    sm4-mode: ECB
    sm4-padding: PKCS5Padding

完整的基础配置

sharding.yaml
配置包括:单机模式服务、数据源(加解密)、规则配置:【数据分片、读写分离、数据加密、单表】
待新增补充:混合规则等

mode: type: Standalone repository: type: JDBC databaseName: demo_db dataSources: ds_basic: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/demo_basic?characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false username: root password: Ph9ep971fm14nYaZsLl9LY+MCqX9uJSozYRNgP2VVSj/hbmokn5OC6kpiAA1I0okA9GiDHEo7qHUvRQYYUNZvQ== initialSize: 1 minIdle: 1 maxActive: 64 maxWait: 20000 validationQuery: SELECT 1 FROM DUAL validationQueryTimeout: 30000 minEvictableIdleTimeMillis: 300000 maxEvictableIdleTimeMillis: 600000 timeBetweenEvictionRunsMillis: 300000 testOnBorrow: true testWhileIdle: true filters: config, stat, wall connectProperties: connectTimeout: 5000 socketTimeout: '20000' config.decrypt: 'true' config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZRYgsnvVKPqZTfMOWmmj6OuupFRSk7+Vtqv70cG3y6T3bm+DcQU3zOC993ozbHpmqeODtuLzURhIuXDMyTKW8CAwEAAQ== ds0000: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/demo_0000?characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false username: root password: Ph9ep971fm14nYaZsLl9LY+MCqX9uJSozYRNgP2VVSj/hbmokn5OC6kpiAA1I0okA9GiDHEo7qHUvRQYYUNZvQ== initialSize: 1 minIdle: 1 maxActive: 64 maxWait: 20000 validationQuery: SELECT 1 FROM DUAL validationQueryTimeout: 30000 minEvictableIdleTimeMillis: 300000 maxEvictableIdleTimeMillis: 600000 timeBetweenEvictionRunsMillis: 300000 testOnBorrow: true testWhileIdle: true filters: config, stat, wall connectProperties: connectTimeout: 5000 socketTimeout: '20000' config.decrypt: 'true' config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZRYgsnvVKPqZTfMOWmmj6OuupFRSk7+Vtqv70cG3y6T3bm+DcQU3zOC993ozbHpmqeODtuLzURhIuXDMyTKW8CAwEAAQ== ds0001: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/demo_0001?characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false username: root password: Ph9ep971fm14nYaZsLl9LY+MCqX9uJSozYRNgP2VVSj/hbmokn5OC6kpiAA1I0okA9GiDHEo7qHUvRQYYUNZvQ== initialSize: 1 minIdle: 1 maxActive: 64 maxWait: 20000 validationQuery: SELECT 1 FROM DUAL validationQueryTimeout: 30000 minEvictableIdleTimeMillis: 300000 maxEvictableIdleTimeMillis: 600000 timeBetweenEvictionRunsMillis: 300000 testOnBorrow: true testWhileIdle: true filters: config, stat, wall connectProperties: connectTimeout: 5000 socketTimeout: '20000' config.decrypt: 'true' config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZRYgsnvVKPqZTfMOWmmj6OuupFRSk7+Vtqv70cG3y6T3bm+DcQU3zOC993ozbHpmqeODtuLzURhIuXDMyTKW8CAwEAAQ== ds0000_slave: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.1.88:3306/demo_0000?characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false username: root password: Ph9ep971fm14nYaZsLl9LY+MCqX9uJSozYRNgP2VVSj/hbmokn5OC6kpiAA1I0okA9GiDHEo7qHUvRQYYUNZvQ== initialSize: 1 minIdle: 1 maxActive: 64 maxWait: 20000 validationQuery: SELECT 1 FROM DUAL validationQueryTimeout: 30000 minEvictableIdleTimeMillis: 300000 maxEvictableIdleTimeMillis: 600000 timeBetweenEvictionRunsMillis: 300000 testOnBorrow: true testWhileIdle: true filters: config, stat, wall connectProperties: connectTimeout: 5000 socketTimeout: '20000' config.decrypt: 'true' config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZRYgsnvVKPqZTfMOWmmj6OuupFRSk7+Vtqv70cG3y6T3bm+DcQU3zOC993ozbHpmqeODtuLzURhIuXDMyTKW8CAwEAAQ== ds0001_slave: dataSourceClassName: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.1.88:3306/demo_0001?characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false username: root password: Ph9ep971fm14nYaZsLl9LY+MCqX9uJSozYRNgP2VVSj/hbmokn5OC6kpiAA1I0okA9GiDHEo7qHUvRQYYUNZvQ== initialSize: 1 minIdle: 1 maxActive: 64 maxWait: 20000 validationQuery: SELECT 1 FROM DUAL validationQueryTimeout: 30000 minEvictableIdleTimeMillis: 300000 maxEvictableIdleTimeMillis: 600000 timeBetweenEvictionRunsMillis: 300000 testOnBorrow: true testWhileIdle: true filters: config, stat, wall connectProperties: connectTimeout: 5000 socketTimeout: '20000' config.decrypt: 'true' config.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZRYgsnvVKPqZTfMOWmmj6OuupFRSk7+Vtqv70cG3y6T3bm+DcQU3zOC993ozbHpmqeODtuLzURhIuXDMyTKW8CAwEAAQ== rules: # 数据分片 - !SHARDING tables: t_claim_case_mdtrt: actualDataNodes: ds$->{['0000','0001']}.t_claim_case_mdtrt_000$->{0..9} tableStrategy: standard: shardingColumn: transaction_no shardingAlgorithmName: t_claim_case_mdtrt_inline keyGenerateStrategy: column: id keyGeneratorName: snowflake t_claim_case_info: actualDataNodes: ds$->{['0000','0001']}.t_claim_case_info_000$->{0..9} tableStrategy: standard: shardingColumn: transaction_no shardingAlgorithmName: t_claim_case_info_inline keyGenerateStrategy: column: id keyGeneratorName: snowflake defaultShardingColumn: transaction_no bindingTables: - t_claim_case_mdtrt, t_claim_case_info defaultDatabaseStrategy: standard: shardingColumn: transaction_no shardingAlgorithmName: database_inline defaultTableStrategy: none: shardingAlgorithms: database_inline: type: INLINE props: algorithm-expression: ds$->{transaction_no[-8..-5]} t_claim_case_mdtrt_inline: type: INLINE props: algorithm-expression: t_claim_case_mdtrt_$->{transaction_no[-4..-1]} t_claim_case_info_inline: type: INLINE props: algorithm-expression: t_claim_case_info_$->{transaction_no[-4..-1]} keyGenerators: snowflake: type: SNOWFLAKE #数据加密 - !ENCRYPT tables: t_claim_case_info: columns: appl_mobile: cipher: name: appl_mobile encryptorName: sm4_encryptor opsnId_no: cipher: name: opsnId_no encryptorName: sm4_encryptor rpter_id_no: cipher: name: rpter_id_no encryptorName: sm4_encryptor rpter_mobile: cipher: name: rpter_mobile encryptorName: sm4_encryptor encryptors: sm4_encryptor: type: SM4 props: sm4-key: 86C63180C2806ED1F43A859DE501215C sm4-mode: ECB sm4-padding: PKCS5Padding # 单表 - !SINGLE tables: - ds_basic.* # 读写分离 - !READWRITE_SPLITTING dataSources: ds0000: writeDataSourceName: ds0000 readDataSourceNames: - ds0000_slave transactionalReadQueryStrategy: PRIMARY loadBalancerName: random ds0001: writeDataSourceName: ds0001 readDataSourceNames: - ds0001_slave transactionalReadQueryStrategy: PRIMARY loadBalancerName: random loadBalancers: random: type: RANDOM props: sql-show: true max-connections-size-per-query: 5

其他

雪花算法自定义worker.id

集群模式下,不同机器需要配置不同的workerId,适合使用ShardingSphere Proxy集群模式,需要使用第三方配置中心(zookeeper)。
但这会变更架构,且本项目CICD时只有一个war部署weblogic server集群的方式。
因此特沿用ShardingSphere JDBC单机模式,并且启动服务时添加处理自定义随机数的workerId以适应集群机器部署。

1.改造雪花算法代码

package com.demo.core.config; import cn.hutool.core.util.RandomUtil; import lombok.Generated; import lombok.Setter; import lombok.SneakyThrows; import org.apache.shardingsphere.infra.algorithm.core.context.AlgorithmSQLContext; import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmExecuteException; import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException; import org.apache.shardingsphere.infra.algorithm.keygen.core.KeyGenerateAlgorithm; import org.apache.shardingsphere.infra.algorithm.keygen.snowflake.SnowflakeKeyGenerateAlgorithm; import org.apache.shardingsphere.infra.algorithm.keygen.snowflake.TimeService; import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions; import org.apache.shardingsphere.infra.instance.InstanceContext; import org.apache.shardingsphere.infra.instance.InstanceContextAware; import org.apache.shardingsphere.infra.instance.workerid.WorkerIdGenerator; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Collection; import java.util.LinkedList; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import static org.apache.shardingsphere.infra.instance.workerid.WorkerIdGenerator.WORKER_ID_KEY; /** * ShardingSphere JDBC 随机workerId雪花算法 * * @date 2024/10/06 13:00 **/ public class RandomWorkerIdSnowflakeKeyGenerateAlgorithm implements KeyGenerateAlgorithm, InstanceContextAware { public static final long EPOCH; private static final String MAX_VIBRATION_OFFSET_KEY = "max-vibration-offset"; private static final String MAX_TOLERATE_TIME_DIFFERENCE_MILLIS_KEY = "max-tolerate-time-difference-milliseconds"; private static final long SEQUENCE_BITS = 12L; private static final long WORKER_ID_BITS = 10L; private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1L; private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS; private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS; private static final int DEFAULT_VIBRATION_VALUE = 1; private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLIS = 10; private static final int DEFAULT_WORKER_ID = 0; @Setter private static TimeService timeService = new TimeService(); private final AtomicReference<InstanceContext> instanceContext = new AtomicReference<>(); private final AtomicInteger sequenceOffset = new AtomicInteger(-1); private final AtomicLong sequence = new AtomicLong(); private final AtomicLong lastMillis = new AtomicLong(); private Properties props; private int maxVibrationOffset; private int maxTolerateTimeDifferenceMillis; private static final String randomWorkerId = RandomUtil.randomNumbers(3); static { EPOCH = LocalDateTime.of(2016, 11, 1, 0, 0, 0).toInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now())).toEpochMilli(); } @Override public void init(final Properties props) { props.setProperty(WorkerIdGenerator.WORKER_ID_KEY, randomWorkerId); this.props = props; maxVibrationOffset = getMaxVibrationOffset(props); maxTolerateTimeDifferenceMillis = getMaxTolerateTimeDifferenceMillis(props); } private int getMaxVibrationOffset(final Properties props) { int result = Integer.parseInt(props.getOrDefault(MAX_VIBRATION_OFFSET_KEY, DEFAULT_VIBRATION_VALUE).toString()); ShardingSpherePreconditions.checkState(result >= 0 && result <= SEQUENCE_MASK, () -> new AlgorithmInitializationException(this, "Illegal max vibration offset.")); return result; } private int getMaxTolerateTimeDifferenceMillis(final Properties props) { int result = Integer.parseInt(props.getOrDefault(MAX_TOLERATE_TIME_DIFFERENCE_MILLIS_KEY, MAX_TOLERATE_TIME_DIFFERENCE_MILLIS).toString()); ShardingSpherePreconditions.checkState(result >= 0, () -> new AlgorithmInitializationException(this, "Illegal max tolerate time difference milliseconds.")); return result; } @Override public void setInstanceContext(final InstanceContext instanceContext) { this.instanceContext.set(instanceContext); if (null != instanceContext) { instanceContext.generateWorkerId(props); } } @Override public Collection<Long> generateKeys(final AlgorithmSQLContext context, final int keyGenerateCount) { Collection<Long> result = new LinkedList<>(); for (int index = 0; index < keyGenerateCount; index++) { result.add(generateKey()); } return result; } private synchronized Long generateKey() { long currentMillis = timeService.getCurrentMillis(); if (waitTolerateTimeDifferenceIfNeed(currentMillis)) { currentMillis = timeService.getCurrentMillis(); } if (lastMillis.get() == currentMillis) { sequence.set(sequence.incrementAndGet() & SEQUENCE_MASK); if (0L == sequence.get()) { currentMillis = waitUntilNextTime(currentMillis); } } else { vibrateSequenceOffset(); sequence.set(sequenceOffset.get()); } lastMillis.set(currentMillis); return ((currentMillis - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | ((long) getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence.get(); } @SneakyThrows(InterruptedException.class) private boolean waitTolerateTimeDifferenceIfNeed(final long currentMillis) { if (lastMillis.get() <= currentMillis) { return false; } long timeDifferenceMillis = lastMillis.get() - currentMillis; ShardingSpherePreconditions.checkState(timeDifferenceMillis < maxTolerateTimeDifferenceMillis, () -> new AlgorithmExecuteException(this, "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds.", lastMillis.get(), currentMillis)); Thread.sleep(timeDifferenceMillis); return true; } private long waitUntilNextTime(final long lastTime) { long result = timeService.getCurrentMillis(); while (result <= lastTime) { result = timeService.getCurrentMillis(); } return result; } private void vibrateSequenceOffset() { if (!sequenceOffset.compareAndSet(maxVibrationOffset, 0)) { sequenceOffset.incrementAndGet(); } } private int getWorkerId() { return null == instanceContext.get() ? DEFAULT_WORKER_ID : instanceContext.get().getWorkerId(); } @Override public String getType() { return "RANDOM_WORKER_ID_SNOWFLAKE"; } @Override public boolean isDefault() { return true; } }

2.添加SPI

添加以下代码路径:

com.demo.core.config.RandomWorkerIdSnowflakeKeyGenerateAlgorithm

3.修改雪花算法配置

http://www.jsqmd.com/news/450521/

相关文章:

  • 【游记】联合省选 2026
  • 小白也能看懂的OpenClaw安装保姆级教程,赶紧先收藏起来,周末实操一下吧,附带命令手册、API配置
  • CVPR‘26 Workshop征稿:探索多智能体具身智能的协同进化
  • 避坑指南:海豚调度器调用Linux资源库Kettle脚本的5个常见错误
  • PSFusion核心技术实战:从原理到部署的全流程解析
  • 少走弯路:AI论文平台 千笔·专业学术智能体 VS 学术猹,本科生写作首选!
  • 3个令人惊叹的C++17功能,让代码变得更简洁
  • Spring Boot + Spring AI快速体验
  • Redis集群模式
  • 如何搭建一个聊天机器人?#3 初步了解koishi、napcat以及onebot
  • 锦囊专家:2025中国企业数智化转型典范案例集 2026
  • 手机也能玩AI换脸?云端部署Roop-Unleashed保姆级教程
  • 免费好用的论文查重网站推荐
  • 群晖Synology Directory Server批量导入用户避坑指南:为什么你的TXT文件总是导入失败?
  • 读懂“社稷为重,君为轻”:真正的忠臣,从不是皇帝的奴才
  • rust web框架actix和axum比较
  • AIChat聊天助手:把 AI 助手“嵌进”你的业务系统
  • 【VMD+Gromacs】用 VMD 玩转分子动力学可视化
  • Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)
  • 强化学习,第五部分:时间差分学习
  • Java Web 校车调度管理系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • “印度人参“药效之“醉茄内酯“内酯环的生物合成途径解析
  • 哈尔滨汽车音响改装 大众揽镜丹麦丹拿232、丹拿212、阿尔派R120-12音响改装升级 全车隔音降噪 哈尔滨消费者满分好评店铺-博士达汽车音响作品 - 木火炎
  • 【2025最新】基于SpringBoot+Vue的校园便利平台管理系统源码+MyBatis+MySQL
  • 【C++基础篇】学习C++就看这篇--->泛型编程之模板
  • 2026搪瓷拼装罐优质品牌推荐指南 - 优质品牌商家
  • 2026年3月,评测揭秘行业口碑好的仿古拉丝机品牌,木纹拉丝机/地板拉丝机/底漆砂光机,拉丝机实力厂家口碑排行 - 品牌推荐师
  • 又一家独角兽冲刺港股:仓储机器人“军备竞赛“进入决赛圈
  • 深度学习驱动的桥梁裂缝检测:从原理到实践
  • 从零开始打造你的专属NAS