From c07e7f48eb622ea224936edfa9d7aecb487a6ba9 Mon Sep 17 00:00:00 2001 From: wangw <1594593906@qq.com> Date: Thu, 2 Jan 2025 22:42:28 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=AA=E8=8A=B1=E7=AE=97=E6=B3=95=20YES/NO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sqx/common/utils/SpringContextUtils.java | 5 + .../java/com/sqx/config/ShardingConfig.java | 28 ++++- src/main/java/com/sqx/config/SnowFlake.java | 114 ++++++++++++++++++ .../java/com/sqx/config/SnowFlakeConfig.java | 24 ++++ .../sqx/modules/utils/InvitationCodeUtil.java | 8 ++ src/main/resources/application.yml | 5 + 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/sqx/config/SnowFlake.java create mode 100644 src/main/java/com/sqx/config/SnowFlakeConfig.java diff --git a/src/main/java/com/sqx/common/utils/SpringContextUtils.java b/src/main/java/com/sqx/common/utils/SpringContextUtils.java index 96a3976f..c92b97e5 100644 --- a/src/main/java/com/sqx/common/utils/SpringContextUtils.java +++ b/src/main/java/com/sqx/common/utils/SpringContextUtils.java @@ -23,6 +23,11 @@ public class SpringContextUtils implements ApplicationContextAware { return applicationContext.getBean(name); } + public static Object getSpringBean(Class clazz) { + return applicationContext == null ? null : applicationContext.getBean(clazz); + } + + public static T getBean(String name, Class requiredType) { return applicationContext.getBean(name, requiredType); } diff --git a/src/main/java/com/sqx/config/ShardingConfig.java b/src/main/java/com/sqx/config/ShardingConfig.java index 7f142a70..b3ff6244 100644 --- a/src/main/java/com/sqx/config/ShardingConfig.java +++ b/src/main/java/com/sqx/config/ShardingConfig.java @@ -1,5 +1,6 @@ package com.sqx.config; +import com.sqx.modules.utils.InvitationCodeUtil; import com.sqx.sharding.MasterSlaveRules; import com.sqx.sharding.ShardingDataBase; import com.zaxxer.hikari.HikariConfig; @@ -8,10 +9,12 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration; +import org.apache.shardingsphere.api.config.sharding.KeyGeneratorConfiguration; import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration; import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration; import org.apache.shardingsphere.api.config.sharding.strategy.InlineShardingStrategyConfiguration; import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory; +import org.codehaus.groovy.util.StringUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -131,13 +134,32 @@ public class ShardingConfig { String.format(centerTablesDataNode, centerTable)); sets.add(tableRuleConfig); } - + //雪花算法方法 + InvitationCodeUtil.getSnowFlakeId(); // 定义区域表的分库规则 InlineShardingStrategyConfiguration databaseShardingStrategyConfig = new InlineShardingStrategyConfiguration( regionTablesShardingDatabaseColumn, regionTablesShardingDatabaseAlgorithm); for (String regionTable : regionTables) { TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration(regionTable, String.format(regionTablesDataNode, regionTable)); tableRuleConfig.setDatabaseShardingStrategyConfig(databaseShardingStrategyConfig); + // 设置区域表使用雪花算法生成主键 + switch (regionTable){ + case "orders": + tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "orders_id")); + break; + case "course_collect": + tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "course_collect_id")); + break; + case "course_user": + tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "course_user_id")); + break; + case "tb_user": + tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "user_id")); + break; + default: + tableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id")); + break; + } sets.add(tableRuleConfig); } @@ -147,6 +169,10 @@ public class ShardingConfig { for (String regionTable : courseDetails) { TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration(regionTable, String.format(regionTablesDataNode, regionTable)); tableRuleConfig.setDatabaseShardingStrategyConfig(courseDetailsShardingStrategyConfig); + // 设置区域表使用雪花算法生成主键 + KeyGeneratorConfiguration keyGeneratorConfig = new KeyGeneratorConfiguration("SNOWFLAKE", "id"); + tableRuleConfig.setKeyGeneratorConfig(keyGeneratorConfig); + sets.add(tableRuleConfig); } return sets; diff --git a/src/main/java/com/sqx/config/SnowFlake.java b/src/main/java/com/sqx/config/SnowFlake.java new file mode 100644 index 00000000..c6096bd7 --- /dev/null +++ b/src/main/java/com/sqx/config/SnowFlake.java @@ -0,0 +1,114 @@ +package com.sqx.config; + +/** + * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版) + **/ +public class SnowFlake { + + /** + * 起始的时间戳 + */ + private final static long START_STMP = 1480166465631L; + + /** + * 每一部分占用的位数 + */ + private final static long SEQUENCE_BIT = 12; //序列号占用的位数 + private final static long MACHINE_BIT = 5; //机器标识占用的位数 + private final static long DATACENTER_BIT = 5;//数据中心占用的位数 + + /** + * 每一部分的最大值 + */ + private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT); + private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT); + private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT); + + /** + * 每一部分向左的位移 + */ + private final static long MACHINE_LEFT = SEQUENCE_BIT; + private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; + private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; + + /** + * 数据中心 + */ + private long datacenterId; + /** + * 机器标识 + */ + private long machineId; + /** + * 代表了一毫秒内生成的多个id的最新序号 + */ + private long sequence = 10000L; + /** + * 上一次时间戳 + */ + private long lastStmp = -1L; + + public SnowFlake() { + } + + public SnowFlake(long datacenterId, long machineId) { + if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { + throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); + } + if (machineId > MAX_MACHINE_NUM || machineId < 0) { + throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); + } + this.datacenterId = datacenterId; + this.machineId = machineId; + } + + /** + * 产生下一个ID + * + * @param ifEvenNum 是否偶数 true 时间不连续全是偶数 时间连续 奇数偶数 false 时间不连续 奇偶都有 所以一般建议用false + */ + public synchronized long nextId(boolean ifEvenNum) { + long currStmp = getNewstmp(); + if (currStmp < lastStmp) { + throw new RuntimeException("Clock moved backwards. Refusing to generate id"); + } + /** + * 时间不连续出来全是偶数 + */ + if (ifEvenNum) { + if (currStmp == lastStmp) { + //相同毫秒内,序列号自增 + sequence = (sequence + 1) & MAX_SEQUENCE; + //同一毫秒的序列数已经达到最大 + if (sequence == 0L) { + currStmp = getNextMill(); + } + } else { + //不同毫秒内,序列号置为0 + sequence = 0L; + } + } else { + //相同毫秒内,序列号自增 + sequence = (sequence + 1) & MAX_SEQUENCE; + } + + lastStmp = currStmp; + + return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 + | datacenterId << DATACENTER_LEFT //数据中心部分 + | machineId << MACHINE_LEFT //机器标识部分 + | sequence; //序列号部分 + } + + private long getNextMill() { + long mill = getNewstmp(); + while (mill <= lastStmp) { + mill = getNewstmp(); + } + return mill; + } + + private long getNewstmp() { + return System.currentTimeMillis(); + } +} \ No newline at end of file diff --git a/src/main/java/com/sqx/config/SnowFlakeConfig.java b/src/main/java/com/sqx/config/SnowFlakeConfig.java new file mode 100644 index 00000000..49b10930 --- /dev/null +++ b/src/main/java/com/sqx/config/SnowFlakeConfig.java @@ -0,0 +1,24 @@ +package com.sqx.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SnowFlakeConfig { + /** + * 数据中心 + */ + @Value("${snowflake.datacenterId}") + private String datacenterId; + /** + * 机器标识 + */ + @Value("${snowflake.machineId}") + private String machineId; + + @Bean(name = "snowFlake") + public SnowFlake snowFlake() { + return new SnowFlake(Long.parseLong(datacenterId), Long.parseLong(machineId)); + } +} \ No newline at end of file diff --git a/src/main/java/com/sqx/modules/utils/InvitationCodeUtil.java b/src/main/java/com/sqx/modules/utils/InvitationCodeUtil.java index aa71722b..66e95faf 100644 --- a/src/main/java/com/sqx/modules/utils/InvitationCodeUtil.java +++ b/src/main/java/com/sqx/modules/utils/InvitationCodeUtil.java @@ -1,5 +1,10 @@ package com.sqx.modules.utils; +import com.sqx.common.utils.SpringContextUtils; +import com.sqx.config.SnowFlake; + +import java.util.Objects; + /** * 邀请码生成解密工具类 * @@ -122,5 +127,8 @@ public class InvitationCodeUtil { return res; } + public static String getSnowFlakeId() { + return String.valueOf(((SnowFlake) Objects.requireNonNull(SpringContextUtils.getSpringBean(SnowFlake.class))).nextId(false)); + } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 642f984a..c3358817 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -59,6 +59,11 @@ limit: urlRate: 10 # 同一用户单url每秒限制次数 ipJumpLimit: 4 # 同一ip每分钟跳动次数 +# 指定sharding-jdbc雪花算法的工作机器ID +snowflake: + datacenterId: 1 + machineId: 3 + spring: main: allow-circular-references: true