.NET Core中Circuit Breaker
谈谈Circuit Breaker在.NET Core中的简单应用
前言
由于微服务的盛行,不少公司都将原来细粒度比较大的服务拆分成多个小的服务,让每个小服务做好自己的事即可。
经过拆分之后,就避免不了服务之间的相互调用问题!如果调用没有处理好,就有可能造成整个系统的瘫痪,好比说其中一些基础服务出现了故障,那么用到这些基础服务的地方都是要做一定的处理的,不能让它们出现大面积的瘫痪!!!
正常情况下的解决方案就要对服务进行熔断处理,不能因为提供方出现了问题就让调用方也废了。
熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施。
对于这个问题,Steeltoe的Circuit Breaker是一个不错的选择。本文的示例代码也是基于它的。
Steeltoe的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;
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("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
如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!
作者:Catcher ( 黄文清 )
.NET Core中Circuit Breaker的更多相关文章
- 谈谈Circuit Breaker在.NET Core中的简单应用
前言 由于微服务的盛行,不少公司都将原来细粒度比较大的服务拆分成多个小的服务,让每个小服务做好自己的事即可. 经过拆分之后,就避免不了服务之间的相互调用问题!如果调用没有处理好,就有可能造成整个系统的 ...
- Circuit Breaker Pattern(断路器模式)
Handle faults that may take a variable amount of time to rectify when connecting to a remote service ...
- Circuit Breaker模式
Circuit Breaker模式会处理一些需要一定时间来重连远程服务和远端资源的错误.该模式可以提高一个应用的稳定性和弹性. 问题 在类似于云的分布式环境中,当一个应用需要执行一些访问远程资源或者是 ...
- [AOP] 7. 一些自定义的Aspect - Circuit Breaker
Circuit Breaker(断路器)模式 关于断路器模式是在微服务架构/远程调用环境下经常被使用到的一个模式.它的作用一言以蔽之就是提高系统的可用性,在出现的问题通过服务降级的手段来保证系统的整体 ...
- Akka之Circuit Breaker
这周在项目中遇到了一个错误,就是Circuit Breaker time out.以前没有接触过,因此学习了下akka的断路器. 一.为什么使用Circuit Breaker 断路器是为了防止分布式系 ...
- 55.fielddata内存控制以及circuit breaker断路器
课程大纲 fielddata加载 fielddata内存限制 监控fielddata内存使用 circuit breaker 一.fielddata加载 fielddata加载到内存的过程是lazy加 ...
- .NET Core中的认证管理解析
.NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- Asp.net Core中使用Session
前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...
随机推荐
- 分享知识-快乐自己:Hibernate 关联映射
关联关系映射--概念: 关联关系是使用最多的一种关系,非常重要.在内存中反映为实体关系,映射到DB中为主外键关系. 实体间的关联,即对外键的维护.关联关系的发生,即对外键数据的改变. 外键:外面的主键 ...
- L93
Three Days to See(Excerpts) All of us have read thrilling stories in which the hero had only a limit ...
- ubuntu c++ 关机 重启 挂起 API
#include <unistd.h> #include <linux/reboot.h> int main() { reboot(LINUX_REBOOT_MAGIC1, L ...
- MySQL_产品昨日库存与历史入库历史出库成本_20161124
产品昨日库存与历史入库历史出库成本 SELECT d.ID,a.*,e.昨日订单额 ,b.昨天入库额,b.历史2天,b.历史3天,b.历史4天,b.历史5天,b.历史6天,b.历史7天,b.历史8天, ...
- Python3解leetcode Linked List Cycle
问题描述: Given a linked list, determine if it has a cycle in it. To represent a cycle in the given link ...
- 如何更改linux文件的拥有者及用户…
本文整理自: http://blog.163.com/yanenshun@126/blog/static/128388169201203011157308/ http://ydlmlh.iteye.c ...
- Hibernate注解详细介绍
引自http://blog.csdn.net/lin_yongrui/article/details/6855394 声明实体Bean @Entity public class Flig ...
- Matcher的replaceAll ()/appendReplacement()/appendTail()详细举例
直接上例子: package com.dajiangtai.djt_spider.util; import java.util.regex.Matcher;import java.util.regex ...
- Laravel框架接入短信平台进行用户注册短信验证
今天刚接触了一个短信接口平台,云通讯第三方短信提供服务商.http://www.yuntongxun.com/ 然后介绍一下怎么使用该短信平台来接入到自己的项目中. 首先你的去注册一个账号,然后根据提 ...
- Java socket异常
Java socket异常 分类: Java 2013-07-15 22:38 981人阅读 评论(0) 收藏 举报 目录(?)[+] 使用Java socket编写程序时,通常会遇到几种种异常:Bi ...