雪花算法

ays
An Yong Shuai 2024-07-11 19:08:20 +08:00
parent b8ff0aefe2
commit 0b54d0e840
8 changed files with 321 additions and 63 deletions

View File

@ -4,33 +4,15 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="e53020f6-f301-415a-a26a-d22b9ac05907" name="更改" comment="秒杀项目初始化">
<change afterPath="$PROJECT_DIR$/etl-cleaning/src/main/java/com/etl/cleaning/job/Testaa.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/entity/request/OrderRequest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/enums/EnumMsg.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/handler/CatchExceptions.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/job/GoodsRedisJob.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/pdf/ExportPdfTest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/resources/font/simfang.ttf" afterDir="false" />
<list default="true" id="e53020f6-f301-415a-a26a-d22b9ac05907" name="更改" comment="集成redisson">
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/config/SnowFlake.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/config/SnowFlakeComponeConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/entity/request/Stu.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-cleaning/src/main/java/com/etl/cleaning/EtlCleaningApplication.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-cleaning/src/main/java/com/etl/cleaning/EtlCleaningApplication.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-common/target/etl-common-1.0-SNAPSHOT.jar" beforeDir="false" afterPath="$PROJECT_DIR$/etl-common/target/etl-common-1.0-SNAPSHOT.jar" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-common/target/maven-archiver/pom.properties" beforeDir="false" afterPath="$PROJECT_DIR$/etl-common/target/maven-archiver/pom.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/pom.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/EtlSpikeApplication.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/EtlSpikeApplication.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/GoodsController.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/GoodsController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/TimesGoodsController.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/TimesGoodsController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/service/IOrderService.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/service/IOrderService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/serviceImpl/OrderServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/serviceImpl/OrderServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/application.properties" beforeDir="false" afterPath="$PROJECT_DIR$/etl-spike/src/main/resources/application.properties" afterDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/mapper/GoodsMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/mapper/OrderItemMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/mapper/OrderMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/mapper/TimesGoodsMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/etl-spike/src/main/resources/mapper/TimesMapper.xml" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -126,7 +108,9 @@
&quot;run.configurations.included.in.services&quot;: &quot;true&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;,
&quot;spring.configuration.checksum&quot;: &quot;a925cb78a857f19db6d9680ed9c97ee6&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;,
&quot;应用程序.OrderController.executor&quot;: &quot;Debug&quot;,
&quot;应用程序.SnowFlake.executor&quot;: &quot;Debug&quot;
},
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
@ -153,20 +137,6 @@
<recent name="com.etl.cleaning.domian.request" />
</key>
</component>
<component name="RunAnythingCache">
<myKeys>
<visibility group="Gradle 任务" flag="true" />
<visibility group="Grunt" flag="true" />
<visibility group="Gulp" flag="true" />
<visibility group="HTTP 请求" flag="true" />
<visibility group="Maven 目标" flag="true" />
<visibility group="Node.js" flag="true" />
<visibility group="npm" flag="true" />
<visibility group="yarn" flag="true" />
<visibility group="最近的项目" flag="true" />
<visibility group="运行配置" flag="true" />
</myKeys>
</component>
<component name="RunDashboard">
<option name="configurationTypes">
<set>
@ -176,12 +146,32 @@
</set>
</option>
</component>
<component name="RunManager" selected="Spring Boot.EtlSpikeApplication">
<configuration name="generated-requests | #1" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" temporary="true" nameIsGenerated="true" path="$APPLICATION_CONFIG_DIR$/scratches/generated-requests.http" requestIdentifier="#1">
<method v="2" />
<component name="RunManager" selected="应用程序.SnowFlake">
<configuration name="OrderController" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.etl.spike.controller.OrderController" />
<module name="etl-spike" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.etl.spike.controller.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="generated-requests | #6" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" temporary="true" nameIsGenerated="true" path="$APPLICATION_CONFIG_DIR$/scratches/generated-requests.http" index="6" requestIdentifier="#6">
<method v="2" />
<configuration name="SnowFlake" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.etl.spike.config.SnowFlake" />
<module name="etl-spike" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.etl.spike.config.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="EtlCleaningApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" temporary="true" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="1.8" />
@ -227,17 +217,19 @@
</method>
</configuration>
<list>
<item itemvalue="HTTP 请求.generated-requests | #1" />
<item itemvalue="HTTP 请求.generated-requests | #6" />
<item itemvalue="Spring Boot.EtlSpikeApplication" />
<item itemvalue="Spring Boot.EtlCleaningApplication" />
<item itemvalue="Spring Boot.EtlGatewayApplication" />
<item itemvalue="应用程序.OrderController" />
<item itemvalue="应用程序.SnowFlake" />
</list>
<recent_temporary>
<list>
<item itemvalue="应用程序.SnowFlake" />
<item itemvalue="应用程序.OrderController" />
<item itemvalue="Spring Boot.EtlSpikeApplication" />
<item itemvalue="Spring Boot.EtlCleaningApplication" />
<item itemvalue="Spring Boot.EtlGatewayApplication" />
<item itemvalue="Spring Boot.EtlCleaningApplication" />
</list>
</recent_temporary>
</component>
@ -293,7 +285,8 @@
<workItem from="1720573540952" duration="27101000" />
<workItem from="1720609776879" duration="8472000" />
<workItem from="1720657818833" duration="4106000" />
<workItem from="1720680767868" duration="1568000" />
<workItem from="1720680767868" duration="10332000" />
<workItem from="1720693637084" duration="2428000" />
</task>
<task id="LOCAL-00001" summary="安勇帅提交测试">
<option name="closed" value="true" />
@ -479,7 +472,15 @@
<option name="project" value="LOCAL" />
<updated>1720577357457</updated>
</task>
<option name="localTasksCounter" value="24" />
<task id="LOCAL-00024" summary="集成redisson">
<option name="closed" value="true" />
<created>1720682351775</created>
<option name="number" value="00024" />
<option name="presentableId" value="LOCAL-00024" />
<option name="project" value="LOCAL" />
<updated>1720682351775</updated>
</task>
<option name="localTasksCounter" value="25" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -566,7 +567,8 @@
<MESSAGE value="任务管理基本操作" />
<MESSAGE value="获取数据库下的所有表名" />
<MESSAGE value="秒杀项目初始化" />
<option name="LAST_COMMIT_MESSAGE" value="秒杀项目初始化" />
<MESSAGE value="集成redisson" />
<option name="LAST_COMMIT_MESSAGE" value="集成redisson" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@ -17,6 +17,7 @@ public class RedissonConfig {
@Value ("${redisson.password}")
private String password;
/**
*
*/

View File

@ -0,0 +1,115 @@
package com.etl.spike.config;
/**
*
*/
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;//数据中心占用的位数
/**
*
*/
//支持的最大数据标识id结果是31
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
//支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
*
*/
//机器ID向左移12位
private final static long MACHINE_LEFT = SEQUENCE_BIT;
//数据标识id向左移17位(12+5)
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
//时间截向左移22位(5+5+12)
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; //数据中心
private long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
/**
*
* @param datacenterId Id(0-31)
* @param machineId //机器标识Id(0-31)
*/
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
*
* @return
*/
public synchronized long nextId() {
//获取当前时间戳
long currStmp = getNewstmp();
//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
//如果是同一时间生成的,则进行毫秒内序列递增
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内序列号置为0
sequence = 0L;
}
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();
}
public static void main(String[] args) {
SnowFlake snowFlake = new SnowFlake(2, 5);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
System.out.println(snowFlake.nextId());
}
System.out.println((System.currentTimeMillis() - start)/1000 + "秒");
}
}

