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 实现熔断与降级机制的更多相关文章

  1. 微服务11:熔断、降级的Hystrix实现(附源码)

    微服务1:微服务及其演进史 微服务2:微服务全景架构 微服务3:微服务拆分策略 微服务4:服务注册与发现 微服务5:服务注册与发现(实践篇) 微服务6:通信之网关 微服务7:通信之RPC 微服务8:通 ...

  2. .NET Core微服务之基于Polly+AspectCore实现熔断与降级机制

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.熔断.降级与AOP 1.1 啥是熔断? 在广义的解释中,熔断主要是指为控制股票.期货或其他金融衍生产品的交易风险,为其单日价格波动幅度 ...

  3. .Net Core with 微服务 - Polly 服务降级熔断

    在我们实施微服务之后,服务间的调用变的异常频繁.多个服务之间可能是互相依赖的关系.某个服务出现故障或者是服务间的网络出现故障都会造成服务调用的失败,进而影响到某个业务服务处理失败.某一个服务调用失败轻 ...

  4. Spring Cloud微服务如何实现熔断降级?

    熔断限流概述 在基于Spring Cloud的微服务架构体系下,按照系统功能边界的不同划分,原先大而全的系统会被拆分为多个不同的微服务,而相应的微服务会提供一组功能关联的服务接口,并向系统中的其他微服 ...

  5. (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...

  6. (1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个 ...

  7. 什么是微服务架构,.netCore微服务选型

    什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微 ...

  8. Polly+AspectCore实现熔断与降级机制

    Polly+AspectCore实现熔断与降级机制 https://www.cnblogs.com/edisonchou/p/9159644.html 一.熔断.降级与AOP 1.1 啥是熔断? 在广 ...

  9. .NETCore微服务探寻(一) - 网关

    前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...

  10. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一) 写下此文章只为了记录Surging微服务学习过程,并且分享给广大想学习surging的基友,方便广大 ...

随机推荐

  1. FastAPI路由与请求处理全解:手把手打造用户管理系统 🔌

    title: FastAPI路由与请求处理全解:手把手打造用户管理系统 date: 2025/3/2 updated: 2025/3/2 author: cmdragon excerpt: 通过咖啡店 ...

  2. SuperSocket 服务端 和 SuperSocket.ClientEngine 客户端及普通客户端

    internal class Program { //static void Main(string[] args) //{ // byte[] arr = new byte[1024]; // 1. ...

  3. 【插件介绍】Mesh2Geom插件

    Mesh to Geometry Plugin,来自达索官方论坛社区 原帖链接:Mesh to Geometry Plugin plugin feature: 允许Abaqus 用户从网格文件生成几何 ...

  4. Mysql导入数据的时候报错Unknown collation: 'utf8mb4_0900_ai_ci'什么问题?

    最近从线上把数据导出来想搭建到本地的时候报了这么一个错? [ERR] 1273 - Unknown collation: 'utf8mb4_0900_ai_ci' 这个错误究竟是什么原因影响的呢? 是 ...

  5. Qt解析JSON

    Qt解析JSON 文章目录 Qt解析JSON QT 解析json的三件套,QJsonDocument.QJsonObject.QJsonArray.QJsonValue JSON主要格式 QT的JSO ...

  6. Qt设置QTextEdit的行高

    Qt设置QTextEdit的行高 解决方法: QTextDocument* doc = ui->edtCountryIntroduce->document(); for(QTextBloc ...

  7. 200条Git命令复习总结使用

    新建 创建一个新的 git 版本库.这个版本库的配置.存储等信息会被保存到.git 文件夹中 # 初始化当前项目 $ git init # 新建一个目录,将其初始化为Git代码库 $ git init ...

  8. OkHTTP发送POST请求传送JSON数据

    导入依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> ...

  9. StringBuilder原理及StringBuffer

    1.StringBuilder的原理 StringBuilder是用来干什么的?为什么我们要学习StringBuilder?字符串拼接明明String也可以实现 答:StringBuilder可以大幅 ...

  10. CoreOS 手动更新

    以阿里云 ECS 安装的 CoreOS 为例,你家装的 CoreOS 基本也一样啦. 查看和修改更新组 第一个问题:"什么是更新组?",请先看CoreOS 发行版本介绍 # 查看更 ...