异常重试

简介: 本文详解RPC框架中的异常重试机制,探讨如何在超时控制、节点避让和幂等性前提下实现安全可靠的重试。通过设置重试白名单、重置超时时间、排除故障节点等策略,提升调用成功率,同时强调业务逻辑需保证幂等性,避免重复操作引发数据问题。

12 | 异常重试:在约定时间内安全可靠地重试
上一讲我讲解了在 RPC 框架中如何设计自适应的负载均衡,其关键点就是调用端收集服务端每个节点的指标数据,再根据各方面的指标数据进行计算打分,最后根据每个节点的分数,将更多的流量打到分数较高的节点上。今天我们就继续下一个话题,讲讲 RPC 框架中的异常重试机制。
为什么需要异常重试?
我们可以考虑这样一个场景。我们发起一次 RPC 调用,去调用远程的一个服务,比如用户的登录操作,我们会先对用户的用户名以及密码进行验证,验证成功之后会获取用户的基本信息。当我们通过远程的用户服务来获取用户基本信息的时候,恰好网络出现了问题,比如网络突然抖了一下,导致我们的请求失败了,而这个请求我们希望它能够尽可能地执行成功,那这时我们要怎么做呢?
我们需要重新发起一次 RPC 调用,那我们在代码中该如何处理呢?是在代码逻辑里 catch 一下,失败了就再发起一次调用吗?这样做显然不够优雅吧。这时我们就可以考虑使用 RPC 框架的重试机制。
RPC 框架的重试机制
那什么是 RPC 框架的重试机制呢?这其实很好理解,就是当调用端发起的请求失败时,RPC 框架自身可以进行重试,再重新发送请求,用户可以自行设置是否开启重试以及重试的次数。那这个机制是如何实现的呢?
服务调用者服务集群负载均衡插件选择一个服务节点服务节点发送请求消息路由分发策略发起请求集群管理器服务节点动态代理(配置为异常重试)服务节点
image.jpeg

