属于服务器端运行的程序_【西天取经】:实现net core自定义服务器端熔断操作...

网上关于net core实现服务熔断的都是客户端的例子,下面我来分享一个服务器端的例子,可以针对每一个用户请求做到服务器端的熔断操作。

在.neter里面,很多programer对流控里面熔断的知识了解不够全面,以为单纯用AspNetCoreRateLimit这样的包做一些流量入口的限制就可以了,其实流控里面还有很多的知识点,比如:超时,熔断,漏斗等等,像类似这样的流控规则必须要让用户请求真正进到程序内部才能做更多的规则限制。

.net core里面目前做的最好最权威是第三方包Polly。网上关于Polly的HttpClientFactory文章很多,这些例子它们的共同点都是在客户端发起请求的时候做了熔断的控制,可是真实的互联网环境里你只要把业务公开,暴露URL在互联网上,就会有很多客户或者流量进来,不管是好的坏的流量全都会过来,这时候服务器端的压力自然就会上来,能多一个流控规则就显得非常重要了,加上之前大家已经了解过的AspNetCoreRateLimit可以限制访问数量一起配合使用,会让你的程序运行的时候更加稳定。

正式介绍代码之前,这里多啰嗦一下,做一个简单的比喻,AspNetCoreRateLimit熔断器之间的关系是:AspNetCoreRateLimit好比是家里的电源总开关,熔断器好比是家里电源总开关后面的那个保险丝。只有双剑合璧珠联璧合才能更好地保护运行时的程序,特别是现在程序都运行在docker或者k8s里面,更需要一个类似像保险丝一样的功能出现来保护我们运行的程序。于是乎这篇文章就诞生了。其实在java微服务里面早就有了这块知识,我这里只是把java里面的砖头搬了过来,并且没有使用任何和微服务框架有关系的框架实现,做到真正意义上可独立在微服务体系之外的断路器。因为我认为在k8s里面,类似微服务框架(Eureka,Nacos,Ocelot)可以省下不用了,k8s里面本身就是用服务名进行通信的,在多用一层感觉有点多余了。另外这些大的框架在运行像网站这样的程序时还是代码重了一些,只选择微服务里面的某几个功能拆出来用会更轻便更好掌握。虽然轮子我不会造,但车子我还是有自信能出一辆性能非常好的跑车来。

通过middleware可以实现服务器端针对用户级别的熔断。感谢张浩帮忙。

首先就是用VS创建一个WebAPI的项目出来,然后把下面的代码copy过去就可以了用了。

中间件CircuitBreakerMiddleware.cs

