实现微服务的幂等

本文探讨了从单体架构转向微服务架构时,重复请求问题的加剧及其解决方案。通过实例说明了如何设计接口的幂等性,包括使用全局唯一ID、去重表和多版本控制等方法,确保在微服务架构中重复调用接口的一致性和可靠性。

转自:https://blog.youkuaiyun.com/wangyan9110/article/details/70953273/

1.一个订单创建接口,第一次调用超时了,然后调用方重试了一次
2.在订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次
3.当这笔订单开始支付,在支付请求发出之后,在服务端发生了扣钱操作,接口响应超时了,调用方重试了一次
4.一个订单状态更新接口,调用方连续发送了两个消息,一个是已创建,一个是已付款。但是你先接收到已付款,然后又接收到了已创建
5.在支付完成订单之后,需要发送一条短信,当一台机器接收到短信发送的消息之后,处理较慢。消息中间件又把消息投递给另外一台机器处理

以上问题,就是在单体架构转成微服务架构之后,带来的问题。当然不是说单体架构下没有这些问题,在单体架构下同样要避免重复请求。但是出现的问题要比这少得多。

为了解决以上问题,就需要保证接口的幂等性,接口的幂等性实际上就是接口可重复调用,在调用方多次调用的情况下,接口最终得到的结果是一致的。有些接口可以天然的实现幂等性,比如查询接口,对于查询来说,你查询一次和两次,对于系统来说,没有任何影响,查出的结果也是一样。

实现幂等的方案分别是:全局唯一ID,去重表,多版本控制

全局唯一ID

如果使用全局唯一ID,就是根据业务的操作和内容生成一个全局ID,在执行操作前先根据这个全局唯一ID是否存在,来判断这个操作是否已经执行。如果不存在则把全局ID,存储到存储系统中,比如数据库、redis等。如果存在则表示该方法已经执行。

从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了(指服务挂了,这时候就要重新执行),这就需要引入全局ID的超时机制。利用Hash结构来放全局ID,因为它的添加,查找,删除的时间复杂度都是O(1)

去重表

这种方法适用于在业务中有唯一标的插入场景中,比如在以上的支付场景中,如果一个订单只会支付一次,所以订单ID可以作为唯一标识。这时,我们就可以建一张去重表,并且把唯一标识作为唯一索引,在我们实现时,把创建支付单据和写入去去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。
但是我觉得这种方式比全局唯一ID的性能差点,因为要经过一次数据库操作.

多版本控制

这种方法适合在更新的场景中,比如我们要更新商品的名字,这时我们就可以在更新的接口中增加一个版本号,来做幂等

boolean updateGoodsName(int id,String newName,int version);

在实现时可以如下

update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

### 等性的重要性 在微服务架构中,等是一致性方面的重要概念。一个操作的特点是指其任意多次执行所产生的影响均与一次执行的影响相同[^2]。 ### 实现等性的方法 #### 使用唯一标识符 一种常见的实现方式是在服务器端生成唯一的请求序列号(Sequence),客户端使用这个序列号作为操作的凭据去实现方法内的操作[^3]。具体来说: - 客户端发起请求时附带一个全局唯一的`idempotency-key` - 服务端接收到请求后先检查此键是否已存在 - 如果不存在则正常处理业务逻辑并记录该键;如果已经存在,则直接返回上次的结果而不做任何更改 这种方法能够有效防止网络抖动等原因造成的重复提交问题。 ```python def handle_request(request_id, data): if is_duplicate(request_id): return get_previous_result(request_id) result = process_data(data) save_result_and_mark_as_processed(request_id, result) return result ``` #### 数据库层面的设计 除了应用层面上的做法外,在数据库设计上也可以采取措施来保障某些特定场景下的等性。比如通过设置唯一索引来避免数据重复插入等问题的发生。 ### 分布式环境下的挑战 考虑到分布式系统的特性,在高并发情况下还需要考虑如何保证跨节点间的一致性。此时可以引入像Redis这样的缓存组件配合分布式锁机制共同工作以达到目的[^1]。 例如利用Redisson提供的API轻松地实现在多个实例之间共享同一把锁的功能从而确保只有一个进程能成功获得锁定进而完成相应任务。 ```java RLock lock = redissonClient.getLock("myDistributedLock"); try { boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); if (isLocked){ // 执行关键代码片段... } } finally { lock.unlock(); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值