netcore微服务Polly 实现熔断与降级机制
Polly的基本使用
Polly是一种.NET弹性和瞬态故障处理库,允许我们以非常顺畅和线程安全的方式来执诸如行重试,断路,超时,故障恢复等策略。 Polly针对对.NET 4.0,.NET 4.5和.NET Standard 1.1以及.NET Core实现,该项目作者现已成为.NET基金会一员,项目一直在不停迭代和更新,项目地址https://github.com/App-vNext/Polly
该库实现了七种恢复策略,下面我一一为您来介绍。
重试策略(Retry)
重试策略针对的前置条件是短暂的故障延迟且在短暂的延迟之后能够自我纠正。允许我们做的是能够自动配置重试机制。
断路器(Circuit-breaker)
断路器策略针对的前置条件是当系统繁忙时,快速响应失败总比让用户一直等待更好。保护系统故障免受过载,Polly可以帮其恢复。
超时(Timeout)
超时策略针对的前置条件是超过一定的等待时间,想要得到成功的结果是不可能的,保证调用者不必等待超时。
隔板隔离(Bulkhead Isolation)
隔板隔离针对的前置条件是当进程出现故障时,多个失败一直在主机中对资源(例如线程/ CPU)一直占用。下游系统故障也可能导致上游失败。这两个风险都将造成严重的后果。都说一粒老鼠子屎搅浑一锅粥,而Polly则将受管制的操作限制在固定的资源池中,免其他资源受其影响。
缓存(Cache)
缓存策略针对的前置条件是数据不会很频繁的进行更新,为了避免系统过载,首次加载数据时将响应数据进行缓存,如果缓存中存在则直接从缓存中读取。
回退(Fallback)
操作仍然会失败,也就是说当发生这样的事情时我们打算做什么。也就是说定义失败返回操作。
策略包装(PolicyWrap)
策略包装针对的前置条件是不同的故障需要不同的策略,也就意味着弹性灵活使用组合。
userapi添加项目Resilience
NuGet>Install-Package Polly
添加 interface IHttpClient
public interface IHttpClient
{
Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken=null, string requestId = null, string authorizationMethod = null);
Task<HttpResponseMessage> PostAsync(string uri, Dictionary<string,string> form, string authorizationToken=null, string requestId = null, string authorizationMethod = null); Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer"); Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
}
添加类 ResilientHttpClient
public class ResilientHttpClient : IHttpClient
{
private readonly HttpClient _client; //根据url origin创建policy
private readonly Func<string, IEnumerable<Policy>> _policyCreator;
//把policy打包成组合policy wraper,进行本地缓存
private readonly ConcurrentDictionary<string, PolicyWrap> _policyWrappers;
private ILogger<ResilientHttpClient> _logger;
private IHttpContextAccessor _httpContextAccessor;
public ResilientHttpClient(Func<string, IEnumerable<Policy>> policyCreator, ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor)
{
_client = new HttpClient();
_policyCreator = policyCreator;
_policyWrappers = new ConcurrentDictionary<string, PolicyWrap>();
_logger = logger;
_httpContextAccessor = httpContextAccessor;
} public async Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken=null, string requestId = null, string authorizationMethod = "Bearer")
{
Func<HttpRequestMessage> requestMessage = () => CreateHttpRequestMessage(HttpMethod.Post, uri, item);
return await DoPostPutAsync(HttpMethod.Post, uri, requestMessage, authorizationToken, requestId, authorizationMethod);
}
public async Task<HttpResponseMessage> PostAsync(string uri, Dictionary<string, string> form, string authorizationToken=null, string requestId = null, string authorizationMethod = null)
{
Func<HttpRequestMessage> requestMessage =()=>CreateHttpRequestMessage(HttpMethod.Post, uri, form);
return await DoPostPutAsync(HttpMethod.Post,uri, requestMessage, authorizationToken, requestId, authorizationMethod);
} public Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
Func<HttpRequestMessage> requestMessage = () => CreateHttpRequestMessage(HttpMethod.Put, uri, item);
return DoPostPutAsync(HttpMethod.Put, uri, requestMessage, authorizationToken, requestId, authorizationMethod);
} public Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri); return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri); if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
} var response = await _client.SendAsync(requestMessage); return await response.Content.ReadAsStringAsync();
});
}
public Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
var origin = GetOriginFromUri(uri); return HttpInvoker(origin, async () =>
{
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri); if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
} if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
} return await _client.SendAsync(requestMessage);
});
} private Task<HttpResponseMessage> DoPostPutAsync(HttpMethod method,string uri,Func<HttpRequestMessage>requestMessageAction, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
{
if (method != HttpMethod.Post && method != HttpMethod.Put)
{
throw new ArgumentException("Value must be either post or put.", nameof(method));
} //每次重试都必须创建新的stringcontent
//每次通讯后都会处理
var origin = GetOriginFromUri(uri); return HttpInvoker(origin, async () =>
{
HttpRequestMessage requestMessage = requestMessageAction(); SetAuthorizationHeader(requestMessage);
if (authorizationToken != null)
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
}
if (requestId != null)
{
requestMessage.Headers.Add("x-requestid", requestId);
} var response = await _client.SendAsync(requestMessage);
// 如果httpresponsecode 500,则引发异常
// 断路器跟踪故障所需
if (response.StatusCode == HttpStatusCode.InternalServerError)
{
throw new HttpRequestException();
}
return response;
});
} private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
{
var normalizedOrigin = NormalizeOrigin(origin); if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
{
policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray());
_policyWrappers.TryAdd(normalizedOrigin, policyWrap);
} // 执行应用全部的操作
// 包装器中定义的策略
return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin));
} private HttpRequestMessage CreateHttpRequestMessage<T>(HttpMethod method,string url, T item)
{
return new HttpRequestMessage(method, url)
{
Content = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json")
};
} private HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, string url, Dictionary<string, string> form)
{
return new HttpRequestMessage(method, url) { Content = new FormUrlEncodedContent(form) };
} private static string NormalizeOrigin(string origin)
{
return origin?.Trim().ToLower();
} private static string GetOriginFromUri(string uri)
{
var url = new Uri(uri);
var origin = $"{url.Scheme}://{url.DnsSafeHost}:{url.Port}";
return origin;
} private void SetAuthorizationHeader(HttpRequestMessage requestMessage)
{
var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authorizationHeader))
{
requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
} }
User.Identity 项目添加文件夹Infrastructure
新建 ResilienceHttpClientFactory
public class ResilienceHttpClientFactory
{
private ILogger<ResilientHttpClient> _logger;
private IHttpContextAccessor _httpContextAccessor;
private int _retryCount;
private int _exceptionCountAllowedBeforeBreacking; public ResilienceHttpClientFactory(ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor, int retryCount,int exceptionCountAllowedBeforeBreacking)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
_retryCount = retryCount;
_exceptionCountAllowedBeforeBreacking = exceptionCountAllowedBeforeBreacking;
} public ResilientHttpClient GetResilienceHttpClient()
{
return new ResilientHttpClient(origin => CreatePolicy(origin), _logger, _httpContextAccessor);
} private Policy[] CreatePolicy(string origin)
{
return new Policy[]{
Policy.Handle<HttpRequestException>().WaitAndRetryAsync(
_retryCount,
retryAttempt=>TimeSpan.FromSeconds(Math.Pow(2,retryAttempt)),
(exception,timeSpan,retryCount,context)=>
{
var msg=$"第{retryCount}implemented with polly's retryPolicy"+
$"of {context.PolicyKey}"+
$"at {context.ExecutionKey}"+
$"due to:{exception}.";
_logger.LogWarning(msg);
_logger.LogDebug(msg);
}
),
Policy.Handle<HttpRequestException>().CircuitBreakerAsync(_exceptionCountAllowedBeforeBreacking,TimeSpan.FromMinutes(1),
(excpeiton,duration)=>{
_logger.LogTrace("熔断器打开");
},()=>
{
_logger.LogTrace("");
}) };
}
}
Startup 修改
ConfigureServices添加 服务注册
//注册全局单利 ResilienceHttpClientFactory
services.AddSingleton(typeof(ResilienceHttpClientFactory), sp =>
{
var logger = sp.GetRequiredService<ILogger<ResilientHttpClient>>();
var httpcontextAccesser = sp.GetRequiredService<IHttpContextAccessor>();
var retryCount = 5;
var exceptionCountAllowedBeforeBreacking = 5;
return new ResilienceHttpClientFactory(logger, httpcontextAccesser, retryCount, exceptionCountAllowedBeforeBreacking);
}); //注册全局单利IHttpClient
services.AddSingleton<IHttpClient>(sp =>
{
return sp.GetRequiredService<ResilienceHttpClientFactory>().GetResilienceHttpClient();
});
UserService HttpClient修改为项目 Resilience IHttpClient
改造完毕运行项目
系统会默认记录允许的异常计数,和重试机制
正常运行

