Redis实现消息队列

应用场景:如双11的时候大量订单请求,此时如果每个请求都交给Mysql来处理会给数据库带来具大的访问压力;此时就用到消息队列将用户请求的数据存到队列中,队列中的数据在流量低的时候再慢慢加入到数据库中。

解决问题:异步解耦:将下单请求快速写入消息队列,异步处理库存扣减和订单生成,避免阻塞主线程,提升系统响应速度;

流量削峰:应对秒杀等高并发场景,通过消息队列暂存请求,防止数据库瞬间承压;

技术:Redis List 队列。使用 LPUSH 写入消息,RPOP/BRPOP 消费消息,实现生产者 - 消费者模型;

通过阻塞读取(BRPOP)减少轮询开销;

Redis命令:GET key:获取库存值(如 seckill:stock:{voucherId})。

INCRBY key increment:原子性扣减库存(如 INCRBY stock_key -1)。

消息队列:就是下单之后,利用redis去进行校验下单条件,再通过队列把消息存入消息队列中,然后再启动一个线程去消费这个消息,完成解耦,同时也加快我们的响应速度

List:不受限于JVM内存上限、数据安全性有保证、可以满足消息有序性、无法避免消息丢失、只支持单消费者。

PubSub:采用发布订阅模型,支持多生产、多消费、不支持数据持久化、无法避免消息丢失、消息堆积有上限,超出时数据丢失

Stream:消息可回溯、一个消息可以被多个消费者读取、可以阻塞读取、有消息漏读的风险

// Rul脚本,判断用户是否有购买资格
private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
​
// 阻塞队列
    private ArrayBlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
​
// 符合购买条件则将其信息加入消息队列中
@Override
    public Result seckillVoucher(Long voucherId) {
        Long id = UserHolder.getUser().getId();
        // 执行lua脚本
        Long execute = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), 
                id.toString()
        );
        int r = execute.intValue();
        // 判断结果是否为0
        if(r != 0){
            // 不为0, 没有下单资格
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        //为0,把下单信息保存到阻塞队列
        long order = redisIdWorker.nexId("order");
        VoucherOrder voucherOrder = new VoucherOrder();
        voucherOrder.setId(order);
        voucherOrder.setUserId(id);
        voucherOrder.setVoucherId(voucherId);
        orderTasks.add(voucherOrder);
        // 获取代理对象(事务)
        proxy = (IVoucherOrderService) AopContext.currentProxy();
        // 返回订单id
        return Result.ok(order);
    }
---优惠券id
local voucher = ARGV[1]
​
--本次需要购买优惠券的用户id
local user = ARGV[2]
​
-- 库存,保存有多少优惠券
local stock_key = "seckill:stock:" .. voucher
--订单key, 保存购买优惠券的用户id集合
local order_key = "seckill:order:" .. voucher
-- 判断库存是否为充足
if(tonumber(redis.call("get",stock_key)) <= 0)
then
    -- 库存不足
    return 1
end
​
-- 判断用户是否已经购买过
if(redis.call("sismember",order_key,user) == 1)
then
    -- 用户已经购买过
    return 2
end
​
-- 扣减库存
redis.call("incrby",stock_key,-1)
​
-- 将用户id保存到集合中
redis.call('sadd', order_key, user)
return 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值