completefuture造成的rpc重试事故
前言
最近经历了一个由于 completefuture
的使用,导致RPC重试机制触发而引起的重复写入异常的生产bug。复盘下来,并非是错误的使用了completefuture,而是一些开发时很难意识到的坑。
背景
用户反馈通过应用A使用ota批量升级设备时存在概率性失败的可能;
功能的运行流程如下:
- 应用A调用应用B的rpc接口
- 应用B将请求发布至mqtt
- 设备订阅接收,开始进行ota升级
通过复盘设备端以及后台的调用日志得知,设备端在相同时间戳或毫秒级相差的时间戳内收到了两条相同的指令,后台日志中也可以找到对应的消息发送日志。
那么这就是一个消息被重复发送的问题,一般有两种情况:
- rpc接口被多次调用
- 发布消息时出现重复发送
考虑到mqtt的qos特殊性,短暂的将qos=0
,即不存在mqtt重发机制,依然会出现重复发送问题;
结合后台的接口调用日志后,可以确认是应用A重复调用了rpc接口。
复盘
在定位到是后台重复调用rpc接口问题后,解决与排查方式也就变得透彻了。
首先是查看代码:经过排查以及debug,应用A只是简单的业务方调用接口,并且由于app上有防触和后台接口限流处理,排除应用A的功能开发问题;
问题只可能出现在 调用rpc 与 应用B接收与返回 两个动作上;
熟悉远程调用服务的同学应该明白,rpc接口调用,特别是基于dubbo-注册中心这样的传统调用方式,是存在默认的失败熔断、降级,以及造成这次事故的罪魁祸首 异常重试机制
。
重试机制:
在分布式接口调用场景中,上游方调用接口,为保证其接口的高可用性,会配置无感的重试时间以及重试策略用来抵御当网络波动,请求丢失,异常等问题时的接口可用性
由于应用B是中心类应用,是很多服务的下游应用,所以针对接口的高可用的设计都将其考虑到了正常调用的范畴中。
因此未防止应用A发起请求后,出现由于应用B的网络波动或业务内部的长链路导致的超时而出现重试调用的问题,业务B中采用了以下的执行器方式进行具体消息的发布:
public static void main(String[] args) {System.out.println("我已经收到了:"+t());}public static void rpcInterface(){Executor executor = new ThreadPoolExecutor(1, 2, 1L,TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {System.out.println("业务开始,时间:"+System.currentTimeMillis());try {Thread.sleep(1000L);} catch (InterruptedException e) {}System.out.println("开启供应链头,组装消息");System.out.println("组装");String message = "message";return message;}, executor);completableFuture.whenComplete((message, exception) -> {System.out.println("消息发送:" + message);});return "ok";}
当应用B的接口判断为处理时间不可控、非查询、消息发布等特殊接口时,会通过以上处理,将实际处理动作线程与rpc接口调用的返回分割开。
比如以上代码执行结果为:
出现问题
在排查过程中,猜测一定是CompletableFuture运行中出现了阻塞,导致返回 ok
的时间超过配置的超时时间而发生重试;
往这个方向考虑结果就很清晰了:
CompletableFuture发生阻塞,再次请求rpc接口,这时CompletableFuture运行,第一次与第二次请求同时进行了消息发布动作;
这里先提应用B在此处线程池的设计与使用了:超时时间3S,有边界队列,拒绝策略为线程等待或主线程执行;
在经过压测后并未发现问题,于是在次接口处理中同样使用了该线程池;
但是,批量ota升级这个动作有些业务上的特殊,会导致任务入队到执行的时间比预想中的要长;
因此这里出现阻塞的原因通过一步步排查得出结论为:
1、多个地方使用同一线程池,而最大线程数未扩容;
2、业务内部设计不合理,出现预料外的慢业务链路,导致占满
结论
这就像是一个陌生的同事接手了一个业务,然后模仿其他相识接口的开发copy
了相同的线程池执行器,然后一股脑的进行套用;
最终出现了这种在测试环境很难出现的问题,因为本地网络加上测试环境线程充足的原因,并且因为相同的线程执行器所以也未考虑到经过压测;
不过回顾这次事故本身,问题与解决很简单,可以算是不熟悉系统导致的bug。但是从另一个角度上看,其实完全可以从源头上避免掉这种重复调用rpc接口的bug出现。
接口幂等
处理重复调用,即对接口进行幂等性;
并非所有的rpc接口都需要对接口做幂等处理,对于非订单操作,db生成的功能,仅查询是无所谓重复调用的。
不过还是需要结合实际考虑,因为本次事故的接口中也是考虑到线程的分离也就没注意对接口进行幂等;
rpc接口幂等有三种通用方案:
方案一:
请求方请求时创建对应接口规则的分布式锁,下游方针对该锁作本次请求的一次调用
方案二:
结合重试时间对接口进行同一请求,几秒内请求n次的限制
方案三:
前两者是比较自定义式的在接口的入口处进行幂等的处理方式;
在spring项目中,我们还可以通过aop组件去实现一个基于自定义注解的接口增强;
我们可以设计一个公共的sdk包common,在其中实现接口幂等组件的装配;
实现方式也很简单:
@Aspect
@Component
public class InterfacelimitAspect {@Around("@annotation(limitInterface)) ")public Object limit(ProceedingJoinPoint point, VoiceEnter voiceEnter) throws Throwable {// 组成唯一的业务id point.getArgs();//或使用traceIdboolean is =localCache.get(id);if(is) //判断是否已经被执行 return;Object proceedResult = point.proceed();return proceedResult;}
}
版权声明:本站原创文章,于2024-04-03,乐云一发表
转载请注明:https://leyunone.com/normal-notes/rpc-reload.html
相关文章:

completefuture造成的rpc重试事故
前言 最近经历了一个由于 completefuture 的使用,导致RPC重试机制触发而引起的重复写入异常的生产bug。复盘下来,并非是错误的使用了completefuture,而是一些开发时很难意识到的坑。 背景 用户反馈通过应用A使用ota批量升级设备时存在概率…...

6月11号作业
思维导图 #include <iostream> using namespace std; class Animal { private:string name; public:Animal(){}Animal(string name):name(name){//cout << "Animal;有参" << endl;}virtual void perform(){cout << "讲解员的…...
探究Vue源码:深入理解diff算法
前言 在Vue中 组件初次渲染时,会调用 render 函数生成初始的虚拟 DOM 树。 当组件的状态发生变化时,Vue 会重新调用 render 函数生成新的虚拟 DOM 树。 而Diff 算法是用来比较新旧虚拟 DOM 树的差异,并且只对差异部分进行更新的算法,从而尽量…...
qt自适应图片
在 Qt 中,通过重写 paintEvent 方法来添加自适应背景图片的过程如下: 创建一个自定义的 QWidget 子类。重写 paintEvent 方法,在该方法中使用 QPainter 绘制背景图片。使用 QPixmap 加载图片,并调整图片的大小以适应窗口的大小。…...

【区块链】解码拜占庭将军问题:区块链共识机制的哲学基石
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 解码拜占庭将军问题:区块链共识机制的哲学基石引言一、拜占庭将军问…...

MCK主机加固:智能科技,构筑网络安全的铜墙铁壁
在数字化转型的浪潮中,企业服务器的安全已成为维护业务连续性和保护数据资产的关键。MCK主机加固产品,以其创新技术,为企业提供了一个全面、智能、高效的安全解决方案。 一、智能安全监测 MCK主机加固产品采用深度学习算法,能够…...

OpenCV 双目相机标定
文章目录 一、简介1.1单目相机标定1.2双目相机标定二、实现代码三、实现效果参考资料一、简介 1.1单目相机标定 与单目相机标定类似,双目标定的目的也是要找到从世界坐标转换为图像坐标所用到的投影P矩阵各个系数(即相机的内参与外参)。具体过程如下所述: 1、首先我们需要…...

WPF/C#:异常处理
什么是异常? 在C#中,异常是在程序执行过程中发生的特殊情况,例如尝试除以零、访问不存在的文件、网络连接中断等。这些情况会中断程序的正常流程。 当C#程序中发生这种特殊情况时,会创建一个异常对象并将其抛出。这个异常对象包…...

2024年跨平台应用解决方法
个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 很久没有写这类high-level的文章了,本身这类框架就一直层出不穷,但是其中历久弥坚,坚韧不拔的框架又有多少呢? 首先考虑到学习成本以及掌握一些编程语言在工作、学习生态上的价值,给这些东西适用生态划分一下. Reac…...

人工智能ChatGPT的多种应用:提示词工程
简介 ChatGPT 的主要优点之一是它能够理解和响应自然语言输入。在日常生活中,沟通本来就是很重要的一门课程,沟通的过程中表达的越清晰,给到的信息越多,那么沟通就越顺畅。 和 ChatGPT 沟通也是同样的道理,如果想要 …...
OceanBase v4.2 解读:tenant=all 语义优化,提升易用性
1 背景 1.1 租户类型及特点 OceanBase中有三种类型的租户: sys租户:集群默认创建,生命周期与集群相一致,管理集群和其他租户,具有较高的地位。用户租户:用户创建的业务租户或普通租户,用于运…...
理论和实验
一、理论和实验的关系 (一)理论可以指导实验 理论家提出理论和猜想,实验家就可以做个实验来验证是否适用。 (二)实验可以提升理论认识 实验家通过做实验,观察实验过程和结果后,如果发现和理论预测有误差,那么理论家就能根据新发现…...
Linux 常用命令 - userdel 【删除用户】
简介 userdel 这个命令源自于 “user delete”,即用户删除。这个命令主要用于在 Linux 系统中删除用户账户及其相关文件。当管理员需要移除一个用户及其在系统中的所有踪迹时,会用到这个命令。 使用方式 userdel [选项] 用户名常用参数 -f:强制删除用户,即使用户当前已登…...
等保测评和安全运维
# 等保测评与安全运维:构建企业网络安全的双重保障 引言 在数字化时代,企业面临着日益复杂的网络安全威胁。为了应对这些挑战,企业不仅要实施有效的安全运维措施,还需要通过等保测评确保其信息系统符合国家的安全标准。本文将探讨…...

Java课程设计:基于Java+Swing+MySQL的图书管理系统(内附源码)
文章目录 一、项目介绍二、项目展示三、源码展示四、源码获取 一、项目介绍 图书管理系统是一个常见的软件项目,广泛应用于图书馆、学校、企业等需要管理图书资源的场景。该系统通常涵盖图书信息录入、查询、借阅、归还等核心功能,是实现图书资源高效管理的重要工具。 随着信…...
WireGuard网络架构及配置详解
WireGuard网络架构及配置详解 一.点对点二.中心网关,实现nat穿透弊端:流量全部经过中心网关,带宽上限受限于中心网关 三.借助registry实现双向nat穿透需要借助registry实现 udp打洞, 待二开 一.点对点 yum install epel-release elrepo-release -y yum install yum-plugin-elr…...

VB.NET实现上位机自动识别可用串口
在实际应用中有时会牵扯到挑选可用串口,比如上位机和从站设备使用Modbus RTU协议进行通讯时需要选择COM串口,每次启动连接前都在设备管理器查看较为麻烦,可以设置一个串口自动识别功能,如果选择了错误的串口还可以提示串口选择错误…...

Node.js版本管理工具-NVM
在开发 Node.js 项目时,经常会遇到需要切换不同版本的 Node.js 的情况。为了方便管理和切换各个版本,我们可以使用一些 Node.js 版本管理工具。 Node Version Manager:简称NVM,最流行的 Node.js 版本管理工具之一。它允许我们在同…...
【react】useEffect 快速上手
useEffect 快速上手 useEffect(setup, dependencies?) 可以接收两个参数,分别是回调函数与依赖数组. useEffect 用什么姿势来调用,本质上取决于你想用它来达成什么样的效果。下面我们来简单介绍 useEffect 的调用规则。 每一次渲染后都执行的副作用&a…...
docker容器部署jenkins
提前安装好jdk和maven,jdk最好使用11版本,jdk-11.0.10 docker run -u root -d \ -p 100:8080 \ -v /var/jenkins_home/workspace/:/var/jenkins_home/workspace/ \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
flow_controllers
关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...