当前位置: 首页 > news >正文

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

🔍类图


🔍适用场景


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

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

相关文章:

  • 解读makefile中的.PHONY
  • 牛客NC236 最大差值【simple 动态规划 Java/Go/PHP】
  • TypeScript类型体操练习
  • 网络、HTTP、HTTPS、Session、Cookie、UDP、TCP
  • 揭秘Tensor Core黑科技:如何让AI计算速度飞跃
  • mysql8忘记密码重置密码和创建新用户
  • Golang协程和通道
  • 数据结构的希尔排序(c语言版)
  • MySQL 高级 - 第十章 | 性能分析工具的使用
  • springcloud-服务拆分与远程调用
  • 所以研究生有不变胖的吗?
  • 【考研数据结构知识点详解及整理——C语言描述】第一章算法和算法评价
  • 3步骤找回丢失文件!EasyRecovery让你轻松应对数据灾难!
  • C++的第一道门坎:类与对象(二)
  • NoSQL数据库技术与应用 教学设计
  • C++类中的特殊成员函数
  • express如何解决request entity too large问题
  • Java|序列化异常StreamCorruptedException的解决方法
  • javascript 总结(常用工具类的封装)
  • JavaScript设计模式与开发实践系列之策略模式
  • learning koa2.x
  • MaxCompute访问TableStore(OTS) 数据
  • Meteor的表单提交:Form
  • October CMS - 快速入门 9 Images And Galleries
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • Python_OOP
  • React的组件模式
  • Vue2.x学习三:事件处理生命周期钩子
  • 半理解系列--Promise的进化史
  • 聊一聊前端的监控
  • 前端_面试
  • 前端之React实战:创建跨平台的项目架构
  • 用element的upload组件实现多图片上传和压缩
  • 用Python写一份独特的元宵节祝福
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • MyCAT水平分库
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • ​linux启动进程的方式
  • ​什么是bug?bug的源头在哪里?
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #1014 : Trie树
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (70min)字节暑假实习二面(已挂)
  • (C语言)字符分类函数
  • (c语言版)滑动窗口 给定一个字符串,只包含字母和数字,按要求找出字符串中的最长(连续)子串的长度
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (九)c52学习之旅-定时器
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)【Hibernate总结系列】使用举例
  • (转)大型网站的系统架构