diff --git a/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java b/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java
index 605c237..841d785 100644
--- a/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java
+++ b/etl-spike/src/main/java/com/etl/spike/config/RedissonConfig.java
@@ -17,6 +17,7 @@ public class RedissonConfig {
@Value ("${redisson.password}")
private String password;
+
/**
* 单机模式配置
*/
diff --git a/etl-spike/src/main/java/com/etl/spike/config/SnowFlake.java b/etl-spike/src/main/java/com/etl/spike/config/SnowFlake.java
new file mode 100644
index 0000000..37a4472
--- /dev/null
+++ b/etl-spike/src/main/java/com/etl/spike/config/SnowFlake.java
@@ -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 + "秒");
+ }
+}
diff --git a/etl-spike/src/main/java/com/etl/spike/config/SnowFlakeComponeConfig.java b/etl-spike/src/main/java/com/etl/spike/config/SnowFlakeComponeConfig.java
new file mode 100644
index 0000000..f72c067
--- /dev/null
+++ b/etl-spike/src/main/java/com/etl/spike/config/SnowFlakeComponeConfig.java
@@ -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;
+ }
+
+}
diff --git a/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java b/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java
index 2847378..192049a 100644
--- a/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java
+++ b/etl-spike/src/main/java/com/etl/spike/controller/OrderController.java
@@ -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;
/**
*
@@ -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));
+
}
}
diff --git a/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java b/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java
index f540509..2f5f5df 100644
--- a/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java
+++ b/etl-spike/src/main/java/com/etl/spike/controller/PdfController.java
@@ -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 {
diff --git a/etl-spike/src/main/java/com/etl/spike/entity/request/Stu.java b/etl-spike/src/main/java/com/etl/spike/entity/request/Stu.java
new file mode 100644
index 0000000..5405947
--- /dev/null
+++ b/etl-spike/src/main/java/com/etl/spike/entity/request/Stu.java
@@ -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)));
+ }
+
+}
diff --git a/etl-spike/src/main/resources/application.properties b/etl-spike/src/main/resources/application.properties
index 2e51f33..77a209c 100644
--- a/etl-spike/src/main/resources/application.properties
+++ b/etl-spike/src/main/resources/application.properties
@@ -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