谈谈Circuit Breaker在.NET Core中的简单应用

谈谈Circuit Breaker在.NET Core中的简单应用,第1张

概述前言由于微服务的盛行,不少公司都将原来细粒度比较大的服务拆分成多个小的服务,让每个小服务做好自己的事即可。经过拆分之后,就避免不了服务之间的相互调用问题!如果调用没有处理好,就有可能造成整个系统的瘫痪,好比说其中一些基础服务出现了故障,那么用到这些基础服务的地方都是要做一定的处理的,不能让它们出现大面积的瘫痪!!!正常情况下的解决方案就要对服务进行熔断处理,不能因为提供方出现了问题就让调用方也废了。熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施。对于这个问题,Steeltoe的Circuit Breaker是一个不错的选择。本文的示例代码也是基于它的。Steeltoe的Circuit BreakerSteeltoe是什么呢?Steeltoe可以说是构建微服务的一个解决方案吧。具体的可以访问它的官网:http://steeltoe.io/回归正题,先来看看官方对Circuit Breaker的描述:What do you do when a service you depend on stops responding? Circuit breakers enable you to bypass a failing service, allowing it time to recover, and preventing your users from seeing nasty error messages. Steeltoe includes a .NET implementation of Netflix Hystrix, a proven circuit breaker implementation with rich metrics and monitoring features.不难发现,Circuit Breaker可以让我们很好的处理失败的服务。它也包含了对Netflix Hystrix的.NET(Core)实现。关于熔断机制,有个非常经典的图(这里直接拿了官方文档的图),核心描绘的就是三种状态之间的变化关系。说了那么多,下面还是来看个简单的例子来略微深入理解一下吧。注:服务发现和服务注册不是本文的重点,所以这里不会使用Steeltoe相应的功能。简单例子先定义一个简单的订单服务,这个服务很简单,就一个返回直接返回对应订单号的接口,这里用默认的ASP.NET Core Web API项目做一下调整就好了。[Route("api/[controller]")]public class ValuesController : Controller{// GET api/values/123[HttpGet("{id}")]public string Get(string id){return $"order-{id}";}}再来一个新服务去调用上面的订单服务。先抛开熔断相关的,定义一个用于访问订单服务的Service接口和实现。public interface IOrderService{Task<string> GetOrderDetailsAsync(string orderId);}public class OrderService : IOrderService{public async Task<string> GetOrderDetailsAsync(string orderId){using (HttpClient client = new HttpClient()){return await client.GetStringAsync($"http://localhost:9999/api/values/{orderId}");}}}比较简单,就是发起HTTP请求到订单服务,拿一下返回的结果。忽略熔断的话,现在已经可以通过这个OrderService去拿到结果了。[HttpGet]public async Task<string> Get([FromServices] Services.IOrderService service, string id = "0"){return await service.GetOrderDetailsAsync(id);}结果如下:这是最最最最理想的情况!如果我们把订单服务停了,会发生什么事呢?十分尴尬,这个订单服务的调用方也废了。当然,try-catch也是可以帮我们处理这个尴尬的问题,但这并不是我们想要的结果啊!下面来看看引入Circuit Breaker之后如何略微优雅一点去处理这个问题。定义一个GetOrderDetailsHystrixCommand,让它继承HystrixCommand。public class GetOrderDetailsHystrixCommand : HystrixCommand<string>{private readonly IOrderService _service;private readonly ILogger<GetOrderDetailsHystrixCommand> _logger;private string _orderId;public GetOrderDetailsHystrixCommand(IHystrixCommandOptions options,IOrderService service,ILogger<GetOrderDetailsHystrixCommand> logger) : base(options){this._service = service;this._logger = logger;this.IsFallbackUserDefined = true;}public async Task<string> GetOrderDetailsAsync(string orderId){_orderId = orderId;return await ExecuteAsync();}protected override async Task<string> RunAsync(){var result = await _service.GetOrderDetailsAsync(_orderId);_logger.LogInformation("Get the result : {0}", result);return result;}protected override async Task<string> RunFallbackAsync(){//断路器已经打开if (!this._circuitBreaker.AllowRequest){return await Task.FromResult("Please wait for sometimes");}_logger.LogInformation($"RunFallback");return await Task.FromResult<string>($"RunFallbackAsync---OrderId={_orderId}");}}这里有几个地方要注意:构造函数一定要有IHystrixCommandOptions这个参数RunAsync是真正执行调用的地方RunFallbackAsync是由于某些原因不能拿到返回结果时会执行的地方,所谓的优雅降级。接下来要做的是在Startup中进行注册。public void ConfigureServices(IServiceCollection services){services.AddSingleton<IOrderService, OrderService>();services.AddHystrixCommand<GetOrderDetailsHystrixCommand>("Order", Configuration);services.AddMvc();}可以看到,在添加熔断命令的时候,还用到了Configuration这个参数,这就说明,我们还少了配置!!配置是放到appsettings.json里面的,下面来看一下要怎么配置:{"hystrix": {"command": {"default": {"circuitBreaker": {//是否启用,默认是true"enabled": true,//在指定时间窗口内,熔断触发的最小个数"requestVolumeThreshold": 5,//熔断多少时间后去尝试请求"sleepWindowInMilliseconds": 5000,//失败率达到多少百分比后熔断"errorThresholdPercentage": 50,//是否强制开启熔断"forceOpen": false,//是否强制关闭熔断"forceClosed": false},//是否启用fallback"fallback": {"enabled": true}}}}}需要添加一个名字为hystrix的节点,里面的command节点才是我们要关注的地方!default,是默认的配置,针对所有的Command!如果说有某个特定的Command要单独配置,可以在command下面添加相应的命令节点即可。其他配置项,都已经用注释的方式解释了。下面这张动图模拟了订单服务从可用->不可用->可用的情形。除了服务不可用,可能还有一种情况发生的概率会比较大,超时!举个例子,有一个服务平常都是响应很快,突然有一段时间不知道什么原因,处理请求的速度慢了很多,这段时间内经常出现客户端等待很长的时间,甚至超时了。当遇到这种情况的时候,一般都会设置一个超时时间,只要在这个时间内没有响应就认为是超时了!可以通过下面的配置来完成超时的配置:{"hystrix": {"command": {"default": {"execution": {"timeout": {"enabled": true},"isolation": {"strategy": "THREAD","thread": {//超时时间"timeoutInMilliseconds": 1000}}},}}}}总结这里也只是介绍了几个比较常用和简单的功能,它还可以合并多个请求,缓存请求等诸多实用的功能。总体来说,Steeltoe的熔断功能,用起来还算是比较简单,也比较灵活。更多配置和说明可以参考官方文档。本文的示例代码:CircuitBreakerDemo

