Skip to content

Commit

Permalink
docs(idempotency): add distributed-system-idempotency.md
Browse files Browse the repository at this point in the history
  • Loading branch information
yanglbme committed Nov 28, 2018
1 parent acb8bd8 commit 7148987
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](/docs/distributed-system/dubbo-load-balancing.md)
- [Dubbo 的 spi 思想是什么?](/docs/distributed-system/dubbo-spi.md)
- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
- 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
- 分布式服务接口请求的顺序性如何保证?
- 如何自己设计一个类似 Dubbo 的 rpc 框架?

Expand Down
27 changes: 27 additions & 0 deletions docs/distributed-system/distributed-system-idempotency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## 面试题
分布式服务接口的幂等性如何设计(比如不能重复扣款)?

## 面试官心理分析
从这个问题开始,面试官就已经进入了**实际的生产问题**的面试了。

一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?

你看,假如你有个服务提供一个接口,结果这服务部署在了 5 台机器上,接着有个接口就是**付款接口**。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单**不小心发起了两次支付请求**,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。

或者是订单系统调用支付系统进行支付,结果不小心因为**网络超时**了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。

所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑。

## 面试题剖析
这个不是技术问题,这个没有通用的一个方法,这个应该**结合业务**来保证幂等性。

所谓**幂等性**,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。

其实保证幂等性主要是三点:
- 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
- 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
- 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。

实际运作过程中,你要结合自己的业务来,比如说利用 redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。

要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 `unique key`。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 redis 里面去,`set order_id payed`,下一次重复请求过来了,先查 redis 的 order_id 对应的 value,如果是 `payed` 就说明已经支付过了,你就别重复支付了。

0 comments on commit 7148987

Please sign in to comment.