View File

@ -0,0 +1,41 @@
package com.etl.spike.config;
import cn.hutool.core.lang.Snowflake;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
*
*/
@Component
public class SnowFlakeComponeConfig {
/**
* ID
*/
@Value ("${server.workId}")
private Long workId;
/**
* ID
*/
@Value ("${server.datacenterId}")
private Long datacenterId;
private static volatile SnowFlake instance;
/**
*
* @return
*/
public SnowFlake getInstance(){
if(instance == null){
synchronized (Snowflake.class){
if(instance == null){
instance = new SnowFlake (workId,datacenterId);
}
}
}
return instance;
}
}

View File

@ -4,9 +4,14 @@ package com.etl.spike.controller;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.etl.common.result.Result;
import com.etl.spike.config.SnowFlakeComponeConfig;
import com.etl.spike.entity.Order;
import com.etl.spike.entity.request.OrderRequest;
import com.etl.spike.entity.request.Stu;
import com.etl.spike.enums.EnumMsg;
import com.etl.spike.service.IOrderService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@ -15,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* <p>
@ -29,9 +35,13 @@ import java.util.Objects;
public class OrderController {
private final StringRedisTemplate redisTemplate;
private final IOrderService orderService;
public OrderController(StringRedisTemplate redisTemplate , IOrderService orderService){
private final RedissonClient redissonClient;
private final SnowFlakeComponeConfig snowFlakeComponeConfig;
public OrderController(StringRedisTemplate redisTemplate , IOrderService orderService , RedissonClient redissonClient , SnowFlakeComponeConfig snowFlakeComponeConfig){
this.redisTemplate = redisTemplate;
this.orderService = orderService;
this.redissonClient = redissonClient;
this.snowFlakeComponeConfig = snowFlakeComponeConfig;
}
/**
@ -68,17 +78,54 @@ public class OrderController {
if ( !md5Hex.equals (orderRequest.getSign ()) ) {
throw new RuntimeException ("验签失败");
}
//检验库存
if( !redisTemplate.opsForHash ().hasKey (EnumMsg.REDIS_KEY.getMessage () , orderRequest.getGoodsId ()) ) {
throw new RuntimeException ("该商品库存异常");
//定义锁键 (不同的商品获取不同的锁)
String lockKey = "redissonLock_"+orderRequest.getGoodsId ();
//获取锁
RLock lock = redissonClient.getLock (lockKey);
try {
//10秒内获取锁锁自动超时时间为3分钟
boolean tryLock = lock.tryLock (10 , 3 , TimeUnit.MINUTES);
if(!tryLock){
return Result.error ("获取锁失败");
}
//检验库存
if( !redisTemplate.opsForHash ().hasKey (EnumMsg.REDIS_KEY.getMessage () , orderRequest.getGoodsId ())) {
throw new RuntimeException ("该商品库存异常");
}
String num = Objects.requireNonNull (redisTemplate.opsForHash ().get (EnumMsg.REDIS_GOODS_NUM.getMessage () ,
orderRequest.getGoodsId ())).toString ();
long goodsNum = Long.parseLong (num);
if(goodsNum<=0){
throw new RuntimeException ("该商品库存不足");
}
//生成订单(订单参数赋值)
Order order = new Order ();
//订单编号雪花算法
long id = snowFlakeComponeConfig.getInstance ().nextId ();
return Result.success ();
} catch (InterruptedException e) {
Thread.currentThread ().interrupt (); //保持中断状态
return Result.error ("处理过程被中断");
}finally {
//如果线程持有锁则释放锁
if(lock.isHeldByCurrentThread ()){
lock.unlock ();
}
}
String num = Objects.requireNonNull (redisTemplate.opsForHash ().get (EnumMsg.REDIS_GOODS_NUM.getMessage () ,
orderRequest.getGoodsId ())).toString ();
long goodsNum = Long.parseLong (num);
if(goodsNum<=0){
throw new RuntimeException ("该商品商品不足");
}
orderService.orderGoOn();
return Result.success ();
}
public static void main(String[] args){
// int a=1;
// int b=1;
// System.out.println (a==b); //true
// String c="1";
// String d="1";
// System.out.println (c.equals (d)); //true
Stu stu = new Stu ("aa",1);
Stu stu1 = new Stu ("aa" , 1);
System.out.println (stu1==stu);
System.out.println (stu1.equals (stu));
}
}

View File

@ -6,6 +6,9 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* pdf
*/
@RestController
@RequestMapping("/pdf")
public class PdfController {

View File

@ -0,0 +1,46 @@
package com.etl.spike.entity.request;
public class Stu {
private String name;
private int id;
// 构造函数
public Stu(String name, int id) {
this.name = name;
this.id = id;
}
// Getter 和 Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public boolean equals(Object obj){
// 检查是否为同一个对象的引用
if (this == obj) {
return true;
}
// 检查传入的对象是否为null或者不是Stu类的实例
if (obj == null || getClass() != obj.getClass()) {
return false;
}
// 类型转换
Stu stu = (Stu) obj;
// 比较属性值
return id == stu.id && (name == stu.name || (name != null && name.equals(stu.name)));
}
}

View File

@ -18,5 +18,8 @@ spring.cloud.sentinel.transport.port=localhost:8719
# ??????
management.endpoints.web.exposure.include= *
spring.main.allow-circular-references=true
redisson.addresses=redis://47.101.130.221:6379
redisson.address=redis://47.101.130.221:6379
redisson.password=123456
# ????
server.workId=2
server.datacenterId=2