java编程中,保证接口幂等性的实现方案讨论

一、什么是幂等性

数学中的幂等是指f(x) = f(f(x)),编程领域的术语是指同一个操作,在重复提交的情况下,最终产生的影响是不变的。举例说:

  • 提交订单时,用户在购物车界面,重复点击“下单”,服务端有且只会创建一个订单。(客户端在做交互设计的时候,用户点击了“下单”就不能再次点击,立马把按钮置灰。但是,这里会有网络的重试或者弱网等异常,导致服务端可能接收到两次http请求。如果服务端没有对http接口做幂等,那么可能会生成两个重复的订单)
  • 用户支付完成后,第三方支付会回调服务端的支付回调接口,服务端然后更新支付订单的状态,最后回调通知其他系统,比如订单系统和积分系统。这里的幂等性是指无论被回调多少次,不会重复更新支付订单的状态和回调通知其他系统。

总结: 对于重要的接口,在实现接口时,一定要考虑是否具备幂等性。

既然不是所有的接口,那都有哪些场景的接口,会需要考虑幂等性呢?

二、哪些场景需要实现幂等

1、异步回调

比如上文说的支付回调接口,也包括在其他的异步操作下的对外接口。
你都必须考虑是否支持幂等。
举个例子,课程服务调用课堂服务的批量创建课堂接口,由于后者是异步的操作,所以待创建完成,课堂服务将要回调通知课程服务。
于是,课程服务需要开放一个接口出来,此接口就必须支持幂等。

2、消息队列

有些业务,需要削峰填谷,会先把异步操作的消息给到Mq,然后在监听方法中消费该mq消息。这里举一个异步复制某文件夹及其下的所有文件为例。

3、网络重试

当出现网络超时,需要重试的时候,你的接口是否支持幂等呢?
比如openfeign就很好地支持retry机制。

三、哪些是天然幂等的

在进一步阐述如何实现幂等前,我们有必要搞清楚,哪些是天然幂等的。

都说自己每天写的代码都是CRUD,我们现在就来分析下它们是否幂等。

  • 查询方法,都说查询的实现往往是最复杂的代码,幸好,它就是幂等的。虽然你前后的查询返回结果可能不一样,然而并不会影响服务端的分毫。

  • 基于版本号或者状态机的更新操作,具体到sql语句,有两种写法,见下:

# 不建议的写法
update PayTrade t set t.status = :newStatus, t.outTradeNo = :outTradeNo, t.payOkDate = :payOkDate where t.channelTradeNo = :channelTradeNo

# 建议你这么写
update PayTrade t set t.status = :newStatus, t.outTradeNo = :outTradeNo, t.payOkDate = :payOkDate where t.channelTradeNo = :channelTradeNo and t.status = :oldStatus

他们的区别就是是否要求前一个状态,后面的sql多了一个“ t.status = :oldStatus ”

我们说,它就是一个幂等操作,而上面的写法则不建议,它不是幂等的。

这里举例了基于状态机的更新,基于版本号的类似,就不一一赘述了。

  • 最后说一说CRUD的删除操作D,它也是天然幂等的。因为删除了的数据,第二次想要再删除而不得了。

  • 大多数的新增操作,都需要最引起你的重视,极其容易导致重复操作。

四、设计思路

1、请求唯一标识

可以是一个随机值token,也可以是业务上的一个字段。
前者是客户端根据约定生成的一个值,这个一般使用userId+时间戳,后者则是业务上的唯一值,比如订单号、课程编号等。

那么随机token,可不可以由服务端来颁发给客户端呢?

也行,原本一个接口就能完成的,拆分成了两个接口。(增加了一个颁发token的接口)

下文将详细说明,如果使用分布式锁实现接口的幂等。

2、每次操作前,先查询

订单服务接收支付服务的支付回调,先根据订单号查询订单详情,如果数据库返回支付流水号和回调接口中的入参支付流水号相同,则忽略该回调,不做任何后续操作。

下面展示一下其简略代码:

final Order order = orderRepository.findByNo(orderPaymentMarkRequest.getOrderNo());

// 如果流水号一样,认为曾经标记支付成功,保持幂等
if (!StringUtils.equals(order.getPayNo(), orderPaymentMarkRequest.getPayNo())) {
   // 业务处理,更新订单等等
}


