【深入理解SpringCloud微服务】手写实现断路器算法

【深入理解SpringCloud微服务】手写实现断路器算法

  • 断路器状态切换
  • 断路器接口
  • 断路器算法实现
    • 相关属性
    • failed()
    • success()
    • canPass()

断路器状态切换

在分析断路器算法前,我们先复习一下断路器的状态转换。

在这里插入图片描述

断路器一般有三个状态:关闭、打开、半开。

  1. 断路器一开始处于关闭状态,此时请求能正常通过;
  2. 断路器会记录调用失败数,当失败数(或失败率)达到一定阈值,断路器就会变成打开状态,此时请求将不会被正常处理;
  3. 当断路器处于打开状态经过一段时间后,会切换为半开状态,此时断路器会允许一个请求通过,如果请求处理成功,则断路器切换为关闭状态,否则还是切换为打开状态。

断路器接口

/*** 断路器* @author huangjunyi* @date 2023/12/27 15:40* @desc*/
public interface Breaker {/*** 记录成功调用*/void success();/*** 记录失败调用*/void failed();/*** 判断是否可通行*/boolean canPass();}

在这里插入图片描述

Breaker接口有success()、failed()、canPass()三个方法,其中canPass()就是根据断路器状态判断请求能否正常通过。

断路器算法实现

相关属性

我们给Breaker接口提供默认实现类BasicBreaker。

/*** @author huangjunyi* @date 2023/12/27 19:11* @desc*/
public class BasicBreaker implements Breaker {// 时间窗长度(单位秒)private int timeSpan;// 时间窗内最大允许错误数量private int maxFailedNum;// 断路器状态,闭合状态为true,表示请求可以通过,开路状态为false,请求不可通过private boolean close;// 断路器最后一次打开的时间点,闭合状态为-1private long lastOpenTime;// 断路器开路状态时间(断路器处于开路状态超过该时间,就会转为半开状态)private long openTime;// 时间跨度内错误数量统计,一个AtomicInteger对应一个时间窗格,一个时间窗格的跨度为1sprivate AtomicInteger[] failedCounter;// 时间窗数组// 一个数组元素表示一个时间窗格// 每个窗格记录的是以秒为单位的时间戳,用于判断时间窗是否过期private long[] times;...}

我们的BasicBreaker实现的是滑动时间窗统计失败数的断路器。这里的滑动时间窗算法与上一篇文章《手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法》中实现的滑动时间窗算法是基本一样的。

但是这里的时间窗是1秒一个时间窗格,而时间窗长度是需要用户指定的timeSpan(单位秒)。AtomicInteger[] failedCounter则是记录失败数的计数器数组,每个时间窗格对应一个计算器。

比如我们设置了timeSpan=5,表示时间窗长度为5,于是时间窗数组times和计数器数组failedCounter长度为5,那么就是下面那样:

在这里插入图片描述

当失败数达到阈值maxFailedNum是,断路器切换为打开,也就是close由true变为false,此时请求将不再正常通过。

failed()

failed()方法的作用是当接口处理失败或超时时记录失败数。

