Redis+Lua實現簡易的秒殺搶購

1  商品搶購

主要邏輯是:減庫存,記錄搶購成功的用戶

@RestController
public class DemoController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final String GOODS_STOCK_KEY = "goods:001";  //  秒殺商品庫存
    private static final String GOODS_USER_KEY = "users:001";   //  搶購成功的用戶列表

    /**
     * 在不加鎖的情況下,會發生超賣
     */
    @GetMapping("/seckill")
    public String seckill() {
        int userId = (int) (Math.random() * 1000);

        ValueOperations valueOps = stringRedisTemplate.opsForValue();
        ListOperations listOps = stringRedisTemplate.opsForList();

        int stock = Integer.parseInt(valueOps.get(GOODS_STOCK_KEY));

        if (stock > 0) {
            valueOps.decrement(GOODS_STOCK_KEY);
            listOps.leftPush(GOODS_USER_KEY, String.valueOf(userId));
            return "搶購成功";
        } else {
            return "商品已售罄";
        }
    }

    /**
     * 將多個命令打包成一個原子操作,利用redis單線程執行命令的特性,在不加鎖的情況下避免了資源競爭
     */
    @GetMapping("/seckill_lua")
    public String seckill_lua() {
        int userId = (int) (Math.random() * 1000);

        String script = "if tonumber(redis.call('get', KEYS[1])) > 0 then " +
                "redis.call('decr', KEYS[1]); " +
                "redis.call('lpush', KEYS[2], ARGV[1]); " +
                "return 1; " +
                "else " +
                "return 0; " +
                "end; ";

        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setResultType(Long.class);
        redisScript.setScriptText(script);

        List keyList = Arrays.asList(GOODS_STOCK_KEY, GOODS_USER_KEY);

        Long result = stringRedisTemplate.execute(redisScript, keyList, String.valueOf(userId));

        if (result == 1) {
            return "搶購成功";
        } else {
            return "商品已售罄";
        }
    }
}

2  多線程處理Excel導入

/**
 * 多線程處理Excel導入
 *
 * PS:
 *  Executors返回的線程池對象的弊端如下:
 *  (1) FixedThreadPool和SingleThreadPool: 允許的請求隊列長度爲Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。
 *  (2) CachedThreadPool: 允許的創建線程數量爲Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。
 */
@PostMapping
public void excelImport() throws InterruptedException {
    //  待處理的數據(比如:從Excel中讀取的數據) 
    List dataList = new ArrayList();

    //  多線程處理
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    CountDownLatch countDownLatch = new CountDownLatch(dataList.size());
    for (Object obj : dataList) {
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {

                } catch (Exception ex) {

                } finally {
                   countDownLatch.countDown();
                }
            }
        });
    }

    countDownLatch.await(30, TimeUnit.SECONDS);

    //  後續執行

    //  返回結果
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章