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));}// 其他操作}
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CUDA】windows下的CUDA安装

一、前言 windows10 下安装 CUDA ,首先需要下载安装包: CUDA toolkit(工具包) 二、安装前的准备 电脑环境检查 通过cmd,输入nvidia-smi 查看自己的驱动版本和支持的CUDA版本,如下图红圈标记位置 下载 …

经典问题 | 线程打印ABA问题

问题描述 有三个线程,分别命名为 ThreadA、ThreadB 和 ThreadC。请设计一个程序,使得它们循环打印输出字符串 "A"、"B"、"C",打印顺序为 "ABCABCABC..."。要求使用线程同步机制确保按照指定顺序打印…

尤雨溪:框架挖坑靠文档来补,这算 PUA 用户吗?丨 RTE 开发者日报 Vol.122

开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE (Real Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

JWT令牌(Token)设计

JWT(JSON Web Token)是一种基于开放标准的令牌(Token),用于在不同实体之间传递和验证信息。它由三部分组成:头部(Header)、载荷(Payload)和签名(S…

Centos Unable to verify the graphical display setup

ERROR: Unable to verify the graphical display setup. 在Linux下安装Oracle时 运行 ./runInstaller 报错 ERROR: Unable to verify the graphical display setup. This application requires X display. Make sure that xdpyinfo exist under PATH variable. No X11 DISPL…

使用Python脚本自动发送个性化微信消息通知

需求: 需要向单位同事定期(每周一次)发送每个人的业务情况提醒,同时也要发送新的登录token。如果采用邮件大家也不常看,提醒效果很差,同时邮件传输非常不安全,单位很多人邮箱默认密码不改&…

使用开源通义千问模型(Qwen)搭建自己的大模型服务

目标 1、使用开源的大模型服务搭建属于自己的模型服务; 2、调优自己的大模型; 选型 采用通义千问模型,https://github.com/QwenLM/Qwen 步骤 1、下载模型文件 开源模型库:https://www.modelscope.cn/models mkdir -p /data/…

微信小程序中组件内生命周期如何调用组件内方法

在组件的.js文件中使用 Component({/*** 组件的属性列表*/properties: {},/*** 组件的初始数据*/data: {},/*** 组件的方法*/methods:{async getData() {},},/*** 组件的生命周期*/lifetimes:{// 在组件实例进入页面节点树时执行attached:function(){this.getData() //调用}} …

JavaScript-数组-笔记

1.定义数组 数组: 可以存储任意数据类型,元素之间使用英文逗号隔开 1)使用new关键字定义数组 var 变量名 new Array() ;定义空数组 var 变量名 new Array(数据1,数据2) ; 定义有元素的数组 注意&#xff…

Jmeter 性能 —— 吞吐量与并发用户数关系!

1、吞吐量和并发用户数的关系 2、已知在线用户数为3000,算出对应的并发用户数 ①我们常用的确定并发用户数的公式是: C活动用户数操作时间/系统运行时间 如每天最大在线用户数为3000人,每个用户平均操作时间为1小时,系统运行时…

【后退N帧协议】- 协议应用与局限分析

后退N帧协议是计算机网络通信中常用的一种流量控制协议,用于确保数据的可靠传输。本文将深入探讨后退N帧协议的原理、应用场景及局限性。 后退N帧协议的核心思想是在发送端发送数据时,等待接收端返回的确认信号。发送端每次发送N帧数据,并等…

零基础学Python网络爬虫案例实战 全流程详解 高级进阶篇

零基础学Python网络爬虫案例实战 全流程详解 入门与提高篇 零基础学Python网络爬虫案例实战 全流程详解 高级进阶篇 编辑推荐 本书讲解了Python爬虫技术的高级进阶知识,帮助有一定爬虫基础的读者进一步提高爬虫技术。本书详解了突破反爬机制的常用手段以及Scrapy和…

基于SSM+Vue的学校社团管理系统(Java毕业设计)

大家好,我是DeBug,很高兴你能来阅读!作为一名热爱编程的程序员,我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里,我将会结合实际项目经验,分享编程技巧、最佳实践以及解决问题的方法。无论你是…

工业相机——靶面尺寸、像元尺寸、分辨率 、传感器尺寸

文章目录 1 靶面尺寸、像元尺寸、分辨率2 相机传感器的感光区尺寸规格1 靶面尺寸、像元尺寸、分辨率 工业相机基本知识理解:靶面尺寸、像元尺寸、分辨率 1、靶面尺寸:由Sensor对角线长度表示,单位英寸,这里的1英寸=16mm 2、像元尺寸:单个感光元件的大小,一般都是正方形…

C# OpenCvSharp DNN FreeYOLO 人脸检测人脸图像质量评估

目录 效果 模型信息 yolo_free_huge_widerface_192x320.onnx face-quality-assessment.onnx 项目 代码 frmMain.cs FreeYoloFace FaceQualityAssessment.cs 下载 C# OpenCvSharp DNN FreeYOLO 人脸检测&人脸图像质量评估 效果 模型信息 yolo_free_huge_widerfa…

Qt隐式共享浅析

一、什么是隐式共享 Qt 的隐式共享(implicit sharing)机制是一种设计模式,用于在进行数据拷贝时提高效率和减少内存占用。 在 Qt 中,许多类(如 QString、QList 等)都使用了隐式共享机制。这意味着当这些类…

色标在matplotlib和plottable中

是这样的,我有一个数组[-4.4, -2.8, -2.6, -2.2, -1.1, 1.1, 1.2, 1.3, 3.6, 6.0, 6.4, 12.3],它需要绘制散点图,点的颜色来代表数值大小;同时,也需要在plottable上作为一列显示,同样用颜色来代表数值的大小…

HarmonyOS应用开发学习笔记 ArkTS 布局概述

一、布局概述 布局指用特定的组件或者属性来管理用户页面所放置UI组件的大小和位置。在实际的开发过程中,需要遵守以下流程保证整体的布局效果 确定页面的布局结构。分析页面中的元素构成。选用适合的布局容器组件或属性控制页面中各个元素的位置和大小约束。 二…

【JUC】进程和线程

目录 📢什么是进程?🎡什么是线程?🚀进程和线程的区别?🎢Java 线程和操作系统的线程有啥区别?🎖️JDK21的虚拟线程🎯虚拟线程和平台线程的对比 📢什么是进程? 进程是程序的一次执…

Zabbix“专家坐诊”第223期问答汇总

来源:乐维社区 问题一 Q:Zabbix 5.0安装完mysql之后怎么备份?忘记mysql当时创建的密码了,怎么样能查看设置的密码? A:mysql初始化密码在 /var/log/mysqld.log中可以看到,搜关键字temporary pas…