Redis+Lua实现简易的秒杀抢购

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);

    //  后续执行

    //  返回结果
}
hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Redis+Lua实现简易的秒杀抢购