分布式弹性故障处理框架——Polly(1)

1 前言之服务雪崩

在我们实施微服务之后,服务间的调用变得异常频繁,多个服务之前可能存在互相依赖的关系,当某个服务出现故障或者是因为服务间的网络出现故障,导致服务调用的失败,进而影响到某个业务服务处理失败,服务依赖的故障可能导致级联崩溃,如一个微服务不可用拖垮整个系统。【服务雪崩】

服务雪崩通常遵循 “从局部故障到全局崩溃” 的递进路径,可拆解为以下步骤:

  1. 初始故障
    某个基础服务(如数据库、缓存、第三方 API)因过载、网络中断或 bug 出现响应延迟或失败。
    例:支付服务因数据库连接池耗尽,处理请求的时间从 100ms 增至 5s。
  2. 请求堆积
    依赖该故障服务的上游服务(如订单服务)因等待响应,线程 / 连接被长时间占用,无法释放资源。
    例:订单服务调用支付服务时,每个请求都需等待 5s,而线程池容量有限(如 200 线程),很快所有线程被占满。
  3. 级联阻塞
    上游服务因资源耗尽,无法处理新请求,导致依赖它的更上游服务(如用户服务)也出现资源耗尽。
    例:用户服务调用订单服务时,因订单服务无响应,用户服务的线程也被占满,无法处理用户登录请求。
  4. 全局崩溃
    故障像多米诺骨牌一样扩散,最终整个系统的核心功能(如下单、支付、登录)全部失效。

服务雪崩的本质是 “故障的无限制扩散”. 就像是蝴蝶效应最后导致美国得克萨斯州的一场龙卷风

2 如何解决服务雪崩

针对雪崩的形成机制,需从 “限制资源消耗”“隔离故障”“快速失败” 三个维度设计防护策略:

  1. 熔断机制(Circuit Breaker)
  • 原理:当某个服务的失败率超过阈值(如 50% 失败),暂时 “断开” 对它的调用,直接返回降级结果,避免资源浪费。
  • 效果:防止故障服务持续消耗上游资源,给故障服务恢复的时间窗口。
  1. 限流机制(Rate Limiting)
  • 原理:限制单位时间内对某个服务的请求量(如每秒 1000 次),防止瞬时流量冲垮服务。
  • 场景:针对下游服务的最大承载能力,提前设置流量阈值。
  1. 隔离机制(Isolation)
  • 原理:为不同服务的调用分配独立的资源池(线程池、连接池),避免一个服务的故障耗尽全局资源。
  • :订单服务调用支付服务时使用单独的线程池(20 线程),调用库存服务时使用另一个线程池(30 线程),即使支付服务故障,也不会影响库存服务的调用。
  1. 超时控制(Timeout)
  • 原理:为服务调用设置明确的超时时间(如 2s),超过时间直接终止请求,避免线程被无限期占用。
  • 关键:超时时间需根据下游服务的正常响应时间合理设置(通常略高于 99% 请求的处理时间)。
  1. 降级策略(Fallback)
  • 原理:当服务调用失败时,返回预设的兜底结果(而非直接报错),保证核心流程可用。
  • :推荐服务故障时,返回默认热门商品列表;支付服务超时后,返回 “支付中,请稍后查询”。

3 什么是Polly

Polly 是 .NET 生态中一款强大的 弹性和瞬态故障处理库,主要用于处理分布式系统中常见的网络故障、超时、资源限流等问题,通过预定义的策略(如重试、熔断、超时等)提高应用程序的稳定性和容错能力。从而增强服务的可用性

3.1 超时策略

超时策略可防止因长时间阻塞导致的资源耗尽或级联故障.

在 Polly 中,TimeoutStrategy 是控制超时行为的核心枚举,
定义了两种不同的超时处理机制:乐观超时(Optimistic)和悲观超时(Pessimistic)
Optimistic使用CancellationToken取消服务,默认是使用乐观超时的处理机制
Pessimistic 不需要使用CancellationToken,强制终端业务逻辑,这种情形可能会导致相关联的资源没有关闭。需要对这部分逻辑做处理

