1 面试题
分布式服务接口的幂等性如何设计(比如不能重复扣款)?
2 考点分析
从这开始,面试官就已经进入了实际的生产问题的面试了
一个分布式系统中的某个接口,要保证幂等性,如何保证?
这个事,其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题.为什么呢?
假如你有个服务提供一个接口,这服务部署在5台机器上,有个付款接口.
然后用户在前端操作时,不知为啥,一个订单不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,这下好了,结果一个订单扣款扣两次,尴尬了!
或者是订单系统调用支付系统进行支付,结果不小心网络超时,然后订单系统走了前面我们看到的那个重试机制,给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了!
- 分布式系统接口的幂等性问题
所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑!
网络问题很常见,100次请求,都ok
1万次,可能1次是超时会重试
10万,10次;100万,100次;如果有100个请求重复了,你没处理,导致订单扣款2次,100个订单都扣错了;每天被100个用户投诉;一个月被3000个用户投诉
这个不是技术问题,没有通用的一个方法,得结合业务来看应该如何保证幂等性.
3 幂等性
所谓幂等性,简而言之,就是一个接口,多次发起同一个请求,接口得保证结果是准确的,比如不能多扣款,不能多插入一条数据,不能将统计值多加了1.
保证幂等性主要有如下几点
- 对于每个请求必须有一个唯一的标识
举个例子:订单支付请求,肯定得包含订单id,一个订单id最多支付一次 - 每次处理完请求后,须有一个记录标识该请求已被处理
比如说常见的方案是在MySQL中记录个状态字段
比如支付之前记录一条这个订单的支付流水 - 每次接收请求需要进行判断之前是否处理过
比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId已存在,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了.
上面只是举个例子,实际运作过程中,要结合自己的业务来,比如说用redis,用orderId作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
要求是支付一个订单,必须插入一条支付流水,order_id建立一个唯一键unique key
你在支付一个订单前,先插入一条支付流水,order_id
就已经传过去了
你就可以写一个标识到Redis中,set order_id payed
,当重复请求过来时,先查Redis的order_id
对应的value
,若为payed
说明已支付,就别重复支付了!
然后呢,你再重复支付这个订单的时候,你写尝试插入一条支付流水,数据库给你报错了,说unique key冲突了,整个事务回滚就可以了
来保存一个是否处理过的标识也可以,服务的不同实例可以一起操作Redis.
参考
- 《Java工程师面试突击第1季-中华石杉老师》