开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情
1.接口的幂等性背景
一个分布式系统中的某个接口,如何保证幂等性?这个问题是我们在实际的生产当中必须考虑的问题,比如说
1.1 场景一:
我们的一个服务部署了多台机器,比如电商里边的订单服务,因为大促来临,为了承担更多流量,部署了多台机器分散流量压力,前端肯定是通过网关,利用负载均衡算法,将请求发送到后端的服务进行推单流程,但是由于某些原因,在扣款环节或者扣减库存优惠券环节,发起了多次请求,这些请求被发到多台机器上边,发生了多次扣减,这肯定是有问题的。
由于网络原因,我们的订单流程处理时间较长,返回超时了,但是订单还在正常流转,并没有出错,一看到超时,我们手动或者自动的会发起重试,这就造成发多次请求。
2.如何保证幂等?
这个一般是要结合现实的业务场景来说的,所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款,不能多插入一条数据,不能将统计值多加了1。
其实保证幂等性主要是三点:
- 对于每个请求必须有一个唯一的标识,举个例子:订单支付请求,肯定得包含订单id,一个订单id最多支付一次
- 每次处理完请求之后,必须有一个记录标识这个请求处理过了,比如说常见的方案是在mysql中记录个状态啥的,比如支付之前记录一条这个订单的支付流水
- 每次接收请求需要进行判断之前是否处理过的逻辑处理,比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
一般订单支付前,必须插入一条支付流水,order_id建一个唯一键,unique key,所以你在支付一个订单之前,先插入一条支付流水,order_id就已经进去了
Order_id如何我们可以结合redis去生成,用订单号做redis的key,支付状态做value,可以写一个标识到redis里面去,set order_id payed,下一次重复请求过来了,先查redis的order_id对应的value,如果是payed就说明已经支付过了,你就别重复支付了
然后,再重复支付这个订单的时候,当尝试插入一条支付流水时候,数据库报错了,说unique key冲突了,整个事务回滚就可以了