微服务设计模式 - 重试模式(Retry Pattern)
微服务设计模式 - 重试模式(Retry Pattern)
定义
重试模式(Retry Pattern)是一种微服务中的设计模式,用于在临时性失败(如网络故障或暂时不可用的服务)发生时,自动重新尝试请求,而不是立即返回错误。通过重试,可以增加操作成功的概率,从而提高系统的可靠性。
结构
重试模式通常包括以下几个组件:
- 调用者:发起请求的实体。
- 操作:需要重试的操作,比如API调用或数据库操作。
- 重试策略:定义重试次数、间隔时间和重试条件的策略。
工作原理
重试模式的工作原理如下:
- 调用者发起请求。
- 执行操作,如果成功则返回结果,如果失败则进入重试策略。
- 重试策略检查是否满足重试条件,如最大重试次数未达到、错误类型允许重试等。
- 如果满足条件,则按照重试策略重新请求操作,否则返回最终失败结果。
优点
-
提高可靠性:在遇到暂时性故障时,通过重试机制增加操作成功的机会。
-
增强用户体验:避免频繁的错误提示,提高用户的满意度。
-
灵活性:通过配置不同的重试策略,适应不同的业务需求。
使用场景
重试模式(Retry Pattern)在很多场景中非常有用,尤其是在处理临时性故障(transient faults)的时候。下面列举了几种典型的使用场景:
- 网络通信问题:
- 网络抖动:在面临暂时性网络抖动或不稳定时,重试可以帮助确保请求成功。
- 网络超时:一些网络请求可能超时,如果这些超时是临时的,那么可以通过重试来解决问题。
- 外部API调用:
- 第三方服务不稳定:在调用外部API或第三方服务时,如果这些服务偶尔不稳定,通过重试可以增加成功的概率。
- API限流:外部API可能会对请求数量进行限流,导致部分请求被拒绝,重试可以在稍后的时间段重新发送请求。
- 数据库操作:
- 数据库连接中断:数据库连接可能偶尔中断,通过重试机制可以重新建立连接。
- 锁定结果:在高并发情况下,某些数据库操作可能会因行锁或表锁被暂时阻塞,通过重试可以等待锁释放。
- 消息队列:
- 消息消费失败:在处理消息队列中的消息时,如果某些消息因临时性问题处理失败,可以通过重试机制重新处理这些消息。
- 分布式系统:
- 服务依赖:在分布式系统中,多个微服务之间相互依赖,如果某个服务临时不可用,通过重试可以确保请求最终成功。
- 其他临时性错误:
- 资源限制:某些临时性资源限制(如内存不足或CPU过载)可能导致操作失败,通过重试可以等待资源恢复。
- 维护或升级:某些服务可能在维护或升级过程中短暂不可用,重试机制可以在服务恢复后继续尝试请求。
影响因素
在实现重试模式时,我们需要考虑多个重要因素,包括幂等性(Idempotency)、事务一致性(Transaction Consistency)、性能影响和异常类型,以确保系统的可靠性和有效性。
以下具体介绍每一个影响因素,并以SrpingBoot相关代码,以及resilience4j(用以实现重试模式)相关配置进行辅助说明。
幂等性(Idempotency)
定义:幂等性是指在相同条件下多次执行操作,结果应保持一致。换句话说,幂等操作在被执行一次或多次后对系统的状态产生相同的影响。
重要性:重试模式通常会多次执行相同操作,因此确保操作的幂等性是至关重要的。若操作不具有幂等性,可能会导致数据不一致或重复处理。
实现示例:
- 对于HTTP请求,可以使用HTTP动词来区分幂等操作。例如,PUT和DELETE通常为幂等操作,而POST可能不是。
- 在数据库写操作时,添加唯一约束,或在应用层实现幂等逻辑。
示例代码 - 幂等性操作:
@Service
public class IdempotentService {@Autowiredprivate OrderRepository orderRepository;@Retry(name = "idempotentService", fallbackMethod = "fallback")public String createOrder(Order order) {// 检查订单是否已经存在(即幂等性检查)Optional<Order> existingOrder = orderRepository.findByOrderId(order.getOrderId());if (existingOrder.isPresent()) {return "Order already exists";}// 创建新订单orderRepository.save(order);return "Order created successfully";}private String fallback(Order order, Exception e) {return "Fallback response";}
}
事务一致性(Transaction Consistency)
定义:事务一致性确保在一组操作中,所有操作要么全部成功,要么全部失败,从而保证系统状态的一致性。
挑战:重试机制可能跨越多个事务,且每次重试都应当考虑事务的一致性问题。未能维护一致性可能导致数据混乱或部分提交的问题。
实现示例:
- 在Java中使用Spring的
@Transactional
注解来管理事务一致性。 - 在分布式系统中,使用2PC(两阶段提交)或Saga模式等事务管理策略。
示例代码 - 事务一致性:
@Service
public class TransactionalService {@Autowiredprivate OrderRepository orderRepository;@Transactional@Retry(name = "transactionalService", fallbackMethod = "fallback")public String createOrderTransactional(Order order) {// 创建新订单orderRepository.save(order);// 下单后其他相关操作...return "Order created successfully with transaction";}private String fallback(Order order, Exception e) {return "Fallback response in transaction";}
}
性能影响(Impact on Performance)
定义:重试机制可能引入额外的延迟和资源消耗,因此需要谨慎管理以减少性能影响。
优化策略:
- 限次数:限制重试次数,避免无限重试。
- 指数退避:每次重试时增加等待时间,减少系统负载。
- 快速失败:在明显不可恢复的情况下,尽早返回错误而不是反复重试。
实现示例:
- 配置如
maxAttempts
和waitDuration
等参数来控制重试策略。
示例配置 - 性能相关:
resilience4j.retry:instances:myService:max-attempts: 3wait-duration: 500msmax-wait-duration: 2sexponential-backoff:multiplier: 2
异常类型(Exception Type)
定义:不同类型的异常可能需要不同的重试策略。有些异常是暂时性的,可以通过重试解决;另一些则是不可恢复的,不应重试。
实现示例:
- 使用防御性编程和异常分类来确定哪些异常应该触发重试。
- 自定义重试规则来处理不同类型的异常。
示例代码 - 异常类型识别:
@Service
public class ExceptionHandlingService {@Retry(name = "exceptionHandlingService", fallbackMethod = "fallback", retryExceptions = {TemporaryException.class }, ignoreExceptions = { PermanentException.class })public String handleService() {// 假设某操作可能抛出TemporaryException或PermanentExceptionriskyOperation();return "Operation completed";}private void riskyOperation() throws TemporaryException, PermanentException {// 实现一些逻辑,可能抛出不同类型的异常}private String fallback(Exception e) {return "Fallback response for exceptions";}
}
重试策略
在实现重试模式时,选择合适的重试策略(Retry Strategy)是至关重要的。不同的重试策略会影响系统的可靠性、性能和响应时间。以下是常见的重试策略:
固定间隔重试(Fixed Interval Retry)
定义:固定间隔重试策略在每次重试之间使用相同的时间间隔。例如,重试每次间隔500毫秒。
优点:实现简单,适用于简单的重试场景。
缺点:在高负载或问题持续存在的情况下,可能会导致系统过载。
示例配置:
resilience4j.retry:instances:myService:max-attempts: 3wait-duration: 500ms
指数退避重试(Exponential Backoff Retry)
定义:每次重试时,等待时间逐步增加。例如,第一次重试后等待500毫秒,第二次重试后等待1秒,第三次重试后等待2秒,以此类推。
优点:逐步增加的等待时间可以有效减少系统负载,适用于网络抖动或外部服务不稳定的情形。
缺点:实现稍微复杂,可能导致较长的重试时间。
示例配置:
resilience4j.retry:instances:myService:max-attempts: 3wait-duration: 500msexponential-backoff:multiplier: 2
抛出异常后退避重试(Backoff with Jitter Retry)
定义:在指数退避的基础上,加入随机时间间隔(称为“抖动”),以避免重试请求出现峰值。
优点:通过在重试间隔中加入随机性,进一步减少了系统因重试请求同时发出的风险,适用于高并发场景。
缺点:实现复杂度更高。
示例代码(Java示例):
RetryConfig config = RetryConfig.custom().maxAttempts(3).waitDuration(Duration.ofMillis(500)).retryOnException(throwable -> throwable instanceof TemporaryException).intervalFunction(IntervalFunction.ofExponentialBackoff(500, 2).withRandomizedWait()).build();
增量退避重试(Incremental Backoff Retry)
定义:每次重试等待时间按照固定的增量增加。例如,第一次重试后等待500毫秒,第二次重试后等待1秒,第三次重试后等待1.5秒。
优点:控制每次重试的等待时间增加量,简单易理解。
缺点:在一定情况下,性能可能不如指数退避策略。
示例代码:
resilience4j.retry:instances:myService:max-attempts: 3wait-duration: 500msinterval-function:increment-interval:interval: 500ms
固定次数重试(Retry with Max Attempts)
定义:限制重试的次数,当超过重试次数时停止重试。
优点:防止过多重试导致资源消耗,保护系统稳定。
缺点:可能导致在某些情况下无效重试。
示例代码:
resilience4j.retry:instances:myService:max-attempts: 5wait-duration: 500ms
自定义重试策略(Custom Retry Strategy)
定义:根据特定的业务需求和场景,设计定制化的重试策略。
优点:灵活、满足特定需求。
缺点:需要更多的开发和测试工作。
示例代码(Java自定义实现):
RetryConfig config = RetryConfig.custom().maxAttempts(5).intervalFunction(IntervalFunction.of(Duration.ofMillis(500), IntervalFunction.of(Random::nextGaussian))).retryOnException(throwable -> {// Define your custom retry condition here.return throwable instanceof TemporaryException;}).build();
综合以上,在选择重试策略时,建议如下:
- 分析场景:根据实际业务场景选择合适的重试策略。例如,网络波动适合使用指数退避重试。
- 测试不同策略:通过负载测试和性能测试,评估不同重试策略对系统的实际影响。
- 结合多种策略:可以组合多个重试策略,例如固定次数重试加上指数退避,满足更复杂的需求。
- 监控与调整:定期监控重试机制的效果,根据实际情况动态调整重试策略。
完整实例代码
这个示例展示了如何在Spring Boot应用中使用Resilience4j实现重试模式。配置文件中定义了重试策略,包括最大尝试次数、等待时间和指数退避参数。服务层通过重试注解@Retry
实现重试逻辑,并在错误情况下调用回退方法。通过这一模式,系统可以有效应对各种临时性故障,提高整体的可靠性和稳定性。
项目结构
.
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── retry/
│ │ │ ├── RetryApplication.java
│ │ │ ├── controller/
│ │ │ │ └── RetryController.java
│ │ │ ├── service/
│ │ │ │ └── RetryService.java
│ │ │ └── exception/
│ │ │ ├── TemporaryException.java
│ │ │ └── PermanentException.java
│ │ ├── resources/
│ │ │ ├── application.yaml
└── pom.xml
Maven 依赖
首先,在Maven的pom.xml
文件中添加Resilience4j依赖项:
<dependencies><!-- Spring Boot dependencies --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Resilience4j dependencies --><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-spring-boot2</artifactId><version>1.7.1</version></dependency><dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-retry</artifactId><version>1.7.1</version></dependency>
</dependencies>
Retry配置
在Spring Boot应用程序的配置文件application.yaml
中配置Resilience4j的重试策略:
resilience4j.retry:instances:myService:max-attempts: 5wait-duration: 500msexponential-backoff:multiplier: 2retry-exceptions:- com.example.retry.exception.TemporaryExceptionignore-exceptions:- com.example.retry.exception.PermanentException
代码说明
RetryApplication.java
主应用程序文件:
package com.example.retry;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class RetryApplication {public static void main(String[] args) {SpringApplication.run(RetryApplication.class, args);}
}
RetryController.java
控制器类:
package com.example.retry.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import com.example.retry.service.RetryService;@RestController
public class RetryController {@Autowiredprivate RetryService retryService;@GetMapping("/retry-test")public ResponseEntity<String> retryTest() {return ResponseEntity.ok(retryService.callExternalService());}
}
RetryService.java
服务层实现重试逻辑:
package com.example.retry.service;import org.springframework.stereotype.Service;
import io.github.resilience4j.retry.annotation.Retry;
import com.example.retry.exception.TemporaryException;
import com.example.retry.exception.PermanentException;@Service
public class RetryService {@Retry(name = "myService", fallbackMethod = "fallback")public String callExternalService() throws TemporaryException, PermanentException {// 模拟外部服务调用double random = Math.random();if (random < 0.5) {throw new TemporaryException("Temporary issue occurred");} else if (random < 0.8) {throw new PermanentException("Permanent issue occurred");}return "Success";}private String fallback(Exception e) {return "Fallback response: " + e.getMessage();}
}
TemporaryException.java
自定义临时异常类型:
package com.example.retry.exception;public class TemporaryException extends Exception {public TemporaryException(String message) {super(message);}
}
PermanentException.java
自定义永久异常类型:
package com.example.retry.exception;public class PermanentException extends Exception {public PermanentException(String message) {super(message);}
}
类序列图
运行测试
运行Spring Boot应用程序后,访问 http://localhost:8080/retry-test
可以触发重试逻辑。根据随机数的不同,有时会成功,有时会触发临时异常进行重试,如果次数用尽则返回回退响应。
总结
在云计算和微服务架构中,重试模式是一种重要的设计模式,通过处理暂时性故障来增强系统的可靠性。当实现重试模式时,必须考虑幂等性、事务一致性、性能影响和异常类型,以确保系统的整体稳定性和正确性。Resilience4j
库提供了实现重试模式的便利方法,通过合理配置可以满足各种不同的业务需求。希望本文能帮助您更好地理解和选择合适的重试策略,为系统设计和实现提供参考。
相关文章:

微服务设计模式 - 重试模式(Retry Pattern)
微服务设计模式 - 重试模式(Retry Pattern) 定义 重试模式(Retry Pattern)是一种微服务中的设计模式,用于在临时性失败(如网络故障或暂时不可用的服务)发生时,自动重新尝试请求&…...

DNS配置
1.搭建dns服务器能够对自定义的正向或者反向域完成数据解析查询。 2.配置从DNS服务器,对主dns服务器进行数据备份。 正反向解析 [rootlocalhost ~]# vim /etc/named.conf options {listen-on port 53 { 192.168.111.130; };directory "/var/named&quo…...

【Linux指令】---获取进程的PID
获取进程的PID getpid()函数...

在centos中安装cmake
安装依赖工具: sudo yum install -y epel-release sudo yum groupinstall -y "Development Tools" sudo yum install -y wget wget 下载 CMake 3.24 的压缩包: wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0-linux-x86_64.tar.gz …...

【补补漏洞吧 | 02】等保测评ZooKeeperElasticsearch未授权访问漏洞补漏方法
一、项目背景 客户新系统上线,因为行业网络安全要求,需要做等保测评, 通过第三方漏扫工具扫描系统,漏扫报告显示ZooKeeper和 Elasticsearch 服务各拥有一个漏洞,具体结果如下: 1、ZooKeeper 未授权访问【…...

Docker Compose一键部署Spring Boot + Vue项目
目录 前提条件 概述 Compose简介 Compose文件 Compose环境 Compose命令 帮助命令 关键命令 Compose部署项目 初始化环境 查看代码文件 sql数据准备 nginx配置文件准备 创建 compose.yaml 一键启动compose多个容器 浏览器访问虚拟机ip:80(可省略默认的80端口) …...

【maven】idea执行了maven的install命令给本地安装项目依赖包 安装后删除
目录 事件起因环境和工具操作过程解决办法1、找到对应的目录下的文件,手动去删除,比如我的依赖库的路径是D:\qc_code\apache-maven-3.8.2\repository 我只需要找到这个目录下对应的依赖包进行手动删除即可(不推荐,强行删除文件夹文…...

Android中的Handle底层原理
先上结论: 一个小小的Handle,就能看出你对Android底层源码理解的深浅。 老规矩,如果你能看懂我上面画的这张草图,这篇文章你可以直接跳过! 开干! 1、先对Handle在Android系统中是如何发挥作用的做一个小结: 1)每一个线程,包括UI主线程,有一个ThreadLocalMap变量:…...

最简单方式SSH连接局域网中另一台电脑的WSL2
1、首先确认一下WSL2中的SSH服务是否开启: 先安装更新一下,再安装一下ssh服务模块,这里很多人都每安装过。 sudo apt update sudo apt upgrade sudo apt install openssh-server 2、配置SSH服务器: 打开配置文件,这…...

热点聚焦:AI在医疗保健领域的深度渗透与变革
内容概要 随着人工智能技术的不断进步,我们正在见证一个充满奇迹的转变,尤其是在医疗保健领域。这种转变不仅仅涉及到提高效率,更在于重新定义我们对疾病诊断和治疗方案的理解。通过智能算法,AI能够在早期识别潜在的健康问题&…...

模板方法模式:定义算法框架的设计模式
1. 引言 在软件开发中,算法的实现通常是可变的,但其基本步骤往往是相对固定的。模板方法模式(Template Method Pattern)正是为了解决这一问题而设计的,它通过定义一个操作中的算法框架,将一些步骤的实现延…...

[Redis] Redis事务
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...

编译原理第一次实验报告
源代码及附件:编译原理实验一源程序及附件资源-CSDN文库实验题目 实验要求 实验设计 前两部分指出了实验的宏观把控,为了具体实施实验,我们需要预先为实验做出如下设计: 本次实验我选取了C语言的一个子集进行设计词法分析器&…...

uniapp的video视频属性打包app后层级过高
问题:在使用uniapp开发APP时,使用video标签显示视频发现H5可以正常展示,但是打包到APP后,它的层级过高,把底部导航都盖住了。 官网说明:uni-app官网 官网给了cover-view组件或plus.nativeObj.view、subNVue…...

问:Redis为什么这么快?
Redis,全称Remote Dictionary Server,是一个开源的高性能键值对数据库。它以其卓越的性能、丰富的数据结构和灵活的使用方式,在现代互联网应用中扮演着重要角色。本文将探讨Redis之所以快的原因,包括其数据结构、内存管理、IO多路…...

环信鸿蒙IM SDK实现附件消息发送与下载
环信HarmonyOS IM SDK 正式版已经发布,该版本全面覆盖即时通讯(IM)的核心功能,为用户提供了完整的IM全功能体验,同时支持从Android APK到 NEXT 的数据迁移,更好地满足企业在不同业务场景下的适配需求。 点…...

探索NetCat:网络流量监测与数据传输的利器
从简单的数据传输到复杂的网络调试,NetCat的灵活性和多功能性让人赞叹不已,在这篇文章中我将深入探讨NetCat的魅力,揭示它的基本功能、实用技巧以及在日常工作中的应用场景,发现如何用这一小工具提升的网络技能与效率。 目录 Net…...

【运动的&足球】足球运动员球守门员裁判检测系统源码&数据集全套:改进yolo11-DBBNCSPELAN
改进yolo11-FocalModulation等200全套创新点大全:足球运动员球守门员裁判检测系统源码&数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.28 注意:由于项目一直在更新迭代,上面“1.图片效果展示”和“2.视频效果展示…...

求最大公约数,最小公倍数
输入两个正整数 m 和 n,求其最大公约数和最小公倍数。 求最小公倍数算法: 最小公倍数 两整数的乘积 最大公约数 根据求最小公倍数的算法,可以看出如果已知最大公约数,就能很容易求出最小公倍数。而通过辗转相除法和相减法&#…...

Android——横屏竖屏
系统配置变更的处理机制 为了避免横竖屏切换时重新加载界面的情况,Android设计了一中配置变更机制,在指定的环境配置发生变更之时,无需重启活动页面,只需执行特定的变更行为。该机制的视线过程分为两步: 修改 Androi…...

scala---10.30
val、var package com_1030class Person {var name:String"rose"def sum(n1:Int,n2:Int):Int{n1n2} } object Person{def main(args: Array[String]): Unit {//创建person对象var personnew Person()println(person.sum(10,20))//30println(person.name)person.nam…...

Pinctrl子需要中client端使用pinctrl过程的驱动分析
往期内容 本专栏往期内容: Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体 input子系统专栏…...

【网络】传输层协议TCP
目录 四位首部长度 序号 捎带应答 标记位 超时重传机制 连接管理机制(RST标记位) 三次握手及四次挥手的原因 TCP的全称是传输控制协议(Transmission Control Protocol),也就是说,对于放到TCP发送缓冲…...

00-开发环境 MPLAB IDE 配置
MPLAB IDE V8.83 File 菜单简介 New (CtrlN): 创建一个新文件,用于编写新的代码。 Add New File to Project...: 将新文件添加到当前项目中。 Open... (CtrlO): 打开现有文件。 Close (CtrlE): 关闭当前打开的文件。 …...

<meta property=“og:type“ content=“website“>
<meta property"og:type" content"website"> 这段代码是HTML中的一部分,具体来说,它是一个用于定义Open Graph协议的meta标签。 代码分析 <meta> 标签:这是一个HTML标签,用于在HTML文档的头…...

C++ 实现俄罗斯方块游戏
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

QT打包Macosx应用发布App Store简易流程
1、QC里编译工程,生成Release版的的app文件; 2、运行macdeployqt把需要的文件打包进app文件中; % ~/Qt/5.15.0/clang_64/bin/macdeployqt {编译的app文件所在路径}/Release/xxxx.app 3、使用codesign对app进行签名,如果要发App…...

untiy mlagents 飞机大战 ai训练
前言 之前那个python源码的飞机大战bug过多,还卡顿,难以继续训练。可直接放弃的话又不甘心,所以找了个unity版本的飞机大战继续(终于不卡了),这次直接使用现成的mlagents库。 过程 前前后后花了两周时间,甚至因此拖…...

从0开始学统计-什么是中心极限定理
引言 中心极限定理(Central Limit Theorem, CLT)是统计学中的一块基石,它揭示了一个难以置信的数学现象:无论一个随机变量的原始分布如何,只要我们取足够大的样本量,这些样本的平均值(或总和&a…...

工具方法 - 个人活动的分类
人类活动的分类是一个复杂的话题,因为人类的活动范围非常广泛且相互交叉。然而,我们可以尝试将人类的活动大致分为以下几个主要类别: 工作活动 工作活动是人类生活中不可或缺的一部分,通常包括以下方面: 1. 职业工作&a…...