Polly+HttpClientFactory
Polly
在.Net Core中有一个被.Net基金会认可的库Polly,它一种弹性和瞬态故障处理库,可以用来简化对服务熔断降级的处理。
Polly的策略主要由“故障”和“动作”两个部分组成,“故障”可以包括异常、超时等情况,“动作”则包括Fallback(降级)、重试(Retry)、熔断(Circuit-Breaker)等。策略则用来执行业务代码,当业务代码出现了“故障”中的情况时就开始执行“动作”。
主要包含以下功能:
- 重试(Retry)
- 断路器(Circuit-breaker)
- 超时检测(Timeout)
- 回退(FallBack)
- 策略包装(PolicyWrap)
故障定义
故障也可以说是触发条件,它使用Handle来定义,表示在什么情况下,才对其进行处理(熔断,降级,重试等)。
// 单一异常种类
Policy.Handle<HttpRequestException>();
// 带条件判断的单一异常
Policy.Handle<SqlException>(ex => ex.Number == 10)
// 多种异常
Policy.Handle<HttpRequestException>().Or<OperationCanceledException>()
// 多种异常
Policy.Handle<HttpRequestException>().OrResult<HttpResponseMessage>(res => res.StatusCode != HttpStatusCode.OK)
// 返回结果异常
Policy.HandleResult<HttpResponseMessage>(r => r.StatusCode != HttpStatusCode.OK)
重试(Retry)
重试就是指Polly在调用失败时捕获我们指定的异常,并重新发起调用,如果重试成功,那么对于调用者来说,就像没有发生过异常一样。在网络调用中经常出现瞬时故障,那么重试机制就非常重要。
var client = new HttpClient();
Policy
// 处理什么异常,比如httprequrest异常
.Handle<HttpRequestException>()
// 或者处理response的httpstatuscode 不等于200的情况
.OrResult<HttpResponseMessage>(res => res.StatusCode != HttpStatusCode.OK)
// 重试次数 3
.Retry(3,
(ex, retryCount,content) =>
{
Console.WriteLine($"请求Api异常,进行第{retryCount}次重试,ErrorCode:{ex.Result.StatusCode}");
})
// 要执行的任务
.Execute(() =>
{
HttpResponseMessage res = client.GetAsync("http://qa.xx.com/Social/policy/1").Result;
return res;
});
回退(FallBack)
回退也称服务降级,用来指定发生故障时的备用方案。
var client = new HttpClient();
Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(res => res.StatusCode != HttpStatusCode.OK)
// 出现异常只会回退处理
.Fallback(() =>
{
HttpResponseMessage res =
client.GetAsync("http://qa.xx.com/Social/policy/2").Result;
Console.WriteLine("Fallback(降级)处理.");
return res;
})
.Execute(() =>
{
HttpResponseMessage res =
client.GetAsync("http://qa.xx.com/Social/policy/1").Result;
return res;
});
超时(Timeou)
Polly支持两种超时策略:
- TimeoutStrategy.Pessimistic: 悲观模式
当委托到达指定时间没有返回时,不继续等待委托完成,并抛超时TimeoutRejectedException异常。 - TimeoutStrategy.Optimistic:乐观模式
这个模式依赖于 co-operative cancellation,只是触发CancellationTokenSource.Cancel函数,需要等待委托自行终止操作。
var timeoutPolicy = Policy.TimeoutAsync(1, TimeoutStrategy.Pessimistic,
(context, timespan, task) =>
{
Console.WriteLine("请求超时.");
return Task.CompletedTask;
});
timeoutPolicy.ExecuteAsync(async () =>
{
var client = new HttpClient();
await client.GetAsync("http://localhost:5000/home/delay");
return Task.CompletedTask;
});
熔断(Circuit-breaker)
如果调用某个目标服务出现过多超时、异常等情况,可以采取一定时间内熔断该服务的调用,熔断期间的请求将不再继续调用目标服务,而是直接返回,节约资源,提高服务的稳定性,熔断周期结束后如果目标服务情况好转则恢复调用。
熔断状态
- 打开(Open)
熔断器打开状态,此时对目标服务的调用都直接返回错误,熔断周期内不会走网络请求,当熔断周期结束时进入半开状态;
- 关闭(Closed)
关闭状态下正常发生网络请求,但会记录符合熔断条件的连续执行次数,如果错误数量达到设定的阈值(如果在没有达到阈值之前恢复正常,之前的累积次数将会归零),熔断状态进入到打开状态;
- 半开(Half-Open)
半开状态下允许定量的服务请求,如果调用都成功则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;
注意:为了服务的稳定性,在执行需要多次 Retry(重试策略)的情况下,最好组合熔断策略,预防可能存在的风险。
var client = new HttpClient();
var ciruitBreaker = Policy.Handle<Exception>()
// 熔断前允许出现3次错误,熔断时间10s,熔断时触发, 熔断恢复时触发,在熔断时间到了之后触发
.CircuitBreaker(3, TimeSpan.FromSeconds(10),
(ex, breakDelay) =>
{
//熔断时触发
Console.WriteLine("断路器打开,熔断触发.");
},
() =>
{
//熔断恢复时触发
Console.WriteLine("熔断器关闭了.");
},
() =>
{
//在熔断时间到了之后触发
Console.WriteLine("熔断时间到,进入半开状态");
}
// 模拟多次调用,触发熔断
for (int i = 1; i <= 150; i++)
{
try
{
ciruitBreaker.Execute(() =>
{
Console.WriteLine($"第{i}次开始执行.");
var res = client.GetAsync("http://localhost:5000/home/delay").Result;
Console.WriteLine($"第{i}次执行:正常:" + res.StatusCode);
Thread.Sleep(TimeSpan.FromSeconds(1));
return res;
});
}
catch (Exception e)
{
Console.WriteLine($"第{i}次执行:异常:" + e.Message);
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
熔断高级配置
根据时间段内总请求数中的异常比例触发熔断
var client = new HttpClient();
var advancedCircuitBreaker = Policy.Handle<Exception>()
.AdvancedCircuitBreaker(0.5, TimeSpan.FromSeconds(10), 3, TimeSpan.FromSeconds(10),
(ex, breakDelay) =>
{
Console.WriteLine("断路器打开,熔断触发.");
}, () =>
{
Console.WriteLine("熔断器关闭了.");
}, () =>
{
Console.WriteLine("熔断时间到,进入半开状态");
});
// 模拟多次调用,触发熔断
for (int i = 1; i <= 150; i++)
{
try
{
advancedCircuitBreaker.Execute(() =>
{
Console.WriteLine($"第{i}次开始执行.");
var res = client.GetAsync("http://localhost:5000/home/delay").Result;
Console.WriteLine($"第{i}次执行:正常:" + res.StatusCode);
Thread.Sleep(TimeSpan.FromSeconds(1));
return res;
});
}
catch (Exception e)
{
Console.WriteLine($"第{i}次执行:异常:" + e.Message);
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
策略包装(PolicyWrap)
策略包提供了一种灵活的方式来封装多个弹性策略(从右往左).
//定义超时
var timeOut = Policy.Timeout(TimeSpan.FromSeconds(10),
((context, timespan, task) =>
{
Console.WriteLine("请求超时.");
}));
//定义重试
var retry = Policy.Handle<Exception>()
.Retry(3, ((exception, retryCount, context) =>
{
Console.WriteLine($"第{retryCount}次重试.");
})
// 定义熔断策略
var circuitBreaker = Policy.Handle<Exception>()
// 熔断前允许出现3次错误,熔断时间10s,熔断时触发, 熔断恢复时触发,在熔断时间到了之后触发
.CircuitBreaker(3, TimeSpan.FromSeconds(10),
(ex, breakDelay) =>
{
//熔断时触发
Console.WriteLine("断路器打开,熔断触发.");
},
() =>
{
//熔断恢复时触发
Console.WriteLine("熔断器关闭了.");
},
() =>
{
//在熔断时间到了之后触发
Console.WriteLine("熔断时间到,进入半开状态");
});
//定义回退策略
var fallback = Policy.Handle<Exception>()
.Fallback(() =>
{
Console.WriteLine("正在降级处理.");
}
fallback.Wrap(Policy.Wrap(circuitBreaker,retry, timeOut)).Execute(() =>
{
Console.WriteLine("start.");
});
HttpClientFactory
- 提供一个中心位置,用于命名和配置逻辑 HttpClient 对象.
- 管理 HttpClientMessageHandlers 的生存期,避免在HttpClient 生存期时出现问题.
- 在 HttpClient 中委托处理程序并实现基于 Polly 的中间件以利用 Polly 的复原策略。
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0
简单使用
Install Microsoft.Extensions.Http
如果有多个可以同时使用
// StartUp->ConfigureServices
services.AddHttpClient("local",options =>
{
options.BaseAddress = new Uri("http://localhost:5000");
}
services.AddHttpClient("fanyou",options =>
{
options.BaseAddress = new Uri("http://qa.fanyouvip.com");
});
//使用
[Route("client")]
public class ClientController : ControllerBase
{
private readonly IHttpClientFactory _clientFactory;
public ClientController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
[HttpGet("Local")]
public async Task<IActionResult> Local()
{
var client = _clientFactory.CreateClient("local");
var res = await client.GetAsync("/home/delay");
return Ok(res);
}
[HttpGet("Fanyou")]
public async Task<IActionResult> Fanyou()
{
var client = _clientFactory.CreateClient("fanyou");
var res = await client.GetAsync("/social");
return Ok(res);
}
}
结合Polly
Install Microsoft.Extensions.Http.Polly
// 第一种方式
services.AddHttpClient("local",
options => { options.BaseAddress = new Uri("http://localhost:5000"); })
.AddTransientHttpErrorPolicy(p =>
{
var handlers = p.OrResult(result => result.StatusCode != HttpStatusCode.OK)
.RetryAsync(3,
(ex, retryCount, context) =>
{
Console.WriteLine($"第{retryCount}次重试.异常:{ex.Exception.Message}");
});
return handlers;
}).AddTransientHttpErrorPolicy(p =>
{
var breaker = p.CircuitBreakerAsync(3, TimeSpan.FromSeconds(10));
return breaker;
});
//第二种方式
services.AddHttpClient("Test",
options => { options.BaseAddress = new Uri("http://localhost:5003"); })
.AddPolicyHandler(RetryPolicy())
.AddPolicyHandler(CircuiBreakerPolicy());
/// <summary>
/// 重试策略
/// </summary>
/// <returns>IAsyncPolicy<HttpResponseMessage></returns>
private IAsyncPolicy<HttpResponseMessage> RetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(res => res.StatusCode != HttpStatusCode.OK)
.WaitAndRetryAsync(3, retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
/// <summary>
/// 熔断策略
/// </summary>
/// <returns>IAsyncPolicy<HttpResponseMessage></returns>
private IAsyncPolicy<HttpResponseMessage> CircuiBreakerPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(5, TimeSpan.FromMinutes(1));
}
Polly+HttpClientFactory的更多相关文章
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(下篇)
译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(中篇)
译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的(包括标题).其中可能会去除一些不影响理解但本人实在不知道如 ...
- 在 .NET Core 中结合 HttpClientFactory 使用 Polly(上篇)
译者:王亮作者:Polly 团队原文:http://t.cn/EhZ90oq 译者序一:前两天写了一篇文章 .NET Core 开源项目 Polly 介绍,在写这篇文章查看 Polly 资料时,看到了 ...
- ASP.Net Core2.1中的HttpClientFactory系列二:集成Polly处理瞬态故障
前言:最近,同事在工作中遇到了使用HttpClient,有些请求超时的问题,辅导员让我下去调研一下,HttpClinet的使用方式已经改成了之前博客中提到的方式,问题的原因我已经找到了,就是因为使用了 ...
- asp.net core 使用HttpClientFactory Polly实现熔断降级
前言 在++NET Core2.1++后也是增加更新了很多东西,当然HttpClientFactory更新中的一部分.虽然说HttpClient这个实现了disposable,但使用它的时候用usin ...
- ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly实现瞬时故障处理
原文:https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling发表于:2018 ...
- HttpClientFactory与Steeltoe结合来完成服务发现
前言 上一篇说了一下用HttpClientFactory实现了简单的熔断降级. 这篇就来简单说说用HttpClientFactory来实现服务发现.由于标题已经好明显的说了Steeltoe 因此这里会 ...
- 用HttpClientFactory来实现简单的熔断降级
前言 在2.1之后,有不少新东西,其中HttpClientFactory算是一个.HttpClientFactory涉及的东西也不算少,三四种clients , 请求中间件,与Polly的结合,生命周 ...
- .NetCore 2.1中的HttpClientFactory最佳实践
.NET Core 2.1中的HttpClientFactory最佳实践 ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpCl ...
- .NET Core 2.1中的HttpClientFactory最佳实践
ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题. 介绍 ...
随机推荐
- 关于腾讯会议pc端的使用教程
一.首先先在官网下载pc端 下面是连接:腾讯会议_腾讯会议下载- 腾讯云 (tencent.com) 点击免费下载即可. 二.登录并创建会议 选择一种登录方式. 创建会议有快速会议和预定会议两种方式 ...
- 【测试平台开发】——01后端web开发框架Flask
官方中文地址:https://flask.net.cn/ 官方英文地址:https://flask.palletsprojects.com/en/2.1.x/ github地址:https://git ...
- Prometheus 告警恢复时,怎么获取恢复时的值?
Prometheus 告警事件中的 $value 表示当前告警触发时的值,但是在告警恢复时,Resolved 事件中的 $value 仍然是最新告警时的值,并非是恢复时的值,这是什么原因和原理?是否有 ...
- 通过DashScope API调用将多种模态转换为向量
本文介绍如何通过模型服务灵积DashScope进行 多模态向量生成 ,并入库至向量检索服务DashVector中进行向量检索. 模型服务灵积DashScope,通过灵活.易用的模型API服务,让各种模 ...
- Pipeline流水线通过git拉取Jenkinsfile报错 error: RPC failed; result=22, HTTP code = 404
Pipeline流水线通过git拉取Jenkinsfile报错 error: RPC failed; result=22, HTTP code = 404 在学习共享库时使用通过git拉取jenkin ...
- Powershell 重新排列 Windows环境变量
最近乱搞环境变量,然后有些重复了,遂写个脚本去重下排序下. 环境变量有长度限制,如果超出了,比如SqlServer相关的,将共同路径单独搞个变量声明下,比如 将其路径手动替换成如下,可大幅压缩变量长度 ...
- MySQL服务无法启动 服务没有报告任何错误
安装MYSQL后 启动服务 出现错误 在启动MySQL服务时 出现该报错 解决方法: 将原本在MySQL根目录下的my.ini文件移动到bin目录下(my.ini文件参考:这里) 删除根目录下的 ...
- 在C#中使用适配器Adapter模式和扩展方法解决面向的对象设计问题
之前有阵子在业余时间拓展自己的一个游戏框架,结果在实现的过程中发现一个设计问题.这个游戏框架基于MonoGame实现,在MonoGame中,所有的材质渲染(Texture Rendering)都是通过 ...
- USB总线-Linux内核USB3.0 Hub驱动分析(十四)
1.概述 USB Hub提供了连接USB主机和USB设备的电气接口.USB Hub拥有一个上行口,至少一个下行口,上行口连接上一级的Hub的下行口或者USB主机,连接主机的为Root Hub,下行口连 ...
- 如何集成化管理API_方便企业内外部调用?
API 已成为企业数字战略中不可或缺的一部分.它们使不同软件系统.应用程序和服务之间能够高效.灵活地相互沟通.API不仅能提升企业内部各部门之间的协作效率,还能加强与外部合作伙伴及客户之间的互动. A ...