소스 검색

redis lua

Qing 1 년 전
부모
커밋
b6acba78aa

+ 36 - 3
redis-demo/src/main/java/com/sf/service/RedisServiceImpl.java

@@ -1,12 +1,20 @@
 package com.sf.service;
 
 import com.sf.utils.RedisUtils;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.ClassPathResource;
 import org.springframework.dao.DataAccessException;
 import org.springframework.data.redis.core.RedisOperations;
 import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.scripting.support.ResourceScriptSource;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
 @Service
 public class RedisServiceImpl implements RedisService {
 
@@ -15,6 +23,9 @@ public class RedisServiceImpl implements RedisService {
 
     @Override
     public void seckill(String userId, String goodsId) {
+        // 先判断 是否在秒杀有效时间内
+        //  也就是当前时间  是否 >= startTime  <= endTime
+
         // goodsId = 1
         // 1_stock_num
 //        String key = goodsId + "_stock_num";
@@ -25,7 +36,8 @@ public class RedisServiceImpl implements RedisService {
 //            Long decr = redisUtils.decr(key);
 //            System.out.println(decr);
 //        }
-        handleByTran(userId, goodsId);
+//        handleByTran(userId, goodsId);
+        handleByLua(userId, goodsId);
 
         // 加锁  乐观锁和悲观锁
         //  以悲观的态度看待  如果修改数据时不增加锁  一定会出现问题
@@ -69,11 +81,28 @@ public class RedisServiceImpl implements RedisService {
                 operations.multi();
                 redisUtils.decr(key);
                 // 代表一笔订单生成  订单id=商品id+用户id
-                redisUtils.set(goodsId + "_" + userId, "1");
+                redisUtils.set("order::" + goodsId + "_" + userId, "1");
                 return operations.exec();
             }
         };
-        redisUtils.execute(sessionCallback);
+        Object object = redisUtils.execute(sessionCallback);
+        System.out.println(object);
+    }
+
+    public void handleByLua(String userId, String goodsId) {
+        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
+        script.setResultType(Long.class);
+
+        ClassPathResource classPathResource = new ClassPathResource("script/seckill.lua");
+        ResourceScriptSource resourceScriptSource = new ResourceScriptSource(classPathResource);
+        script.setScriptSource(resourceScriptSource);
+
+        List<String> keyList = new ArrayList<>();
+        keyList.add(userId);
+        keyList.add(goodsId);
+
+        Object result = redisUtils.execute(script, keyList);
+        log.info("userId:{},goodsId:{},result:{}", userId, goodsId, result);
     }
 
     @Override
@@ -95,5 +124,9 @@ public class RedisServiceImpl implements RedisService {
 //        map.put("2", 10L);
 //        redisUtils.setAllMap("stock_num", map);
 
+
+        // <1_startTime,"20220224">
+        // <1_endTime,"20220227">
+
     }
 }

+ 10 - 0
redis-demo/src/main/java/com/sf/utils/RedisUtils.java

@@ -3,8 +3,10 @@ package com.sf.utils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.SessionCallback;
+import org.springframework.data.redis.core.script.RedisScript;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Map;
 
 @Component
@@ -29,6 +31,10 @@ public class RedisUtils {
         return redisTemplate.opsForValue().decrement(key);
     }
 
+    public void setNx(String key, Object value) {
+        redisTemplate.opsForValue().setIfAbsent(key, value);
+    }
+
     public void setMap(String key, String subKey, Object subValue) {
         redisTemplate.opsForHash().put(key, subKey, subValue);
     }
@@ -41,6 +47,10 @@ public class RedisUtils {
         return redisTemplate.execute(sessionCallback);
     }
 
+    public Object execute(RedisScript script, List keys) {
+        return redisTemplate.execute(script, keys);
+    }
+
     public boolean hasKey(String key) {
         return redisTemplate.hasKey(key);
     }

+ 13 - 1
redis-demo/src/main/java/com/sf/utils/thread/TestRunnable.java

@@ -19,11 +19,23 @@ public class TestRunnable {
                 }
             });
             threads[i] = thread;
-            threads[i].start();
+//            threads[i].start();
         }
 
 //        for (int i = 0; i < threads.length; i++) {
+//            Thread thread = new Thread(() -> {
+//                try {
+//                    HttpUtils.seckill(url);
+//                } catch (Exception e) {
+//                    throw new RuntimeException(e);
+//                }
+//            });
+//            threads[i] = thread;
 //            threads[i].start();
 //        }
+
+        for (int i = 0; i < threads.length; i++) {
+            threads[i].start();
+        }
     }
 }

+ 23 - 0
redis-demo/src/main/java/com/sf/utils/thread/TestRunnable1.java

@@ -0,0 +1,23 @@
+package com.sf.utils.thread;
+
+import com.sf.utils.HttpUtils;
+
+public class TestRunnable1 {
+
+    public static void main(String[] args) {
+        String url = "http://localhost:8080/seckill?userId=111&goodsId=1";
+        for (int i = 0; i < 20; i++) {
+            Thread thread = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        HttpUtils.seckill(url);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+            thread.start();
+        }
+    }
+}

+ 2 - 2
redis-demo/src/main/java/com/sf/utils/thread/TestThreadPool.java

@@ -11,14 +11,14 @@ public class TestThreadPool {
         // new ThreadPoolExecutor()
         // Executors 使用线程池的工具类
         // 可以通过newFixedThreadPool 获取固定个数的线程池
-        ExecutorService executorService = Executors.newFixedThreadPool(20);
+        ExecutorService executorService = Executors.newFixedThreadPool(100);
 
         // Runnable Callable
         // 对于Callable  要通过Future/FutureTask 来获取返回结果
 
         String url = "http://localhost:8080/seckill?";
         List<Callable<String>> tasks = new ArrayList<>();
-        for (int i = 0; i < 20; i++) {
+        for (int i = 0; i < 100; i++) {
             // 动态url的处理
             String newUrl = url + "userId=" + i + "&goodsId=1";
             System.out.println(newUrl);

+ 1 - 0
redis-demo/src/main/resources/application.yml

@@ -4,4 +4,5 @@ spring:
 #      host: 127.0.0.1
       host: 192.168.31.245
       port: 6379
+#      database: 0
 #      password: 123456

+ 25 - 0
redis-demo/src/main/resources/script/seckill.lua

@@ -0,0 +1,25 @@
+-- 注释是
+-- 声明变量是local
+-- 获取传递的参数是keys数组 从1开始获取
+-- 拼接字符串 "+"在这里是 ".."  字符串本身的使用 ''
+-- redis相关命令 使用redis.call方法  需要的参数是 命令名称 命令的参数  get key
+-- 如果要处理接受的结果  将不限定类型的local转为数值类型  使用tonumber方法
+-- 再去执行减库存 和 新增订单操作
+-- return的是脚本返回给redis的结果
+-- 获取传递的参数 分别是用户id和商品id
+local userId = KEYS[1];
+local goodsId = KEYS[2];
+-- 根据参数拼接key值
+local numKey = goodsId .. '_stock_num';
+local orderKey = 'order::' .. goodsId .. '_' .. userId;
+-- 判断是否有库存
+local num = redis.call("get",numKey);
+if(tonumber(num) <= 0) then
+-- 代表秒杀空了
+   return 0;
+else
+-- 秒杀成功
+   redis.call("decr",numKey);
+   redis.call("set",orderKey,1);
+end
+return 1;