策略类型触发条件依赖操作协作抛出的异常类型
乐观超时超时 + 操作响应取消OperationCanceledException
悲观超时超时(强制触发)TimeoutRejectedException
     /// <summary>/// Polly的超时策略【使用乐观处理机制】/// </summary>/// <param name="url"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> TimeOutOptimisticPolicy(string url) {// 示例:设置 5 秒悲观超时 超时后将执行回调函数// 超时策略可防止因长时间阻塞导致的资源耗尽或级联故障// 在 Polly 中,TimeoutStrategy 是控制超时行为的核心枚举,// 定义了两种不同的超时处理机制:乐观超时(Optimistic)和悲观超时(Pessimistic)// Optimistic使用CancellationToken取消服务,默认是使用乐观超时的处理机制// Pessimistic不需要使用CancellationToken取消服务// onTimeoutAsync回调函数是在超时发生时执行的附加操作,但它不会改变 ExecuteAsync 的返回值。// 这个回调主要用于日志记录、监控或其他副作用操作,而不是直接返回 HTTP 响应。using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));var token = cts.Token;var timeOutPolicy = Policy.TimeoutAsync<IActionResult>(timeout: TimeSpan.FromSeconds(3),timeoutStrategy: TimeoutStrategy.Optimistic,onTimeoutAsync: (context, timespan, task) =>{Console.WriteLine($"操作超时:{timespan.TotalSeconds}秒");return Task.CompletedTask;});return await  timeOutPolicy. ExecuteAsync(async token =>{try{using (HttpClient client = new HttpClient()){HttpResponseMessage message = await client.GetAsync(url, token);//序列化响应结果string resContent = await message.Content.ReadAsStringAsync();return new OkObjectResult(resContent);}}//任务由于超时异常被取消 ,在任务内部抛出 TaskCanceledException 异常catch (TaskCanceledException e) {return new ObjectResult(new ReturnMessageModel<string>{Code = "408",Message = "操作超时",}){ StatusCode = 408 };}catch (Exception e){return new ObjectResult(new ReturnMessageModel<string>{Code = "500",Message = $"服务错误:{e.Message}" + e.GetType().Name,Status = (int)HttpStatusCode.InternalServerError}){ StatusCode = 500 };}}, token);}/// <summary>/// Polly的超时策略【使用悲观处理机制】/// </summary>/// <param name="url"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> TimeOutPessimisticPolicy(string url){var timeOutPolicy = Policy.TimeoutAsync<IActionResult>(timeout: TimeSpan.FromSeconds(3),timeoutStrategy: TimeoutStrategy.Pessimistic,onTimeoutAsync: (context, timespan, task) =>{Console.WriteLine($"操作超时:{timespan.TotalSeconds}秒");return Task.CompletedTask;});try{return await timeOutPolicy.ExecuteAsync(async () =>{try{using (HttpClient client = new HttpClient()){HttpResponseMessage message = await client.GetAsync(url);//序列化响应结果string resContent = await message.Content.ReadAsStringAsync();return new OkObjectResult(resContent);}}catch (Exception e){return new ObjectResult(new ReturnMessageModel<string>{Code = "500",Message = $"服务错误:{e.Message}" + e.GetType().Name,Status = (int)HttpStatusCode.InternalServerError}){ StatusCode = 500 };}});}catch (TimeoutRejectedException e) {// 服务超时。return new ObjectResult(new ReturnMessageModel<string>{Code = "408",Message = "操作超时",}){ StatusCode = 408 };}}
3.2 重试策略

在分布式系统中,网络请求可能会因为临时故障(如网络抖动、服务暂时不可用)而失败。Polly 的重试策略(Retry Policy)是处理这类问题的有效工具,它可以在请求失败时自动重试,提高系统的稳定性和容错能力

固定次数重试策略

  /// <summary>/// 固定次数重试策略/// </summary>/// <param name="url"></param>/// <returns></returns>[HttpGet]public async Task<IActionResult> RetryPolicy(string url) {// 重试策略,对所有捕获的异常进行重试次数策略,当重试次数<= 设定值时,均会进入回调函数逻辑var policy = Policy.Handle<Exception>().RetryAsync(3, (exception, retryCount) => {Console.WriteLine($"重试第 {retryCount} 次: {exception.Message}");});try{return await policy.ExecuteAsync(async () =>{using (HttpClient client = new HttpClient()){HttpResponseMessage message = await client.GetAsync(url);//序列化响应结果string resContent = await message.Content.ReadAsStringAsync();return new OkObjectResult(resContent);}});}catch (Exception e) {return new ObjectResult(new ReturnMessageModel<string>{Code = "500",Message = $"服务错误:{e.Message}" + e.GetType().Name,Status = (int)HttpStatusCode.InternalServerError}){ StatusCode = 500 };}}
3.3 降级策略