public class OrderPaymentMarkRequest {

    @ApiModelProperty(notes = "订单号")
    private String orderNo;
    
    @ApiModelProperty(notes = "平台支付流水号")
    private String payNo;
}

3、使用乐观锁实现

引入状态机或者版本号等机制,前文已有举例,这里不再重复。

五、分布式锁实现幂等

在这里插入图片描述

代码示例:

    @Lock(name = "CourseCenter", key = "'createLecture:token:'+ #request.token")
    @Transactional(rollbackFor = Throwable.class)
    public ApiResult createLecture(LectureCreateRequest request) {
        if (log.isInfoEnabled()) {
            log.info("创建讲次,入参是{}", JsonUtils.toJsonString(request));
        }
        // 其他操作
    }
}

这里使用了自定义注解封装分布式锁,所以代码本身比较简单。

鉴于篇幅,具体实现分布式锁,网上也比较多的实现方案。

本文就不具体展开论述了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/304731.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vxe-树形结构层级过多横向滚动条设置

一. 问题 在使用vxe-table中的树形结构的时候,如果树形结构的层级过多的话的会导致层级很深的层级,鼠标点击无法选中,对应的内容显示不完全。 层级过多后 二. 解决 方法1:动态的计算所占行的宽度,选取最大宽度给表格列…

统信UOS虚拟机安装VirtualBox扩展使用USB功能

为什么要安装VirtualBox扩展包? 安装 Oracle VM VirtualBox 扩展包的原因是,它提供了对 USB 2.0、USB 3.0、远程桌面协议 VRDP(VirtualBox Remote Desktop Protocol)等实用功能的支持,以增强 VirtualBox 的功能。这些…

MIT_线性代数笔记:第 25 讲 对称矩阵和正定性

目录 对称矩阵 Symmetric matrices实特征值 Real eigenvalues正定矩阵 Positive definite matrices 对称矩阵是最重要的矩阵之一,其特征值为实数并且拥有一套正交特征向量。正定矩阵的性质则更好。 对称矩阵 Symmetric matrices 包含特殊性质的矩阵,例如…

CCF模拟题 202309-1 坐标变换(其一)

问题描述 试题编号: 202309-1 试题名称: 坐标变换(其一) 时间限制: 1.0s 内存限制: 512.0MB 问题描述: 对于平面直角坐标系上的坐标(x,y),小P定义了一个包含…

【Flutter 开发实战】Dart 基础篇:常用运算符

在Dart中,运算符是编写任何程序的基本构建块之一。本文将详细介绍Dart中常用的运算符,以帮助初学者更好地理解和运用这些概念。 1. 算术运算符 算术运算符用于执行基本的数学运算。Dart支持常见的加、减、乘、除、整除以及取余运算。常见的算数运算符如…

从虚拟到现实:数字孪生驱动智慧城市可持续发展

随着科技的飞速发展,智慧城市已经成为未来城市发展的重要趋势。数字孪生技术作为智慧城市建设中的关键技术之一,正在发挥着越来越重要的作用。本文将探讨数字孪生如何从虚拟走向现实,驱动智慧城市的可持续发展。 一、数字孪生技术&#xff1…

【CV】计算两个向量的夹角,并使用 OpenCV 可视化弧线

