Java接口幂等性设计原理解析

作者:随风而逝,只是飘零 时间:2022-12-22 12:27:01 

在微服务架构下,我们在完成一个订单流程时经常遇到下面的场景:

  • 一个订单创建接口,第一次调用超时了,然后调用方重试了一次

  • 在订单创建时,我们需要去扣减库存,这时接口发生了超时,调用方重试了一次

  • 当这笔订单开始支付,在支付请求发出之后,在服务端发生了扣钱操作,接口响应超时了,调用方重试了一次

  • 一个订单状态更新接口,调用方连续发送了两个消息,一个是已创建,一个是已付款。但是你先接收到已付款,然后又接收到了已创建

  • 在支付完成订单之后,需要发送一条短信,当一台机器接收到短信发送的消息之后,处理较慢。消息中间件又把消息投递给另外一台机器处理

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

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

除了查询功能具有天然的幂等性之外,增加、更新、删除都要保证幂等性。那么如何来保证幂等性呢?

全局唯一ID

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

从工程的角度来说,使用全局ID做幂等可以作为一个业务的基础的微服务存在,在很多的微服务中都会用到这样的服务,在每个微服务中都完成这样的功能,会存在工作量重复。另外打造一个高可靠的幂等服务还需要考虑很多问题,比如一台机器虽然把全局ID先写入了存储,但是在写入之后挂了,这就需要引入全局ID的超时机制。

使用全局唯一ID是一个通用方案,可以支持插入、更新、删除业务操作。但是这个方案看起来很美但是实现起来比较麻烦,下面的方案适用于特定的场景,但是实现起来比较简单。

去重表

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

插入或更新

这种方法插入并且有唯一索引的情况,比如我们要关联商品品类,其中商品的ID和品类的ID可以构成唯一索引,并且在数据表中也增加了唯一索引。这时就可以使用InsertOrUpdate操作。在mysql数据库中如下:

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}

状态机控制

这种方法适合在有状态机流转的情况下,比如就会订单的创建和付款,订单的付款肯定是在之前,这时我们可以通过在设计状态字段时,使用int类型,并且通过值类型的大小来做幂等,比如订单的创建为0,付款成功为100。付款失败为99

在做状态机更新时,我们就这可以这样控制

update `order` set status=#{status} where id=#{id} and status<#{status}

来源:https://www.cnblogs.com/zxf330301/p/10079997.html

标签:Java,接口,幂,设计
0
投稿

猜你喜欢

  • SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)推荐

    2023-01-21 13:58:45
  • 使用fileupload组件实现文件上传功能

    2023-05-04 02:48:06
  • intellij idea中spring boot properties文件不能自动提示问题解决

    2021-09-24 09:53:46
  • C#实现的海盗分金算法实例

    2023-12-20 21:00:53
  • java后台接受到图片后保存方法

    2023-06-03 09:23:04
  • C#实现文件夹的复制和删除

    2023-02-18 00:54:22
  • Android中使用ViewStub实现布局优化

    2023-11-28 21:16:18
  • Java数据结构与算法之稀疏数组与队列深入理解

    2022-05-31 06:33:11
  • Android基本游戏循环实例分析

    2021-12-26 12:06:22
  • Java代码精简之道(推荐)

    2023-07-28 02:00:05
  • 基于Java生成GUID的实现方法

    2022-04-09 02:44:09
  • SpringBoot整合Web开发之Json数据返回的实现

    2023-04-27 05:06:51
  • Android中LinearLayout布局的常用属性总结

    2023-11-23 17:09:37
  • 如何让C#、VB.NET实现复杂的二进制操作

    2023-10-14 19:22:52
  • java获取Date时间的各种方式汇总

    2021-12-07 16:36:16
  • Java常见面试题之多线程和高并发详解

    2023-07-24 09:33:51
  • java selenium教程环境搭建基于Maven

    2023-11-27 01:35:38
  • Springboot @Validated和@Valid的区别及使用详解

    2023-05-30 18:40:25
  • Java JDK 二分法 分析demo(推荐)

    2022-02-28 23:29:21
  • MTK Android平台开发流程

    2023-06-23 08:59:11
  • asp之家 软件编程 m.aspxhome.com