<h2 ID="前言">前言

由于微服务的盛行,不少公司都将原来细粒度比较大的服务拆分成多个小的服务,让每个小服务做好自己的事即可。

经过拆分之后,就避免不了服务之间的相互调用问题!如果调用没有处理好,就有可能造成整个系统的瘫痪,好比说其中一些基础服务出现了故障,那么用到这些基础服务的地方都是要做一定的处理的,不能让它们出现大面积的瘫痪!!!

正常情况下的解决方案就要对服务进行熔断处理,不能因为提供方出现了问题就让调用方也废了。

熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施。

对于这个问题,的Circuit Breaker是一个不错的选择。本文的示例代码也是基于它的。

Steeltoe是什么呢?Steeltoe可以说是构建微服务的一个解决方案吧。具体的可以访问它的官网:

回归正题,先来看看官方对Circuit Breaker的描述:

What do you do when a service you depend on stops responding? Circuit breakers enable you to bypass a failing service,allowing it time to recover,and preventing your users from seeing nasty error messages. Steeltoe includes a .NET implementation of Netflix Hystrix,a proven circuit breaker implementation with rich metrics and monitoring features.

不难发现,Circuit Breaker可以让我们很好的处理失败的服务。它也包含了对Netflix Hystrix的.NET(Core)实现。

关于熔断机制,有个非常经典的图(这里直接拿了官方文档的图),核心描绘的就是三种状态之间的变化关系。

说了那么多,下面还是来看个简单的例子来略微深入理解一下吧。

注:服务发现和服务注册不是本文的重点,所以这里不会使用Steeltoe相应的功能。

先定义一个简单的订单服务,这个服务很简单,就一个返回直接返回对应订单号的接口,这里用默认的ASP.NET Core Web API项目做一下调整就好了。