服务降级(Service Degradation 是一种应对系统过载或依赖服务故障的弹性策略,通过暂时牺牲部分非核心功能或服务质量,确保系统核心功能的可用性和稳定性.一般是碰到异常时会给一个默认回调的形式去代替原有的服务

        /// <summary>/// 模拟从缓存中获得暂时的响应数据/// </summary>/// <returns></returns>private async Task<HttpResponseMessage> GetDataFromRedis() {HttpResponseMessage httpResponse = new HttpResponseMessage();httpResponse.StatusCode = HttpStatusCode.OK;var resdata=new  ReturnMessageModel<bool>("0",0,"从缓存中获得数据",true);string json = JsonConvert.SerializeObject(resdata);httpResponse.Content = new StringContent(json, Encoding.UTF8, "application/json");return httpResponse;}/// <summary>/// 服务降级策略【降级策略常常搭配其他策略一起使用】/// </summary>/// <param name="url"></param>/// <returns></returns>[HttpGet]public async Task<ReturnMessageModel<string>> DegradationPolicy(string url) {// 定义重试策略var retryPolicy = Policy<HttpResponseMessage>.Handle<Exception>().RetryAsync(3,onRetryAsync: (ex, times) => {Debug.WriteLine($"当前重试次数为 {times}");return Task.CompletedTask;});//  定义降级策略var fallbackPolicy = Policy<HttpResponseMessage>.Handle<Exception>().FallbackAsync(async (cancellaction) =>{//  该回调函数中执行降级的逻辑,比如说从缓存中获取逻辑Debug.WriteLine("进行了服务降级策略");return await GetDataFromRedis();});// Polly 处理策略,先重试,后降级// Policy.WrapAsync 包含多种执行策略// 在Polly中,策略的包裹顺序非常重要,因为策略的执行顺序是从最外层策略开始,逐层向内执行。// 当我们使用PolicyWrap时,通过Policy.WrapAsync方法组合策略,会形成一个策略管道,请求首先进入最先包裹的策略(最外层),然后依次向内传递,直到执行用户提供的原始委托。var policy = Policy.WrapAsync(fallbackPolicy,retryPolicy);// 使用降级策略去处理任务HttpResponseMessage res=   await policy.ExecuteAsync(async() =>{using (HttpClient client = new HttpClient()){HttpResponseMessage message = await client.GetAsync(url);return message;}});string resContent = await res.Content.ReadAsStringAsync();return new ReturnMessageModel<string>(resContent);}
3.4 熔断策略

Polly 的熔断策略(Circuit Breaker)用于在系统检测到持续故障时,自动断开(熔断)对特定服务的请求,防止系统资源被耗尽并快速失败。当熔断器处于开启状态时,所有请求会立即被拒绝(抛出 BrokenCircuitException),直到经过设定的恢复期后,熔断器会进入半开状态,允许部分请求尝试恢复服务。如果这些请求成功,则关闭熔断器;否则,继续保持开启。

熔断器包含三个状态

Closed(闭合):正常状态,操作允许执行。

Open(断开):熔断状态,所有操作被快速失败,不再尝试执行。

Half-Open(半开):熔断器尝试恢复,允许少量操作执行以测试依赖服务是否恢复。

Polly提供两种熔断器策略:

  1. 基于连续失败次数的熔断器(Basic Circuit Breaker)

  2. 基于高级失败率(滑动窗口)的熔断器(Advanced Circuit Breaker)

熔断器状态需要跨多个调用共享,因此通常将熔断器策略实例声明为静态或通过依赖注入容器注入(单例生命周期)。

BasicCircuitBreaker代码演示

  // 静态熔断器实例(状态持久化)private static readonly AsyncCircuitBreakerPolicy _circuitBreaker = Policy.Handle<Exception>().CircuitBreakerAsync(exceptionsAllowedBeforeBreaking: 4,durationOfBreak: TimeSpan.FromSeconds(30),onBreak: (ex, breakDelay) =>{Debug.WriteLine($"熔断开启!持续 {breakDelay.TotalSeconds} 秒。");},onReset: () =>{Debug.WriteLine("熔断关闭,恢复正常请求。");},onHalfOpen: () =>{Debug.WriteLine("熔断器进入半开状态,尝试恢复。");});
       /// <summary>/// 熔断策略[基于连续失败次数的熔断器]/// </summary>/// <param name="url">请求API</param>/// <returns></returns>[HttpGet]public async Task<ReturnMessageModel<string>> BasicCircuitBreakPolicy(string url) {// 重试策略var retryPolicy = Policy.Handle<Exception>().RetryAsync(5, (ex, qty) => {Thread.Sleep(100);Debug.WriteLine($"当前重试次数为 {qty} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");return Task.CompletedTask;});// 先重试 再熔断 最后降级服务var fallbackPolicy = Policy<string>.Handle<Exception>().FallbackAsync("先熔断降级策略结果", onFallbackAsync: _ => {Debug.WriteLine($"降级策略将要执行 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");return Task.CompletedTask;}).WrapAsync(_circuitBreaker.WrapAsync(retryPolicy));string res = await fallbackPolicy.ExecuteAsync(async() =>{using (HttpClient client = new HttpClient()){HttpResponseMessage message = await client.GetAsync(url);string messageStr=await message.Content.ReadAsStringAsync();return messageStr;}});return new ReturnMessageModel<string>(res); }

在这里插入图片描述

Advanced Circuit Breaker代码演示,将上述的基础异常次数_circuitBreaker熔断器换成_advancedCircuitBreaker

   private static readonly AsyncCircuitBreakerPolicy _advancedCircuitBreaker = Policy.Handle<Exception>().AdvancedCircuitBreakerAsync(failureThreshold: 0.5, // 失败率阈值(50%)samplingDuration: TimeSpan.FromSeconds(10), // 采样时间窗口(10秒)minimumThroughput: 5, // 最小吞吐量(10秒内至少8个请求)durationOfBreak: TimeSpan.FromSeconds(30), // 熔断持续时间onBreak: (ex, breakDelay) =>{Debug.WriteLine($"熔断开启!持续 {breakDelay.TotalSeconds} 秒。");},onReset: () =>{Debug.WriteLine("熔断关闭,恢复正常请求。");},onHalfOpen: () =>{Debug.WriteLine("熔断器进入半开状态,尝试恢复。");});

在这里插入图片描述

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

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

相关文章

【机器学习深度学习】大模型推理速度与私有化部署的价值分析

目录 前言 一、主流推理框架速度对比 二、为什么 HuggingFace 框架更适合微调验证&#xff1f; 三、大模型私有化部署的必要性分析 ✅ 私有化部署的主要动因 1. 数据隐私与业务安全 2. 可控性与性能保障 ❌ 哪些情况不建议私有部署&#xff1f; 四、总结与选型建议 &…

elementui-admin构建

1、vue-element-admin vue-element-admin是基于element-ui 的一套后台管理系统集成方案。 功能&#xff1a;介绍 | vue-element-adminA magical vue adminhttps://panjiachen.github.io/vue-element-admin-site/zh/guide/# GitHub地址&#xff1a;https://github.com/PanJia…

深入排查:编译环境(JDK)与运行环境(JRE/JDK)不一致时的常见 Java 错误及解决方案

深入排查&#xff1a;编译环境&#xff08;JDK&#xff09;与运行环境&#xff08;JRE/JDK&#xff09;不一致时的常见 Java 错误及解决方案 在后端 Java 项目中&#xff0c;编译环境&#xff08;JDK&#xff09; 与 运行环境&#xff08;JRE/JDK&#xff09; 版本不一致&…

[JS逆向] 微信小程序逆向工程实战

博客配套代码与工具发布于github&#xff1a;微信小程序 &#xff08;欢迎顺手Star一下⭐&#xff09; 相关爬虫专栏&#xff1a;JS逆向爬虫实战 爬虫知识点合集 爬虫实战案例 逆向知识点合集 前言&#xff1a; 微信小程序对于很多尝试JS逆向的人群来说&#xff0c;都是一个…

基于5G系统的打孔LDPC编码和均匀量化NMS译码算法matlab性能仿真

目录 1.引言 2.算法仿真效果演示 3.数据集格式或算法参数简介 4.算法涉及理论知识概要 4.1打孔技术 4.2 均匀量化NMS译码 5.参考文献 6.完整算法代码文件获得 1.引言 在5G通信系统中&#xff0c;信道编码技术是保障高速率、高可靠性数据传输的核心支撑&#xff0c;而低…

基于Java标准库读取CSV实现天地图POI分类快速导入PostGIS数据库实战

目录 前言 一、天地图POI分类简介 1、数据表格 2、分类结构 二、从CSV导入到PG数据库 1、CSV解析流程 2、数据转换及入库 3、入库成果及检索 三、总结 前言 在之前的博客中&#xff0c;曾经对高德地图和百度地图的POI分类以及使用PostGIS数据库来进行管理的模式进行了详…

人-AI交互中的信息论不同于传统的信息论,其信息的增量≠不确定性的减量

在人机交互&#xff08;Human-AI Interaction, HAI&#xff09;领域&#xff0c;信息论的应用确实与传统的信息论有所不同。这种差异主要源于人机交互HAI中信息的复杂性、动态性以及人类认知的特点。1. 传统信息论的核心概念传统信息论由克劳德香农&#xff08;Claude Shannon&…

K8s 通过 Scheduler Extender 实现自定义调度逻辑

1. 为什么需要自定义调度逻辑 什么是所谓的调度? 所谓调度就是指给 Pod 对象的 spec.nodeName 赋值 待调度对象则是所有 spec.nodeName 为空的 Pod 调度过程则是从集群现有的 Node 中为当前 Pod 选择一个最合适的 实际上 Pod 上还有一个平时比较少关注的属性&#xff1a;…

7.19 换根dp | vpp |滑窗

lcr147.最小栈通过两个栈 维护实现class MinStack { public:stack<int> A, B;MinStack() {}void push(int x) {A.push(x);if(B.empty() || B.top() > x)B.push(x);}void pop() {if(A.top() B.top())B.pop();A.pop();}int top() {return A.top();}int getMin() {retur…

以太坊的心脏与大脑:详解执行客户端(EL)与共识客户端(CL)

好的&#xff0c;各位技术同道&#xff0c;欢迎再次光临我的博客。在上一篇文章中&#xff0c;我们聊了如何搭建一个以太坊测试节点&#xff0c;并提到了节点需要同时运行“执行客户端”和“共识客户端”。很多朋友对此表示了浓厚兴趣&#xff0c;想深入了解这两者究竟是什么&a…

Debian-10,用glibc二进制预编译包,安装Mysql-5.7.44 笔记250716

Debian-10,用glibc二进制预编译包,安装Mysql-5.7.44 笔记250716 &#x1f4e6; 一步脚本 #!/bin/bash### 安装依赖 apt install -y libaio1 libnuma1 libncurses5### 下载MySQL-5.7.44 的 glib二进制包: mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz ,(如果不存在) mkdir…

用逻辑回归(Logistic Regression)处理鸢尾花(iris)数据集

# 导入必要的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from…

华大北斗TAU1201-1216A00高精度双频GNSS定位模块 自动驾驶专用

在万物互联的时代&#xff0c;您还在为定位不准、信号丢失而烦恼吗&#xff1f;TAU1201-1216A00华大北斗高精度定位模块TAU1201是一款高性能的双频GNSS定位模块&#xff0c;搭载了华大北斗的CYNOSURE III GNSS SoC 芯片&#xff0c;该模块支持新一代北斗三号信号体制&#xff0…

坚持继续布局32位MCU,进一步完善产品阵容,96Mhz主频CW32L012新品发布!

在全球MCU市场竞争加剧、国产替代加速的背景下&#xff0c;嵌入式设备对核心控制芯片的性能、功耗、可靠性及性价比提出了前所未有的严苛需求。为适应市场竞争&#xff0c;2025年7月16日&#xff0c;武汉芯源半导体正式推出基于CW32L01x系列低功耗微控制器家族的全新成员&#…

用线性代数推导码分多址(CDMA)

什么是码分多址 码分多址&#xff1a;CDMA允许多个用户同时、在同一频率上传输数据。它通过给每个用户分配唯一的、相互正交的二进制序列来实现区分。用户的数据比特被这个码片序列扩展成一个高速率的信号&#xff0c;然后在接收端通过相同的码片序列进行相关运算来回复原数据 …

mac 配置svn

1.查看brew的版本&#xff1a;brew install subversion2.安装brew命令&#xff1a;bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"3.把路径添加到path环境变量&#xff1a;echo export PATH"/opt/homebrew/b…

使用 .NET Core 的原始 WebSocket

在 Web 开发中&#xff0c;后端存在一些值得注意的通信协议&#xff0c;用于将更改通知给已连接的客户端。所有这些协议都用于处理同一件事。但鲜为人知的协议很少&#xff0c;鲜为人知的协议也很少。今天&#xff0c;将讨论 WebSocket&#xff0c;它在开发中使用最少&#xff…

编程实现Word自动排版:从理论到实践的全面指南

在现代办公环境中&#xff0c;文档排版是一项常见但耗时的工作。特别是对于需要处理大量文档的专业人士来说&#xff0c;手动排版不仅费时费力&#xff0c;还容易出现不一致的问题。本文将深入探讨如何通过编程方式实现Word文档的自动排版&#xff0c;从理论基础到实际应用&…

力扣经典算法篇-25-删除链表的倒数第 N 个结点(计算链表的长度,利用栈先进后出特性,双指针法)

1、题干 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a;输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 示例 3&…

VIT速览

当我们取到一张图片&#xff0c;我们会把它划分为一个个patch&#xff0c;如上图把一张图片划分为了9个patch&#xff0c;然后通过一个embedding把他们转换成一个个token&#xff0c;每个patch对应一个token&#xff0c;然后在输入到transformer encoder之前还要经过一个class …