前言

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

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

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

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

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

Steeltoe的Circuit Breaker

Steeltoe是什么呢?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}");
} }

这里有几个地方要注意:

  1. 构造函数一定要有IHystrixCommandOptions这个参数
  2. RunAsync是真正执行调用的地方
  3. 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

谈谈Circuit Breaker在.NET Core中的简单应用的更多相关文章

  1. Hangfire在ASP.NET CORE中的简单实现

    hangfire是执行后台任务的利器,具体请看官网介绍:https://www.hangfire.io/ 新建一个asp.net core mvc 项目 引入nuget包 Hangfire.AspNe ...

  2. Hangfire在ASP.NET CORE中的简单实现方法

    hangfire是执行后台任务的利器,具体请看官网介绍:https://www.hangfire.io/ 新建一个asp.net core mvc 项目 引入nuget包 Hangfire.AspNe ...

  3. 在.NET Core中使用简单的插件化机制

    前言 插件化,其实也并不是什么新东西了,像nopCommerce等开源项目都有类似的机制,而且功能比较完善和齐全. 相信大家都对接过不少支付方式,支付宝.微信以及各大银行或第三方的支付公司. 我们可以 ...

  4. 在.net core中一个简单的加密算法

    using System; using System.Text; //System.Security下加密算法的命名空间 using System.Security.Cryptography; nam ...

  5. .NET Core中Circuit Breaker

    谈谈Circuit Breaker在.NET Core中的简单应用 前言 由于微服务的盛行,不少公司都将原来细粒度比较大的服务拆分成多个小的服务,让每个小服务做好自己的事即可. 经过拆分之后,就避免不 ...

  6. 浅谈ASP.NET Core中IOC与DI的理解和使用

    说起IOC和DI,使用过ASP.NET Core的人对这两个概念一定不陌生,早前,自己也有尝试过去了解这两个东西,但是一直觉得有点很难去理解,总觉得对其还是模糊不清,所以,趁着今天有空,就去把两个概念 ...

  7. 谈谈ASP.NET Core中的ResponseCaching

    前言 前面的博客谈的大多数都是针对数据的缓存,今天我们来换换口味.来谈谈在ASP.NET Core中的ResponseCaching,与ResponseCaching关联密切的也就是常说的HTTP缓存 ...

  8. Circuit Breaker Pattern(断路器模式)

    Handle faults that may take a variable amount of time to rectify when connecting to a remote service ...

  9. .NET Core 中的通用主机和后台服务

    简介 我们在做项目的时候, 往往要处理一些后台的任务. 一般是两种, 一种是不停的运行,比如消息队列的消费者.另一种是定时任务. 在.NET Framework + Windows环境里, 我们一般会 ...

随机推荐

  1. JAVA通过注解处理器重构代码,遵循单一职责

    前言:最近在看一个内部网关代码的时候,发现处理Redis返回数据这块写的不错,今天有时间好好研究下里面的知识点. 业务流程介绍: #项目是采用Spring Boot框架搭建的.定义了一个@Redis注 ...

  2. Spring(六):Spring&Struts2&Hibernate搭建的blog项目

    经过断断续续的学习.累积,终于基于别人的开源blog项目,变成了自己的第一个相对完整点的blog项目. 计划暂时把这个blog程序暂停------有更多(工作中用到的)东西需要去做,因此学习SSH b ...

  3. 05、NetCore2.0依赖注入(DI)之Web应用启动流程管理

    05.NetCore2.0依赖注入(DI)之Web应用启动流程管理 在一个Asp.net core 2.0 Web应用程序中,启动过程都做了些什么?NetCore2.0的依赖注入(DI)框架是如何管理 ...

  4. [转]pymongo常用操作函数

    pymongo 是 mongodb 的 python Driver Editor.记录下学习过程中感觉以后会常用多一些部分,以做参考. 1. 连接数据库 要使用pymongo最先应该做的事就是先连上运 ...

  5. php过滤表单提交的html等危险代码

    表单提交如果安全做得不好就很容易因为这个表单提交导致网站被攻击了,下面我来分享两个常用的php过滤表单提交的危险代码的实例,各位有需要的朋友可参考. PHP过滤提交表单的html代码里可能有被利用引入 ...

  6. Vue 项目代理设置的优化

    Vue 项目代理设置的优化 Vue 类的项目开发中项目结构基本都是类似于 Vue-cli 生成的方式, 这种方式开发中,最常用到的模式是开启代理进行 mock 调试或远程调试, 也就是使用了 Vue- ...

  7. Java进阶篇(一)——接口、继承与多态

    前几篇是Java的入门篇,主要是了解一下Java语言的相关知识,从本篇开始是Java的进阶篇,这部分内容可以帮助大家用Java开发一些小型应用程序,或者一些小游戏等等. 本篇的主题是接口.继承与多态, ...

  8. [LeetCode] Predict the Winner 预测赢家

    Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from eith ...

  9. [LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

    Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不 ...

  10. [USACO 13DEC]Vacation Planning(gold)

    Description Air Bovinia operates flights connecting the N farms that the cows live on (1 <= N < ...