还是挺简单的。我们可以回想下 第 11 讲,通过这一讲我们了解到,调用端在发起 RPC 调用时,会经过负载均衡,选择一个节点,之后它会向这个节点发送请求信息。当消息发送失败或收到异常消息时,我们就可以捕获异常,根据异常触发重试,重新通过负载均衡选择一个节点发送请求消息,并且记录请求的重试次数,当重试次数达到用户配置的重试次数的时候,就返回给调用端动态代理一个失败异常,否则就一直重试下去。
RPC 框架的重试机制就是调用端发现请求失败时捕获异常,之后触发重试,那是不是所有的异常都要触发重试呢?
当然不是了,因为这个异常可能是服务提供方抛回来的业务异常,它是应该正常返回给动态代理的,所以我们要在触发重试之前对捕获的异常进行判定,只有符合重试条件的异常才能触发重试,比如网络超时异常、网络连接异常等等。
了解了 RPC 框架的重试机制,那用户在使用异常重试时需要注意哪些问题呢?
比如我刚才提的那个调用场景,当网络突然抖动了一下导致请求超时了,但这个时候调用方的请求信息可能已经发送到服务提供方的节点上,也可能已经发送到服务提供方的服务节点上,那如果请求信息成功地发送到了服务节点上,那这个节点是不是就要执行业务逻辑了呢?是的。
那如果这个时候发起了重试,业务逻辑是否会被执行呢?会的。
那如果这个服务业务逻辑不是幂等的,比如插入数据操作,那触发重试的话会不会引发问题呢?会的。
综上,我们可以总结出:在使用 RPC 框架的时候,我们要确保被调用的服务的业务逻辑是幂等的,这样我们才能考虑根据事件情况开启 RPC 框架的异常重试功能。这一点你要格外注意,这算是一个高频误区了。
通过上述讲解,我相信你已经非常清楚 RPC 框架的重试机制了,这也是现在大多数 RPC 框架所采用的重试机制。
那看到这儿,你觉得这个机制完善了吗?有没有想到连续重试对请求超时时间的影响?继续考虑这样一个场景:我把调用端的请求超时时间设置为 5s,结果连续重试 3 次,每次都耗时 2s,那最终这个请求的耗时是 6s,那这样的话,调用端设置的超时时间是不是就不准确了呢?
如何在约定时间内安全可靠地重试?
我刚才讲到,连续的异常重试可能会出现一种不可靠的情况,那就是连续的异常重试并且每次处理的请求时间比较长,最终会导致请求处理的时间过长,超出用户设置的超时时间。
解决这个问题最直接的方式就是,在每次重试后都重置一下请求的超时时间。当调用端发起 RPC 请求时,如果发送请求发生异常并触发了异常重试,我们可以先判定下这个请求是否已经超时,如果已经超时了就直接返回超时异常,否则就先重置下这个请求的超时时间,之后再发起重试。
那么解决了因多次异常重试引发的超时时间失效的问题,这个重试机制是不是就完全可靠了呢?
我们接着考虑,当调用端设置了异常重试策略,发起了一次 RPC 调用,通过负载均衡选择了节点,将请求消息发送到这个节点,这时这个节点由于负载压力较大,导致这个请求处理失败了,调用端触发了重试,再次通过负载均衡选择了一个节点,结果恰好仍选择了这个节点,那么在这种情况下,重试的效果是否受影响了呢?
当然有影响。因此,我们需要在所有发起重试、负载均衡选择节点的时候,去掉重试之前出现过问题的那个节点,以保证重试的成功率。
那我们现在再完整地回顾一下,考虑了业务逻辑必须是幂等的、超时时间需要重置以及去掉有问题的服务节点后,这样的异常重试机制,还有没有可优化的地方呢?
我刚才讲过,RPC 框架的异常重试机制,是调用端发送请求之后,如果发送失败会捕获异常,触发重试,但并不是所有的异常都会触发重试的,只有 RPC 框架中特定的异常才会如此,比如连接异常、超时异常。
而像服务端业务逻辑中抛回给调用端的异常是不能重试的。那么请你想一下这种情况:服务端的业务逻辑抛给调用端一个异常信息,而服务端抛出这个异常是允许调用端重新发起一次调用的。
比如这个场景:服务端的业务逻辑是对数据库某个数据的更新操作,更新失败则抛出个更新失败的异常,调用端可以再次调用,来触发服务端重新执行更新操作。那这个时候对于调用端来说,它接收到了更新失败异常,虽然是服务端抛回来的业务异常,但也是可以进行重试的。
那么在这种情况下,RPC 框架的重试机制需要怎么优化呢?
RPC 框架是不会知道哪些业务异常能够去进行异常重试的,我们可以加个 重试异常的白名单,用户可以将允许重试的异常加入到这个白名单中。当调用端发起调用,并且配置了异常重试策略,捕获到异常之后,我们就可以采用这样的异常处理策略。如果这个异常是 RPC 框架允许重试的异常,或者这个异常类型存在于可重试异常的白名单中,我们就允许对这个请求进行重试。
所有可能出现的问题,我们排查了一圈下来之后,一个可靠的重试机制就诞生了,如下图所示:
服务调用者服务集群负载均衡插件选择一个服务节点服务节点发送请求消息路由分发策略发起请求集群管理器服务节点动态代理(配置为异常重试)服务节点超时处理策略异常处理策略重试计数
image.jpeg

总结
今天我们讲解了 RPC 框架的重试机制,还有如何在约定时间内进行安全可靠地重试。这个机制是当调用端发起的请求失败时,如果配置了异常重试策略,RPC 框架会捕捉异常,对异常进行判定,符合条件则进行重试,重新发送请求。
在重试的过程中,为了能够在约定的时间内进行安全可靠地重试,在每次触发重试之前,我们需要先判定下这个请求是否已经超时,如果超时了会直接返回超时异常,否则我们需要重置下这个请求的超时时间,防止因多次重试导致这个请求的处理时间超过用户配置的超时时间,从而影响到业务处理的耗时。
在发起重试、负载均衡选择节点的时候,我们应该去掉重试之前出现过问题的那个节点,这样可以提高重试的成功率,并且我们允许用户配置可重试异常的白名单,这样可以让 RPC 框架的异常重试功能变得更加友好。
另外,在使用 RPC 框架的重试机制时,我们要确保被调用的服务的业务逻辑是幂等的,这样才能考虑是否使用重试,这一点至关重要。