背景 基于人体/动物,骨骼点数据,计算关节角度 1. 原理 计算两个向量的夹角,我们已三个点为例,BA 向量和BC向量,求 B 的角度。若为四个点,延长交叉即可。 2. 效果 效果图如下 3. 核心代码 def compute_vector_angle(a, b, c):"""计算两个向量 ba 和…

Unity中BRP下的深度图

文章目录 前言一、在Shader中使用1、在使用深度图前申明2、在片元着色器中 二、在C#脚本中开启摄像机深度图三、最终效果 前言 在之前的文章中,我们实现了URP下的深度图使用。 Unity中URP下使用屏幕坐标采样深度图 在这篇文章中,我们来看一下BRP下深度…

【PB续命07】JDBC连接达梦数据库

JDBC(Java DataBase Connectivity) 称为Java数据库连接,它是一种用于数据库访问的应用程序API,由一组用Java语言编写的类和接口组成,有了JDBC就可以用同一的语法对多种关系数据库进行访问,而不用担心其数据库操作语言的差异。 有了…

无线网卡怎么连接台式电脑?正确操作步骤分享!

“我在使用电脑时经常都需要用到网络,请问大家在使用无线网卡时怎么将它与台式电脑进行连接的呢?” 使用电脑的用户在进行网上冲浪时都需要先连接网络。如果不想使用网线,无线网卡不仅可以为用户提供网络服务,在使用时该更加灵活和…

实战:低代码表单引擎助力文件上传与数据处理

在当今的信息化时代,数据已成为企业的重要资产。为了更好地管理和利用这些数据,许多企业开始采用表单上传组件来导入和处理数据。通过使用表单上传组件,用户可以方便地将文件上传至系统中,然后进行后续的数据处理和分析。这种方式…

Java零基础教学文档第一篇:JavaSE(3)

接上期后续 本期继续分享尚未结束的JavaSE章节 JavaSE属于初入门阶段,内容很多~ 但很基础, 大家需保持耐心,慢慢的学~ 争取你们学习的速度! 跟上我更新的速度哦~ 今日新篇章 流程控制语句 【主要内容】 if单选结构 if双选结构…

适合培训协会搭建的培训机构管理系统开发方案

一、项目背景与目标 (一)项目背景 培训学校教务管理系统是培训机构数字化管理的必备系统,该系统功能大大提升机构办学的管理效率、提升机构在家长心中的专业度,市面上的培训机构管理系统收费越来越贵,为了给协会内培…

什么情况?DDD 中的每个任务都执行了两次?

最近在使用 COLA 框架自带的异步任务时,发现每次执行异步都执行了两次,如果一些没有做幂等的接口,这样是会有问题的,比如入库操作之类的,就会造成数据重复入库,造成严重 bug。 带着疑惑,开始了…

目标检测-One Stage-YOLOx

文章目录 前言一、YOLOx的网络结构和流程1.YOLOx的不同版本2.Yolox-Darknet53YOLOv3 baselineYolox-Darknet53 3.Yolox-s/Yolox-m/Yolox-l/Yolox-x4.Yolox-Nano/Yolox-Tiny 二、YOLOx的创新点总结 前言 根据前文CenterNet、YOLOv4等可以看出学界和工业界都在积极探索使用各种t…

HackTheBox - Medium - Linux - Shared

Shared Shared 是一台中等难度的 Linux 机器,它具有通向立足点的 Cookie SQL 注入,然后通过对 Golang 二进制文件进行逆向工程并利用两个 CVE 来获得 root shell 来提升权限。 外部信息收集 端口扫描 循例nmap Web枚举 查看证书 看到这个扫了一下vhos…

骑砍战团MOD开发(36)-千人千面

骑砍1战团mod开发-千人千面_哔哩哔哩_bilibili骑砍1战团mod开发-千人千面, 视频播放量 5、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 1、转发人数 0, 视频作者 霸王奉先, 作者简介 csdn:blog.csdn.net/qq_35829452project:gitcode.net/users/qq_35829452,相关视频…

【解决方案】 无法将“pip“项识别为 cmdlet、函数、脚本文件

在当今的软件开发和运维领域,Python已经成为了一个不可或缺的工具。而pip,作为Python的包管理工具,更是Python生态系统中不可或缺的一部分。然而,有时候我们可能会遇到一个令人困扰的问题:无法将“pip”项识别为cmdlet…

【思扬赠书 | 第2期】语义解析为何作为连接自然语言与机器智能的桥梁?

⛳️ 写在前面参与规则!!! ✅参与方式:关注博主、点赞、收藏、评论,任意评论(每人最多评论三次) ⛳️本次送书1~4本【取决于阅读量,阅读量越多,送的越多】 文章目录 01 …

汽车中的ECU、VCU、MCU、HCU

一、ECU是汽车电脑,刷汽车电脑可以提高动力,也可以减低动力,看需求。 简单原理如下。 1.汽车发动机运转由汽车电脑(即ECU)控制。 2.ECU控制发动机的进气量,喷油量,点火时间等,从而…