在微服务架构下,咱们在完成一个订单流程时常常遇到下面的场景:java
- 一个订单建立接口,第一次调用超时了,而后调用方重试了一次
- 在订单建立时,咱们须要去扣减库存,这时接口发生了超时,调用方重试了一次
- 当这笔订单开始支付,在支付请求发出以后,在服务端发生了扣钱操做,接口响应超时了,调用方重试了一次
- 一个订单状态更新接口,调用方连续发送了两个消息,一个是已建立,一个是已付款。可是你先接收到已付款,而后又接收到了已建立
- 在支付完成订单以后,须要发送一条短信,当一台机器接收到短信发送的消息以后,处理较慢。消息中间件又把消息投递给另一台机器处理
以上问题,就是在单体架构转成微服务架构以后,带来的问题。固然不是说单体架构下没有这些问题,在单体架构下一样要避免重复请求。可是出现的问题要比这少得多。mysql
为了解决以上问题,就须要保证接口的幂等性,接口的幂等性实际上就是接口可重复调用,在调用方屡次调用的状况下,接口最终获得的结果是一致的。有些接口能够自然的实现幂等性,好比查询接口,对于查询来讲,你查询一次和两次,对于系统来讲,没有任何影响,查出的结果也是同样。redis
除了查询功能具备自然的幂等性以外,增长、更新、删除都要保证幂等性。那么如何来保证幂等性呢?sql
若是使用全局惟一ID,就是根据业务的操做和内容生成一个全局ID,在执行操做前先根据这个全局惟一ID是否存在,来判断这个操做是否已经执行。若是不存在则把全局ID,存储到存储系统中,好比数据库、Redis等。若是存在则表示该方法已经执行。数据库
从工程的角度来讲,使用全局ID作幂等能够做为一个业务的基础的微服务存在,在不少的微服务中都会用到这样的服务,在每一个微服务中都完成这样的功能,会存在工做量重复。另外打造一个高可靠的幂等服务还须要考虑不少问题,好比一台机器虽然把全局ID先写入了存储,可是在写入以后挂了,这就须要引入全局ID的超时机制。架构
使用全局惟一ID是一个通用方案,能够支持插入、更新、删除业务操做。可是这个方案看起来很美可是实现起来比较麻烦,下面的方案适用于特定的场景,可是实现起来比较简单。微服务
这种方法适用于在业务中有惟一标的插入场景中,好比在以上的支付场景中,若是一个订单只会支付一次,因此订单ID能够做为惟一标识。这时,咱们就能够建一张去重表,而且把惟一标识做为惟一索引,在咱们实现时,把建立支付单据和写入去去重表,放在一个事务中,若是重复建立,数据库会抛出惟一约束异常,操做就会回滚。spa
这种方法插入而且有惟一索引的状况,好比咱们要关联商品品类,其中商品的ID和品类的ID能够构成惟一索引,而且在数据表中也增长了惟一索引。这时就可使用InsertOrUpdate操做。在MySQL数据库中以下:.net
insert into goods_category (goods_id,category_id,create_time,update_time) values(#{goodsId},#{categoryId},now(),now()) on DUPLICATE KEY UPDATE update_time=now()
boolean updateGoodsName(int id,String newName,int version);
在实现时能够以下设计
update goods set name=#{newName},version=#{version} where id=#{id} and version<${version}
在作状态机更新时,咱们就这能够这样控制
update `order` set status=#{status} where id=#{id} and status<#{status}
以上就是保证接口幂等性的一些方法。
/** * ————————若是以为本博文还行,别忘了推荐一下哦,谢谢! * 做者:写程序的奥特曼 * 欢迎转载,请保留此段声明。 * 出处:https://my.oschina.net/u/2286631/blog/1504672 */