相关文章
使用markdown-it对markdown进行一个实时解析
# 引言 大家应该都接触过markdown笔记吧,我们常常见到很多能写文章的网页提供了一个实时的markdown解析的功能----即我们在一侧写下markdown语法,右边能够实时解析,并将解析后的markdown语法渲染到右侧页面上。非常炫酷实用,那么这是如何实现的呢?今天我们来一起探究一下。 # 开始 首先,我们这个演示学习在Vue3+TypeScript下使用的,所以我们预先建议准备一个Vue3+Typescript工程。 对于markdown的解析,我们可以使用`markdown-it`来进行解析。 比如,我们在左侧设置一个输入框,接收用户markdown输入,我们通过`ma
|
8月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
8月前
|
人工智能 Java 数据库
Spring AI
Spring AI 为 Java 生态注入智能,提供统一抽象接口,简化大模型集成,助力开发者高效构建 AI 应用,推动企业智能化转型。
1312 147
|
5月前
|
架构师 Java 数据库
Java开发进阶:从初级工程师到架构师的能力提升路径
本文系统梳理Java开发者从初级工程师到架构师的成长路径,涵盖各阶段技术能力要求与提升方向,强调基础夯实、架构设计、业务理解及软实力培养,为职业进阶提供清晰指引。
296 2
|
9月前
|
JSON 数据格式 开发者
Python 3.10 结构模式匹配:简化条件逻辑的利器
Python 3.10 结构模式匹配:简化条件逻辑的利器
380 91
|
6月前
|
缓存 安全 应用服务中间件
Nginx 基本使用和高级用法详解
Nginx 是高性能 Web 服务器,支持反向代理、负载均衡、SSL 加速与静态资源缓存。本文详解其安装配置、虚拟主机、安全加固、限流、日志管理及 Docker 部署,助你构建高可用、高性能的现代 Web 架构。(238 字)
1065 0
|
9月前
|
人工智能 数据可视化 API
2025大语言模型部署实战指南:从个人笔记本到企业级服务的全栈方案
本文深度解析四大主流大模型部署框架,覆盖个人开发、边缘设备与高并发生产场景。从Ollama、llama.cpp到vLLM与LM Studio,助你精准匹配技术方案,实现高效部署。
|
10月前
|
JSON 数据建模 定位技术
抖音位置信息修改方法,虚拟修改位置实现的???
import frida import json # Android位置服务核心类 LOCATION_MANAGER =
|
Java 数据库连接 mybatis
MyBatis篇-映射关系(1-1 1-n n-n)
本文介绍了MyBatis中四种常见关系映射的配置方法,包括一对一、一对多、多对一和多对多。**一对一**通过`resultMap`实现属性与字段的映射;**一对多**以用户-角色为例,使用`<collection>`标签关联集合数据;**多对一**以作者-博客为例,利用`<association>`实现关联;**多对多**则通过引入第三方类(如UserForDept)分别在User和Dept类中添加集合属性,并配置对应的`<collection>`标签完成映射。这些方法解决了复杂数据关系的处理问题,提升了开发效率。
|
Dubbo Java 应用服务中间件
Dubbo两小时快速上手教程(直接代码、Spring、SpringBoot)
最近项目中需要用到dubbo,虽然我知道dubbo是一个RPC框架,但是没有去详细了解这个框架。既然项目要用,那就先把Dubbo的应用给学会,等熟练使用之后,再去了解Dubbo内部的原理。如果想要项目代码,直接联系我即可。如果想要demo代码,直接联系我即可。
8375 1