[Route("API/[controller]")]public class ValuesController : Controller{    // GET API/values/123    [httpGet("{ID}")]    public string Get(string ID)    {        return $"order-{ID}";    }        }

再来一个新服务去调用上面的订单服务。

先抛开熔断相关的,定义一个用于访问订单服务的Service接口和实现。

public interface IOrderService{    Task GetorderDetailsAsync(string orderID);}

public class OrderService : IOrderService
{
public async Task GetorderDetailsAsync(string orderID)
{
using (httpClIEnt clIEnt = new httpClIEnt())
{
return await clIEnt.GetStringAsync($"http://localhost:9999/API/values/{orderID}");
}
}
}

比较简单,就是发起http请求到订单服务,拿一下返回的结果。

忽略熔断的话,现在已经可以通过这个OrderService去拿到结果了。

[httpGet]public async Task Get([FromServices] Services.IOrderService service,string ID = "0"){    return await service.GetorderDetailsAsync(ID);}

结果如下:

这是最最最最理想的情况!如果我们把订单服务停了,会发生什么事呢?

十分尴尬,这个订单服务的调用方也废了。

当然,try-catch也是可以帮我们处理这个尴尬的问题,但这并不是我们想要的结果啊!

下面来看看引入Circuit Breaker之后如何略微优雅一点去处理这个问题。

定义一个GetorderDetailsHystrixCommand,让它继承HystrixCommand

public class GetorderDetailsHystrixCommand : HystrixCommand{    private Readonly IOrderService _service;    private Readonly ILogger _logger;    private string _orderID;
pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c G<a href="https://www.jb51.cc/tag/eto/" target="_blank" >eto</a>rderDetailsHystrixCommand(    IHystrixCommandOptions options,IOrderService service,ILogger<GetOrderDetailsHystrixCommand> logger    ) : base(options){    this._service = service;    this._logger = logger;    this.IsFallbackUser<a href="https://m.jb51.cc/tag/define/" target="_blank" >define</a>d = true;}pub<a href="https://m.jb51.cc/tag/li/" target="_blank" >li</a>c async Task<string> G<a href="https://www.jb51.cc/tag/eto/" target="_blank" >eto</a>rderDetailsAsync(string order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>){    _order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a> = order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>;    return await ExecuteAsync();}protected overr<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>e async Task<string> RunAsync(){    var res<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t = await _service.G<a href="https://www.jb51.cc/tag/eto/" target="_blank" >eto</a>rderDetailsAsync(_order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>);    _logger.Log<a href="https://www.jb51.cc/tag/informat/" target="_blank" >informat</a>ion("Get the res<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t : {0}",res<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t);    return res<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t;}protected overr<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>e async Task<string> RunFallbackAsync(){    //断路器已经打开    if (!this._circuitBreaker.AllowRequest)    {        return await Task.Fr<a href="https://www.jb51.cc/tag/omr/" target="_blank" >omr</a>es<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t("Please wait for sometimes");    }    _logger.Log<a href="https://www.jb51.cc/tag/informat/" target="_blank" >informat</a>ion($"RunFallback");    return await Task.Fr<a href="https://www.jb51.cc/tag/omr/" target="_blank" >omr</a>es<a href="https://m.jb51.cc/tag/ul/" target="_blank" >ul</a>t<string>($"RunFallbackAsync---Order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>={_order<a href="https://m.jb51.cc/tag/ID/" target="_blank" >ID</a>}");}

}

这里有几个地方要注意:

构造函数一定要有IHystrixCommandOptions这个参数RunAsync是真正执行调用的地方RunFallbackAsync是由于某些原因不能拿到返回结果时会执行的地方,所谓的优雅降级。

接下来要做的是在Startup中进行注册。

public voID ConfigureServices(IServiceCollection services){    services.AddSingleton();    services.AddHystrixCommand("Order",Configuration);
services.AddMvc();

}

可以看到,在添加熔断命令的时候,还用到了Configuration这个参数,这就说明,我们还少了配置!!

配置是放到appsettings.Json里面的,下面来看一下要怎么配置:

{  "hystrix": {    "command": {      "default": {        "circuitBreaker": {          //是否启用,默认是true          "enabled": true,//在指定时间窗口内,熔断触发的最小个数          "requestVolumeThreshold": 5,//熔断多少时间后去尝试请求          "sleepWindowInMilliseconds": 5000,//失败率达到多少百分比后熔断          "errorThresholdPercentage": 50,//是否强制开启熔断          "forceOpen": false,//是否强制关闭熔断          "forceClosed": false        },//是否启用fallback        "fallback": {          "enabled": true        }      }    }  }} 

需要添加一个名字为hystrix的节点,里面的command节点才是我们要关注的地方!

default,是默认的配置,针对所有的Command!如果说有某个特定的Command要单独配置,可以在command下面添加相应的命令节点即可。

其他配置项,都已经用注释的方式解释了。

下面这张动图模拟了订单服务从可用->不可用->可用的情形。

除了服务不可用,可能还有一种情况发生的概率会比较大,超时!

举个例子,有一个服务平常都是响应很快,突然有一段时间不知道什么原因,处理请求的速度慢了很多,这段时间内经常出现客户端等待很长的时间,甚至超时了。

当遇到这种情况的时候,一般都会设置一个超时时间,只要在这个时间内没有响应就认为是超时了!

可以通过下面的配置来完成超时的配置:

{  "hystrix": {    "command": {      "default": {        "execution": {          "timeout": {            "enabled": true          },"isolation": {            "strategy": "THREAD","thread": {              //超时时间              "timeoutInMilliseconds": 1000            }           }        },}    }  }} 

这里也只是介绍了几个比较常用和简单的功能,它还可以合并多个请求,缓存请求等诸多实用的功能。总体来说,Steeltoe的熔断功能,用起来还算是比较简单,也比较灵活。

更多配置和说明可以参考。

本文的示例代码:

总结

以上是内存溢出为你收集整理的谈谈Circuit Breaker在.NET Core中的简单应用全部内容,希望文章能够帮你解决谈谈Circuit Breaker在.NET Core中的简单应用所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1256024.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存