using System;using System.Collections.Concurrent;using System.Net;using System.Threading.Tasks;using Microsoft.AspNetCore.Http;using Microsoft.AspNetCore.Http.Extensions;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.Logging;using Polly;using Polly.CircuitBreaker;namespace CircuitBreakerDemo{    ///     /// net core 实现自定义断路器中间件    ///     public class CircuitBreakerMiddleware : IDisposable    {        private readonly RequestDelegate _next;        private readonly ConcurrentDictionary<string, AsyncPolicy> _asyncPolicyDict;        private readonly ILogger _logger;        private readonly IConfiguration _configuration;        public CircuitBreakerMiddleware(RequestDelegate next, ILogger logger, IConfiguration configuration)        {            this._next = next;            this._logger = logger;            this._configuration = configuration; //未来url的断路规则可以从config文件里读取,增加灵活性            logger.LogInformation($"{nameof(CircuitBreakerMiddleware)}.Ctor()");            this._asyncPolicyDict = new ConcurrentDictionary<string, AsyncPolicy>(Environment.ProcessorCount, 31);        }        public async Task InvokeAsync(HttpContext context)        {            var request = context.Request;            try            {                await this._asyncPolicyDict.GetOrAdd(string.Concat(request.Method, request.GetEncodedPathAndQuery())                                                     , key => Policy.Handle()                                                                    .AdvancedCircuitBreakerAsync                                                                    (                                                                        //备注:20秒内,请求次数达到10次以上,失败率达到20%后开启断路器,断路器一旦被打开至少要保持5秒钟的打开状态。                                                                        failureThreshold: 0.2D,                       //失败率达到20%熔断开启                                                                        minimumThroughput: 10,                        //最多调用10次                                                                        samplingDuration: TimeSpan.FromSeconds(20),   //评估故障持续时长20秒                                                                        durationOfBreak: TimeSpan.FromSeconds(5),     //恢复正常使用前电路保持打开状态的最少时长5秒                                                                        onBreak: (exception, breakDelay, context) =>  //断路器打开时触发事件,程序不能使用                                                                        {                                                                            var ex = exception.InnerException ?? exception;                                                                            this._logger.LogError($"{key} => 进入打开状态,中断持续时长:{breakDelay},错误类型:{ex.GetType().Name},信息:{ex.Message}");                                                                        },                                                                        onReset: context =>                           //断路器关闭状态触发事件,断路器关闭                                                                        {                                                                            this._logger.LogInformation($"{key} => 进入关闭状态,程序恢复正常使用");                                                                        },                                                                        onHalfOpen: () =>                             //断路器进入半打开状态触发事件,断路器准备再次尝试操作执行                                                                        {                                                                            this._logger.LogInformation($"{key} => 进入半开状态,重新尝试接收请求");                                                                        }                                                                    )                                                     )                                            .ExecuteAsync(async () => await this._next(context))                                            ;            }            catch (BrokenCircuitException exception)            {                this._logger.LogError($"{nameof(BrokenCircuitException)}.InnerException.Message:{exception.InnerException.Message}");                var response = context.Response;                response.StatusCode = (int)HttpStatusCode.BadRequest;                response.ContentType = "text/plain; charset=utf-8";                await response.WriteAsync("Circuit Broken o(╥﹏╥)o");            }        }        public void Dispose()        {            this._asyncPolicyDict.Clear();            this._logger.LogInformation($"{nameof(CircuitBreakerMiddleware)}.Dispose()");        }    }}

程序入口Startup.cs

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){    if (env.IsDevelopment())    {         app.UseDeveloperExceptionPage();    }    app.UseRouting();    //注意位置在Routing下面,UseEndpoints上面    app.UseMiddleware();    app.UseEndpoints(endpoints =>    {         endpoints.MapControllers();    });}

控制器WeatherForecastController.cs

using System;using System.Collections.Generic;using System.Linq;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Logging;namespace CircuitBreakerDemo.Controllers{    [ApiController]    [Route("[controller]")]    public class WeatherForecastController : ControllerBase    {        private static readonly string[] Summaries = new[]        {            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"        };        private static Random _Random = new Random();        private readonly ILogger _logger;        public WeatherForecastController(ILogger logger)        {            this._logger = logger;        }        [HttpGet("test")]        public string Test()        {            if (_Random.Next(Summaries.Length) % 3 == 0)            {                throw new Exception("程序运行错误");            }            return Summaries[_Random.Next(Summaries.Length)];        }    }}

项目跑起来就是各种刷新页面了,然后就可以看到结果了。看不到熔断后的降级处理页面就一直刷新,一直到你看到Circuit Broken o(╥﹏╥)o的页面为止。

还有一个地方需要引起注意,就是项目里面如果使用了自定义异常处理的过滤器,这里添加的断路器就会失效。

public class ExceptionFilter : IExceptionFilter

所以如果选择使用断路器,一定要把之前过滤器这里注释了。每一家这里开发可能不一样,我说的是我自己遇见的情况。

// options.Filters.Add(); //统一异常处理过滤器,改到断路器里面

错误显示结果分为页面,JSON包结构

1、 项目里面实际显示的错误结果页面(需要用到在中间件里面返回页面的知识点)

  • 500的错误页面:

d785ceb213e571fe27b10aabc921cd10.png

  • 400的断路处理后的降级页面:

5735231bbeef28b47c4efd8e666a01a3.png

2、项目里面实际显示的错误结果包结构JSON

  • 500的错误包结构内容:

459f9091cc21969e58977797284b5b11.png

  • 400的错误包结构内容:

a15e4a01540015b4e122549ae8b119da.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值