异常的时候重试

git : https://gitee.com/LIAOKUI/user.api
参考:https://github.com/HoussemDellai/ResilientHttpClient
https://github.com/App-vNext/Polly
https://blog.csdn.net/guwei9111986/article/details/51649240
netcore微服务Polly 实现熔断与降级机制的更多相关文章
- 微服务11:熔断、降级的Hystrix实现(附源码)
微服务1:微服务及其演进史 微服务2:微服务全景架构 微服务3:微服务拆分策略 微服务4:服务注册与发现 微服务5:服务注册与发现(实践篇) 微服务6:通信之网关 微服务7:通信之RPC 微服务8:通 ...
- .NET Core微服务之基于Polly+AspectCore实现熔断与降级机制
Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.熔断.降级与AOP 1.1 啥是熔断? 在广义的解释中,熔断主要是指为控制股票.期货或其他金融衍生产品的交易风险,为其单日价格波动幅度 ...
- .Net Core with 微服务 - Polly 服务降级熔断
在我们实施微服务之后,服务间的调用变的异常频繁.多个服务之间可能是互相依赖的关系.某个服务出现故障或者是服务间的网络出现故障都会造成服务调用的失败,进而影响到某个业务服务处理失败.某一个服务调用失败轻 ...
- Spring Cloud微服务如何实现熔断降级?
熔断限流概述 在基于Spring Cloud的微服务架构体系下,按照系统功能边界的不同划分,原先大而全的系统会被拆分为多个不同的微服务,而相应的微服务会提供一组功能关联的服务接口,并向系统中的其他微服 ...
- (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型
开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...
- (1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型
开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个 ...
- 什么是微服务架构,.netCore微服务选型
什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微 ...
- Polly+AspectCore实现熔断与降级机制
Polly+AspectCore实现熔断与降级机制 https://www.cnblogs.com/edisonchou/p/9159644.html 一.熔断.降级与AOP 1.1 啥是熔断? 在广 ...
- .NETCore微服务探寻(一) - 网关
前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...
- .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一)
原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一) 写下此文章只为了记录Surging微服务学习过程,并且分享给广大想学习surging的基友,方便广大 ...
随机推荐
- python 两个函数间如何调用
def a(): pass def b(): pass s=a() b(s) 或者 b(a())
- Python面向对象-反射
python面向对象-反射 在python的面向对象中,与用户交互的主要方式就是利用反射来判断用户输入的指令是否存在和可执行 反射的定义 在python中反射主要是应用于类的对象上,在运行时,将对象的 ...
- [tldr] GO泛型编程
最少的内容简述如何在GO中使用泛型编程 函数泛型 func f[T any](s Set[T]) { } 在函数声明的时候添加一个[]作为泛型的说明, 在使用的时候是可以自动推断 很多时候, any的 ...
- js回忆录(2) -- 逻辑表达式,条件语句
之所以要把逻辑表达式和条件语句放一块写一下,完全是因为二者的联系太过紧密,逻辑运算产生布尔值的结果,而条件语句则根据布尔值决定走那个分支. 逻辑与: &&, 首先逻辑与有逻辑与运算功能 ...
- 扩展知识:vscode配置easyx
扩展知识:vscode配置easyx 前言 因为个人用习惯了vscode,对于visual studio的操作只能说相当程度上很不适应,因此,我打算经历一番配置,让vscode可以配置上easyx ...
- Linux 防火墙及开放端口管理
查看防火墙是否开启systemctl status firewalld 若没有开启则是开启状态systemctl start firewalld 关闭则start改为stop 查看所有开启的端口fi ...
- 超值分享:ASN.1格式解析源码(未使用openssl),有助于分析证书、私钥等文件
源码下载地址 --下载服务由我的HttpServer服务器提供 一款简单的ASN.1格式解析工具,可将ASN.1格式输出,是你分析证书.私钥等文件的必备良器,比如查看公钥大数.私钥大数.加密算法.H ...
- Centos 安装 nload (流量监控)
QQ交流群:646559931.系统安装基本工具[root@localhost ~]# yum install -y gcc gcc-c++ make flex byacc libpcap ncurs ...
- 【软件】解决奥林巴斯生物显微镜软件OlyVIA提示“不支持您使用的操作系统”安装中止的问题
[软件]解决奥林巴斯生物显微镜软件OlyVIA提示"不支持您使用的操作系统"安装中止的问题 零.问题 资源在文末 问题如下,从奥林巴斯生物显微镜软件官网下载地址:https://l ...
- IIS7配置301永久重定向
我把我的小域名www.taadis.com301永久重定向到taadis.com. 关键图解: