【设计模式】JAVA Design Patterns——Circuit Breaker(断路器模式)

🔍目的


以这样一种方式处理昂贵的远程服务调用,即单个服务/组件的故障不会导致整个应用程序宕机,我们可以尽快重新连接到服务

🔍解释


真实世界例子

想象一个 Web 应用程序,它同时具有用于获取数据的本地文件/图像和远程服务。 这些远程服务有时可能健康且响应迅速,或者由于各种原因可能在某 个时间点变得缓慢和无响应。因此,如果其中一个远程服务缓慢或未成功响应,我们的应用程序将尝试使用多个线程/进程从远程服务获取响应,很快它们都会挂起(也称为 [线程饥饿]thread starvationopen in new window)导致我们的整个 Web 应用程序崩溃。我们应该能够检测到这种情况并向用户显示适当的消息,以便他/她可以探索不受远程服务故障影响的应用程序的其他部分。 同时,其他正常工作的服务应保持正常运行,不受此故障的影响。

通俗描述

断路器允许优雅地处理失败的远程服务。当我们应用程序的所有部分彼此高度解耦时,它特别有用,一个组件的故障并不意味着其他部分将停止工作。

维基百科

断路器是现代软件开发中使用的一种设计模式。 它用于检测故障并封装防止故障不断重复发生、维护期间、临时外部系统故障或意外系统困难的逻辑。

程序示例

So, how does this all come together? With the above example in mind we will imitate the
functionality in a simple example. A monitoring service mimics the web app and makes both local and remote calls.

在一个简单的例子中模仿这个功能。 监控服务模仿 Web 应用程序并进行本地和远程调用。

最终用户程序

@Slf4j
public class App {private static final Logger LOGGER = LoggerFactory.getLogger(App.class);/*** Program entry point.** @param args command line args*/public static void main(String[] args) {var serverStartTime = System.nanoTime();var delayedService = new DelayedRemoteService(serverStartTime, 5);var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,2000 * 1000 * 1000);var quickService = new QuickRemoteService();var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,2000 * 1000 * 1000);// 创建一个可以进行本地和远程调用的监控服务对象var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,quickServiceCircuitBreaker);// 获取本地资源LOGGER.info(monitoringService.localResourceResponse());// 从延迟服务中获取响应 2 次,以满足失败阈值LOGGER.info(monitoringService.delayedServiceResponse());LOGGER.info(monitoringService.delayedServiceResponse());// 在超过故障阈值限制后获取延迟服务断路器的当前状态// 现在是打开状态LOGGER.info(delayedServiceCircuitBreaker.getState());// 同时,延迟服务宕机,从健康快速服务获取响应LOGGER.info(monitoringService.quickServiceResponse());LOGGER.info(quickServiceCircuitBreaker.getState());// 等待延迟的服务响应try {LOGGER.info("Waiting for delayed service to become responsive");Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 检查延时断路器的状态,应该是HALF_OPENLOGGER.info(delayedServiceCircuitBreaker.getState());// 从延迟服务中获取响应,现在应该是健康的LOGGER.info(monitoringService.delayedServiceResponse());// 获取成功响应后,它的状态应该是关闭。LOGGER.info(delayedServiceCircuitBreaker.getState());}
}

监控服务类

public class MonitoringService {private final CircuitBreaker delayedService;private final CircuitBreaker quickService;public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {this.delayedService = delayedService;this.quickService = quickService;}// 假设:本地服务不会失败,无需将其包装在断路器逻辑中public String localResourceResponse() {return "Local Service is working";}/*** Fetch response from the delayed service (with some simulated startup time).** @return response string*/public String delayedServiceResponse() {try {return this.delayedService.attemptRequest();} catch (RemoteServiceException e) {return e.getMessage();}}/*** Fetches response from a healthy service without any failure.** @return response string*/public String quickServiceResponse() {try {return this.quickService.attemptRequest();} catch (RemoteServiceException e) {return e.getMessage();}}
}

直接调用获取本地资源,但它将对远程(昂贵)服务的调用包装在断路器对象中,防止故障如下:

public class DefaultCircuitBreaker implements CircuitBreaker {private final long timeout;private final long retryTimePeriod;private final RemoteService service;long lastFailureTime;private String lastFailureResponse;int failureCount;private final int failureThreshold;private State state;private final long futureTime = 1000 * 1000 * 1000 * 1000;/*** Constructor to create an instance of Circuit Breaker.** @param timeout          Timeout for the API request. Not necessary for this simple example* @param failureThreshold Number of failures we receive from the depended service before changing*                         state to 'OPEN'* @param retryTimePeriod  Time period after which a new request is made to remote service for*                         status check.*/DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,long retryTimePeriod) {this.service = serviceToCall;//  我们从关闭状态开始希望一切都是正常的this.state = State.CLOSED;this.failureThreshold = failureThreshold;// API的超时时间.// 用于在超过限制时中断对远程资源的调用this.timeout = timeout;this.retryTimePeriod = retryTimePeriod;//An absurd amount of time in future which basically indicates the last failure never happenedthis.lastFailureTime = System.nanoTime() + futureTime;this.failureCount = 0;}// 重置所有@Overridepublic void recordSuccess() {this.failureCount = 0;this.lastFailureTime = System.nanoTime() + futureTime;this.state = State.CLOSED;}@Overridepublic void recordFailure(String response) {failureCount = failureCount + 1;this.lastFailureTime = System.nanoTime();// Cache the failure response for returning on open statethis.lastFailureResponse = response;}// 根据 failureThreshold、failureCount 和 lastFailureTime 评估当前状态。protected void evaluateState() {if (failureCount >= failureThreshold) { //Then something is wrong with remote serviceif ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {// 我们已经等得够久了,应该尝试检查服务是否已启动state = State.HALF_OPEN;} else {// 服务可能仍会出现故障state = State.OPEN;}} else {// 一切正常state = State.CLOSED;}}@Overridepublic String getState() {evaluateState();return state.name();}/*** Break the circuit beforehand if it is known service is down Or connect the circuit manually if* service comes online before expected.** @param state State at which circuit is in*/@Overridepublic void setState(State state) {this.state = state;switch (state) {case OPEN -> {this.failureCount = failureThreshold;this.lastFailureTime = System.nanoTime();}case HALF_OPEN -> {this.failureCount = failureThreshold;this.lastFailureTime = System.nanoTime() - retryTimePeriod;}default -> this.failureCount = 0;}}/*** Executes service call.** @return Value from the remote resource, stale response or a custom exception*/@Overridepublic String attemptRequest() throws RemoteServiceException {evaluateState();if (state == State.OPEN) {// 如果电路处于打开状态,则返回缓存的响应return this.lastFailureResponse;} else {// 如果电路未打开,则发出 API 请求try {//在实际应用程序中,这将在线程中运行,并且将利用断路器的超时参数来了解服务// 是否正在工作。 在这里,我们根据服务器响应本身模拟var response = service.call();// api 响应正常,重置所有。recordSuccess();return response;} catch (RemoteServiceException ex) {recordFailure(ex.getMessage());throw ex;}}}
}

我们可以通过上述实现这个有限状态机

1.使用某些参数初始化断路器对象:timeoutfailureThreshold 和 retryTimePeriod,这有助于确定 API 的弹性。

2.最初,我们处于“关闭”状态,没有发生对 API 的远程调用。

3.每次调用成功时,我们都会将状态重置为开始时的状态。

4.如果失败次数超过某个阈值,我们将进入“open”状态,这就像开路一样,阻止远程服务调用,从而节省资源。 (这里,我们从 API 返回名为 stale response 的响应)

5.一旦超过重试超时时间,我们就会进入“半开”状态并再次调用远程服务以检查服务是否正常工作,以便我们可以提供新鲜内容。 失败将其设置回“打开”状态,并在重试超时时间后进行另一次尝试,而成功将其设置为“关闭”状态,以便一切重新开始正常工作

🔍类图


🔍适用场景


在以下情况下使用断路器模式

  • 构建一个容错应用程序,其中某些服务的故障不应导致整个应用程序宕机。
  • 构建一个持续运行(永远在线)的应用程序,这样它的组件就可以在不完全关闭的情况下升级。

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

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

相关文章

Linux软硬链接详解

软链接: ln -s file1 file2//file1为目标文件,file2为软链接文件 演示: 从上图可以得出: 软链接本质不是同一个文件,因为inode不同。 作用: 软连接就像是Windows里的快捷方式,里面存放的是目标…

苹果手机数据不慎删除?这4个方法果粉必看!

苹果手机该怎么恢复丢失的数据呢?有时候会因为使用不当或者是被他人误删等原因,导致重要的数据丢失,这时我们需要找回丢失手机数据,小编给大家分享4种恢复苹果手机数据的技巧,大家赶紧来学一学吧! 一、iclo…

SpringBoot 返回值 i18n 自动处理

