防止重复下单(redis+数据库唯一索引requestId实现幂等)

为什么会重复下单

为什么会重复下单,对于订单服务而言,就是接到了多个下单的请求,原因可能有很多,最常见的是这两种:

  • 用户重复提交
  • 网络原因导致的超时重试
    在这里插入图片描述

如何防止重复下单

防止用户提交,最常规的做法,就是客户端 点击下单之后,在 收到服务端响应之前,按钮置灰!!!

当然,防止重复下单,肯定不能只依靠客户端,可能会因为一些网络的抖动导致仍然有重复的请求到达服务端,所以还是要在服务端做防重/幂等的处理

PS:这里额外插入一点我对防重和幂等的理解:防重指的是防止重复提交幂等指的是多次请求如一次,简单说,就是防重可以给对重复请求抛异常,幂等是对重复的请求响应第一次的结果,在我们讨论的这个场景里,幂等 就是响应唯一的订单号。

在这里插入图片描述

防重第一步,需要识别请求是否重复,这一步,需要客户端配合实现

需要客户端在请求下单接口的时候,需要生成一个唯一的请求号:requestId,服务端拿这个请求号,判断是否重复请求

客户端 怎么生成唯一的请求号?客户端在提交之前,先去 发送请求 获取一个uuid作为requestId,然后请求时带上这个requestId这个参数即可

利用数据库实现幂等

可以在订单表t_order里添加一个字段:requestId,添加唯一索引:

在这里插入图片描述

利用Redis防重

另外一个办法,就是下单请求的时候要加锁了,通常我们的服务都是集群部署,所以一般都是用Redis实现分布式锁。

大概的逻辑:

  • 就是以requestId为维度,进行加锁,如果获取锁失败,就抛一个自定义的重复下单异常。
  • 如果获取到锁,先check一下,是否已经下单,为了提高性能,下单完成后,也把下单的结果放在Redis缓存里
    在这里插入图片描述

大概的代码如下:

    public PlaceOrderResVO placeOrder(PlaceOrderReqVO reqVO) {
        //加锁
        RLock orderLock = redissonClient.getLock(RedisConstant.PLACE_ORDER_LOCK_KEY + reqVO.getRequestId());
        //获取锁失败,抛出重复下单异常
        if(orderLock.isExistes){
          throw new OrderRepeatException();
        }
        // 加锁
        orderLock.lock();
        try {
            //检查是否已经下单
            RBucket<PlaceOrderResVO> orderCache = redissonClient.getBucket(RedisConstant.PLACE_ORDER_LOCK_KEY+reqVO.getRequestId());
            if(orderCache.isExistes){
                return orderCache.get();
            }
            //下单业务逻辑
            ……
            //落库
            //订单落库
            Order order = orderMapper.saveOrder(orderDO); 
            ……
            //缓存结果
            orderCache.put(resVO);
            return resVO;
        } 
        } catch (Exception e) {
            //……
        } finally {
            orderLock.unlock();
        }
        return resVO;
    }

为什么获取不到锁的时候要抛异常呢?

因为下单里面其实还有一些其它的业务流程,比如**锁库存、清优惠券……**而此时,获取到锁的请求的下单流程还没有结束,下单的结果还获取不到,没法完成响应,也就没办法做幂等

客户端,也可以根据响应的状态码,进行特殊处理,比如这个异常先不提示,但是允许用户再次点击下单按钮,来提升用户的体验

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值