    @Overridepublic void failed() {long currentTimeMillis = System.currentTimeMillis();// 当前这一秒,是从1970年1月1日零点到现在的第几秒long currentTimeSeconds = currentTimeMillis / 1000L;// 计算时间窗格下标int timeIndex = (int) (currentTimeSeconds % times.length);// 判断时间窗格对应的计数器是否为空,// 如果不为空再判断时间窗格的秒值与currentTimeSeconds是否相等// 如果不相等,表示该时间窗格已经过期if (failedCounter[timeIndex] == null || times[timeIndex] != currentTimeSeconds) {// 时间窗格对应的计数器为空或已过期,重置计数器和窗格的秒值failedCounter[timeIndex] = new AtomicInteger(1);times[timeIndex] = currentTimeSeconds;} else {// 时间窗格不为空且没过期,增加失败数failedCounter[timeIndex].incrementAndGet();}// 统计失败数,是否要修改为开路if (sum(currentTimeSeconds) > maxFailedNum) {lastOpenTime = currentTimeMillis;close = false;}}

由于我们设计的时间窗是1秒一个窗格,那么只要用System.currentTimeMillis()除以1000,再模上时间窗格数组的长度times.length,就能得出目标时间窗格下标timeIndex。

然后判断时间窗格对应的计数器是否为空,或者时间窗格是否已过期。如果是,那么重置计数器和时间窗格;否则增加时间窗格对应的计数器的计数。

这里怎么判断一个时间窗格是否已过期呢?由于我们已经得到了了从1970年1月1日零点到当前的秒值currentTimeSeconds,而时间窗格times[timeIndex]记录的是从1970年1月1日零点到它当时被重置时的秒值,因此两者比较一下是否相等,就能得知时间窗格是否过期。

比如当前时间是2024-04-05 20:09:20,时间戳是1712318960000,假如时间窗数组是这样:

在这里插入图片描述
那么currentTimeSeconds = 1712318960000 / 1000 = 1712318960,然后timeIndex = 1712318960 % 5 = 0,那么得到的时间窗格数组下标为0,然后判断times[timeIndex]又等于1712318960,那么得知该时间窗格没有过期。

在这里插入图片描述

假如时间往后走了5秒,此时时间是2024-04-05 20:09:25,时间戳是1712318965000,那么currentTimeSeconds = 1712318965000 / 1000 = 1712318965,然后timeIndex = 1712318965 % 5 = 0,得到的时间窗格数组下标又是0,然后times[timeIndex]是1712318960,不等于1712318965,那么得知时间窗格已过期。

在这里插入图片描述

最后通过sum(currentTimeSeconds)得出当前时间窗失败数的统计值,如果超过断路器阈值,则切换断路器状态为打开状态。

在这里插入图片描述

再看下sum()方法如何做统计。

    private int sum(long currentTimeSeconds) {int sum = 0;for (int i = 0; i < times.length; i++) {// timeSpan是用户指定的时间窗长度// 如果currentTimeSeconds - times[i]大于等于timeSpan,表示该时间窗格已过期if (currentTimeSeconds - times[i] >= timeSpan) {// 已不在当前时间窗内的窗格,忽略continue;}if (failedCounter[i] != null) {// 计数器的值累加到sumsum += failedCounter[i].get();}}return sum;}

在这里插入图片描述

success()

    @Overridepublic void success() {// 请求成功,清空所有的错误记录failedCounter = new AtomicInteger[timeSpan];// 修改断路器为闭合状态close = true;}

我们设计的断路器,一旦请求处理成功,那么清空所有的错误记录,然后修改断路器为闭合状态。

在这里插入图片描述

canPass()

    @Overridepublic boolean canPass() {// 闭合状态,放行if (close) {return true;}long currentTimeMillis = System.currentTimeMillis();// 计算断路器打开时间是否已超过指定打开时间openTimeif (lastOpenTime != -1 && currentTimeMillis - lastOpenTime > openTime) {// 断路器打开时间已超过指定时间,此时处于半开状态,放行一个请求// 更新lastOpenTime断路器最后一次打开时间lastOpenTime = currentTimeMillis;return true;}// 断路器处于打开状态,拒绝处理请求return false;}

我们的断路器用一个boolean类型的close变量记录状态,true是关闭状态,false是打开状态。半开状态是根据时间计算的,当前时间currentTimeMillis减去上一次打开的时间lastOpenTime如果大于等于openTime,表示断路器转为半开状态。

在这里插入图片描述

代码已经提交到gitee,可以自行下载阅读。
https://gitee.com/huang_junyi/simple-microservice/tree/master/simple-microservice-protector/src/main/java/com/huangjunyi1993/simple/microservice/protector/breaker
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

PP-Structure 快速入门

PP-Structure 快速入门 1. 环境准备2.快速使用2.1 命令行使用2.1.1 图像定位布局分析表格识别2.1.2 布局分析表格识别2.1.3 布局分析2.1.4 表格识别2.1.5 关键信息提取2.1.6 布局恢复2.2 python 脚本使用2.2.1 图像方向布局分析表格识别2.2.2 布局分析表格识别2.2.3 布局分析2…

正则表达式匹配英文字符

正则表达式匹配英文 20 个字符&#xff0c;包括大写&#xff0c;小写。 根据搜索结果&#xff0c;看到 honeymoose 分享过一个正则表达式的要求是: 匹配 20 个英文字符(大写、小写都包括)。 那么这个正则表达式可以写成: ^[a-zA-Z]{20}$解释一下: ^ 表示匹配字符串的开始[a-z…

pWnos1.0 靶机渗透 (Perl CGI 的反弹 shell 利用)

靶机介绍 来自 vulnhub 主机发现 ┌──(kali㉿kali)-[~/testPwnos1.0] …

EtherCAT 转 EtherNet/IP, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899协议转换通信网关 EtherCAT 转 EtherNet/IP GW系列型号 MS-GW12 概述 MS-GW12 是 EtherCAT 和 EtherNet/IP 协议转换网关&#xff0c;为用户提供两…

基于Vue的汽车维修配件综合管理系统设计与实现SpringBoot后端源码

目录 1. 系统背景 2. 系统目标 3. 功能模块 4. 技术选型 5. 关键技术点 6. 实现步骤 7. 项目意义 8. 后期展望 1. 系统背景 市场需求分析&#xff1a;随着汽车保有量的不断增加&#xff0c;汽车维修和保养的需求日益增长。车主对维修质量和配件质量的要求也越来越高。汽…

安全防护检测数据集 3500张 PPE 动火 带标注 voc yolo 12类

安全防护检测数据集 3500张 PPE 动火 带标注 voc yolo 分类名: (图片张数&#xff0c; 标注个数) he Imet: (3649&#xff0c;10494) no_ goggles: (2197&#xff0c;4545) no_ mask: (2986&#xff0c; 6918) no_ vest: (2602&#xff0c; 7462) boots: (1802&#xff0c; 765…

VirtualBox虚拟机连接宿主机并能够上网(小白向)

现存问题 windows系统主要使用vmare和virtualbox两种虚拟机&#xff0c;virtualbox相对于vmare更加轻便&#xff0c;但少有博客能够详细说明使用virtualbox的教程。踩了网上的坑后&#xff0c;决定写一篇文章介绍virtualbox虚拟机上网的流程。 需求 1. virtualbox虚拟机与宿主机…

一篇文章搞懂Android 刷卡器对接:RS232 DB9串口通讯,通讯设置,刷卡器API介绍;代码示例;MDB协议;

目录 前言 在一些国家,还没有普及扫码支付的时候,消费者会纸币、硬币或者刷卡进行支付,这里我们讲解一下刷卡支付。 在市面上,有哪家刷卡器公司可以说的上是开通了很多国家的支付银行,那么Nayax和Pax可以说的上是名列前茅,他们适配了很多国家,对接其他国家的银行,让我…

ChatGPT 更新 Canvas 深度测评:论文写作这样用它!

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 ChatGPT又又更新了&#xff1a;基于ChatGPT 4o模型的Canvas 写作和代码功能。目前&#xff0c;仅针对Plus和Team用户。是一个独立的模块&#xff0c;如下所示&#xff1a; 官方…

ISO IEC 18004 2024/2015 Chinese 下载

ISO_IEC 18004 2024.pdf - 蓝奏云文件大小&#xff1a;40.3 M|https://610402220623.lanzouq.com/iqZ122bnx0yjISO IEC 18004-2015 zh-CN.pdf - 蓝奏云文件大小&#xff1a;34.1 M|https://610402220623.lanzouq.com/iEXSB2bnx0hc

G. Gears (2022 ICPC Southeastern Europe Regional Contest. )

G. Gears 思路&#xff1a; 本身这个题并不难&#xff0c;奈何卡了很久后看了题解才做出来&#xff0c;感觉自己好笨。 很容易想到的是&#xff0c;只要确定了一个齿轮的位置&#xff0c;其他齿轮的位置都可以直接推出来。所以当前目标是如何确定第一个齿轮的位置。 令 x [ i …

系统守护者:使用PyCharm与Python实现关键硬件状态的实时监控

目录 前言 系统准备 软件下载与安装 安装相关库 程序准备 主体程序 更改后的程序&#xff1a; 编写.NET程序 前言 在现代生活中&#xff0c;电脑作为核心工具&#xff0c;其性能和稳定性的维护至关重要。为确保电脑高效运行&#xff0c;我们不仅需关注软件优化&#xf…

Koa2项目实战2(路由管理、项目结构优化)

添加路由&#xff08;处理不同的URL请求&#xff09; 路由&#xff1a;根据不同的URL&#xff0c;调用对应的处理函数。 每一个接口服务&#xff0c;最核心的功能是&#xff1a;根据不同的URL请求&#xff0c;返回不同的数据。也就是调用不同的接口返回不同的数据。 在 Node…

数据服务-备份服务(rsync)

1. 概述 特点&#xff1a; 1. rsync是个服务也是命令 2. 使用方便,具有多种模式 3. 传输数据的时候是增量传输 1.1 增量与全量 1. 增量&#xff1a;只会把修改&#xff0c;新建的内容推走 2. 全量&#xff1a;无论数据多少全部推送 1.2 把/etc/目录传输到另一台机器的/tmp/下面…

安卓 /proc 目录详解:从内核到进程的桥梁

在安卓系统中&#xff0c;/proc 目录是开发者、调试者、甚至是普通用户深入了解系统状态、性能及行为的一个重要入口。这个虚拟文件系统不仅包含了丰富的内核信息&#xff0c;还反映了运行中的每个进程的状态。 /proc 文件系统 /proc 文件系统&#xff08;procfs&#xff09;是…

前端编程艺术(3)---JavaScript

目录 1.JavaScript 1.输出 2.变量和数据类型 3.运算符 4.数组 5.函数 6.面向对象 7.ES6面向对象 2.BOM 1.document对象 3.DOM 4.JSON 1.JavaScript JavaScript是一种脚本编程语言&#xff0c;通常用于为网页增加交互性和动态效果。它是一种高级语言&#xff…

C++ 算法学习——1.6 差分算法与二维差分算法

一维差分算法概述&#xff1a; 差分算法是一种用于计算序列中相邻元素之间差值的技术。在C中&#xff0c;STL&#xff08;标准模板库&#xff09;提供了std::adjacent_difference函数来实现差分算法。 std::adjacent_difference函数&#xff1a; std::adjacent_difference函数位…

余承东直播论道智能驾驶:激光雷达不可或缺,华为ADS 3.0引领安全创新

华为余承东:激光雷达,智能驾驶安全性的关键 9月29日,华为消费者业务集团CEO余承东在一场引人注目的直播中,与知名主持人马东就智能驾驶技术的最新进展进行了深入交流。在这场直播中,余承东针对激光雷达在智能驾驶中的必要性问题,发表了明确且深刻的观点,引发了业界和公众…

网关路由登录校验

网关过滤器 登录校验必须在请求转发到微服务之前做&#xff0c;否则就失去了意义。而网关的请求转发是Gateway内部代码实现的&#xff0c;要想在请求转发之前做登录校验&#xff0c;就必须了解Gateway内部工作的基本原理。 暂时无法在飞书文档外展示此内容 如图所示&#xff…

一、Python(介绍、环境搭建)

一、介绍 Python 是一种高级编程语言&#xff0c;具有简洁易读的语法、丰富的库和强大的功能。Python是解释型语言&#xff0c;运行代码必须依赖安装好的解释器。Python目前存在两个版本&#xff1a;Python2、Python3&#xff08;主流使用&#xff09; 二、环境搭建 1.安装P…