定义基础通用类 首先定义一波错误码:ResultCode Getter AllArgsConstructor public enum ResultCode {SUCCESS(200, "请求成功", "request.success"),Fail(400, "请求失败", "request.failed"),PASSWORD_NOT_MATCH(1000…

Parasoft C++Test软件静态分析操作指南_编码规范/标准检查

系列文章目录 Parasoft CTest软件安装指南 Parasoft CTest软件静态分析操作指南_编码规范/标准检查 Parasoft CTest软件静态分析操作指南_软件质量度量 Parasoft CTest软件静态分析_自动提取静态分析数据生成文档 Parasoft CTest软件单元测试_操作指南 Parasoft CTest软件单元…

微信小程序多端应用Donut Android生成签名

一、生成签名的作用 确保应用的完整性:签名可以确保应用在发布后没有被修改。如果应用被修改,签名就会改变,Android系统就会拒绝安装。确定应用的唯一身份:签名是应用的唯一标识,Android系统通过签名来区分不同的应用…

设计模式18—— 迭代器模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用,主要是下面的UML图可以起到大作用,在你学习过一遍以后可能会遗忘,忘记了不要紧,只要看一眼UML图就能想起来了。同时也请大家多多指教。 迭代器模式(Iterat…

基环树学习笔记

理论基础: 内向基环树就是每个联通块有且仅有一个环,并且出度为1的有向图,每一个内向基环树都是由联通环和指向联通环的树枝组成。而且基环可以只有两个节点构成。 Leetcode - 2127:参加会议的最多员工数 题目: 一个…

【RabbitMQ】SpringAMQP--消息转换器

SpringAMQP–消息转换器 测试发送Object类型消息 1.声明队列 Configuration public class FanoutConfig {Beanpublic Queue objectQueue(){return new Queue("object.queue");} }运行消费者后: 2.发送消息 RunWith(SpringRunner.class) SpringBootTes…

【数据结构与算法】七大排序算法(上)

【数据结构与算法】七大排序算法(上) 🥕个人主页:开敲🍉 🔥所属专栏:数据结构与算法🍅 🌼文章目录🌼 1. 排序的概念及应用 1.1 排序的概念 1.2 排序的应用 1.3 常见排序算法 2. 常…

开源博客项目Blog .NET Core源码学习(23:App.Hosting项目结构分析-11)

本文学习并分析App.Hosting项目中后台管理页面的标签管理页面、轮播图维护页面。 标签管理页面 标签管理页面用于显示、检索、新建、编辑、删除标签数据,以便在前台页面的首页及文章专栏等页面显示标签数据。标签管理页面附带一新建及编辑页面,以支撑新…

如同“水生态”的存储引擎|OceanBase数据转储合并技术解读(一)

本系列文章主要围绕 OceanBase数据库存储引擎中的转储合并进行解读,涉及到数据存储、转储合并、数据校验等方面的内容,旨在让读者了解OceanBase数据库的存储引擎中与转储合并有关的各种概念,帮助读者更好地理解OceanBase数据库的存储技术原理…

基于STM32实现智能饮水机控制系统

目录 引言环境准备智能饮水机控制系统基础代码示例:实现智能饮水机控制系统 温度传感器数据读取水泵和加热器控制水位传感器数据读取用户界面与显示应用场景:家庭和办公室的智能饮水管理问题解决方案与优化收尾与总结 1. 引言 本教程将详细介绍如何在S…

关于pdfbox读取pdf

最近,想着将pdf的文件进行读取其内容,发现了一个比较好用的依赖pdfbox。目前使用这个依赖,进行实现一个简单实例,如果之后需要使用到更深的了解,会进行更新。这里提醒一下:jdk8尽量采用pdfbox3.x版本。 对…

Linux一键安装Docker、kkfileviewer

Linux一键安装Docker、kkfileviewer 一、安装docker 安装docker脚本 vi initDocker.sh脚本内容 #安装前先更新yum,防止连接镜像失败 yum -y update#卸载系统之前的docker(可选择,我这里直接注释了) #yum remove docker docker…

香橙派KunpengPro测评之使用C语言操控40pin引脚

香橙派KunpengPro测评之使用C语言操控40pin引脚 香橙派KunpengPro介绍香橙派实物图香橙派登录界面香橙派KunpengPro的登录界面香橙派KunpengPro的原始桌面香橙派KunpengPro内安装了VScode等软件香橙派KunpengPro的终端 香橙派硬件参数核心性能图形与显示接口丰富性扩展与兼容性…

十四天学会Vue——Vue核心(理论+实战)上篇(第一天)

一、Vue核心(上篇) 热身tops:选取开发模式 ①用于开发模式 我们只需要知道 我们是开发模式,开发模式他会跟你提示代码出现错误的地方以及出错原因,而生产模式比较简洁。 ②用于生产模式 1.1 new Vue()实例 了解Vue&a…

数据库语法树优化

目录 一、σ、π、⋈ 1.选择σ 2.投影π 3.连接⋈ 二、 构建语法树 ① 解读sql语句 ② 写出关系代数表达式 ③ 画出语法树 三、优化语法树 四、练习 语法树优化方法 一、σ、π、⋈ 1.选择σ 选择就是在关系R中选择满足给定条件的诸元组。 通过条件SdeptIS选择出系别…

基于香橙派搭建家庭网盘

一、概述 家庭网盘是一种用于家庭用户的在线存储和文件共享服务。它允许家庭成员在云端存储、同步和分享照片、视频、文档等文件,方便快捷地访问和管理个人和家庭数据。家庭网盘通常提供安全可靠的数据存储和备份功能,保障用户数据的安全性。此外&#x…

一文解决弹窗交互设计难题,轻松上手

弹窗交互的分类 我们每天所说的弹出窗口是一个非常笼统的概念。我们习惯性地称所有的对话框、浮层和提示条为弹出窗口。事实上,弹出窗口可以分为两种类型:模态弹出框和非模态弹出框。在 UI 在设计中,当它迫使用户与之交互时,我们…

【算法】【二叉树,DFS,哈希集合,分类讨论】力扣1110. 删点成林

1110. 删点成林 文章目录 【算法】力扣【二叉树,DFS,哈希集合,分类讨论】1110. 删点成林题目描述示例 1:示例 2: 输入输出示例解释思路解析核心思想算法步骤复杂度分析 代码实现总结 【算法】力扣【二叉树&#xff0c…