简单整理一下熔断与限流,跟上一节息息相关。
polly 的策略类型分为两类:
被动策略(异常处理、结果处理)
主动策略(超时处理、断路器、舱壁隔离、缓存)
熔断和限流通过下面主动策略来实现:
降级响应
失败重试
断路器
舱壁隔离
Policy 类型 | 状态 | 说明 |
---|---|---|
CircuitBreaker(断路器) | 有状态 | 共享失败率,以决定是否熔断 |
Bulkhead(舱壁隔离) | 有状态 | 共享容量使用情况,以决定是否执行动作 |
Cache(缓存) | 有状态 | 共享缓存的对象,以决定是否命中 |
其他策略 | 无状态 |
先来看一下熔断,什么是熔断呢?
熔断就是让我们的上游服务器一段时间内对下游服务器不进行调用。
这里解释一下上游服务器和下游服务器,比如说A调用B,那么A就是上游服务器,B就是下游服务器。
那么为什么要熔断呢?比如说A调用B,现在A调用B 10次有8次是错误的,那么这个时候就要想一件事,代码没有变过,那么肯定是量变成了质变。
这时候B之所以不可用,那么是因为请求太多了,处理不过来(比如内存升高了,io 99%了等)。
那么这个时候A就不进行调用了,隔一段时间后再进行调用。也就是A对B的这条线进行了熔断处理。
services.AddHttpClient("GreeterClient").AddPolicyHandler(Policy<HttpResponseMessage> .Handle<HttpRequestException>().CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 10, durationOfBreak: TimeSpan.FromSeconds(10), onBreak: (r, t) => { // 熔断的时候处理事件 }, onReset: () => { // 恢复的时候的处理 },onHalfOpen: () => { // 恢复之前进行处理 }));
CircuitBreakerAsync 表示断路器,这个用来实现熔断的。
handledEventsAllowedBeforeBreaking 表示失败10次,进行熔断。
durationOfBreak 熔断的事件
其他几个事件上面做了备注。
其实上面这种不常用,因为限制比较死,比如说10次就熔断。
一般都是百分比来计算的。
services.AddHttpClient("GreeterClient").AddPolicyHandler(Policy<HttpResponseMessage> .Handle<HttpRequestException>().AdvancedCircuitBreakerAsync( failureThreshold:0.8, samplingDuration:TimeSpan.FromSeconds(10), minimumThroughput:100, durationOfBreak: TimeSpan.FromSeconds(10), onBreak: (r, t) => { // 熔断的时候处理事件 }, onReset: () => { // 恢复的时候的处理 }, onHalfOpen: () => { // 恢复之前进行处理 }));
failureThreshold 表示失败的比例
samplingDuration 表示失败的时间
failureThreshold 和 samplingDuration一般是组合起来用的,表示是10秒内失败次数要有80%就会熔断。
minimumThroughput 表示10秒类必须有100个请求才会出根据其他的条件进行熔断判断。
上面就是熔断了,那么什么是服务降级呢?
网上的一段话是这样的:服务降级是指 当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心业务正常运作或高效运作。
这段话的感觉好像是关闭某些服务一样,而且比较绕。
个人理解是服务降级是指降低了原有的服务体验,都属于服务降级。
熔断其实也是一种服务降级,但是单纯的熔断就降级的有点厉害了。
比如我去买东西,然后店直接关门了,服务体验下降了,体验降得比较厉害。
但是如果去买东西,店没有关闭,而是有一排服务员,告诉你现在因为供销商没到货买不到了,这体验是不是好点,这也是服务降级。
那么就看下第二种情况的服务降级怎么实现吧。
var breakPolicy = Policy<HttpResponseMessage> .Handle<HttpRequestException>().AdvancedCircuitBreakerAsync( failureThreshold: 0.8, samplingDuration: TimeSpan.FromSeconds(10), minimumThroughput: 100, durationOfBreak: TimeSpan.FromSeconds(10), onBreak: (r, t) => { // 熔断的时候处理事件 }, onReset: () => { // 恢复的时候的处理 }, onHalfOpen: () => { // 恢复之前进行处理 }); var message = new HttpResponseMessage() { Content = new StringContent("不要慌,老板没有跑路,只是和老婆的妹妹出去了,要等一下!") }; var fallback = Policy<HttpResponseMessage>.Handle<BrokenCircuitException>().FallbackAsync(message); var fallbackBreak = Policy.WrapAsync(fallback, breakPolicy); services.AddHttpClient("GreeterClient").AddPolicyHandler(fallbackBreak);
上面代码就是当熔断后,过来的请求会有BrokenCircuitException异常,那么捕获到BrokenCircuitException异常,那么就告诉用户店没有倒闭,等一下就好。
这种就是比较优雅的降级。
上面这种var fallbackBreak = Policy.WrapAsync(fallback, breakPolicy); 就是将几个policy组合在一起,然后给某个或者某些HttpClient 增加该组合策略,比如Policy.WrapAsync(fallback, breakPolicy,xxx,yyy)等。
接下来解释一下限流。
为什么要限流呢? 如果没有限流其实熔断的意义是不大的。
为什么这么说呢? 比如说公司有1百万请求,然后这个时候熔断了,但是这100w请求还在啊,只有恢复服务,服务器又要进行熔断,一下子就瞬间爆炸。
var bulk = Policy.BulkheadAsync<HttpResponseMessage>( maxParallelization:30, maxQueuingActions:20, onBulkheadRejectedAsync:context=>Task.CompletedTask); var message = new HttpResponseMessage() { Content = new StringContent("你没有抢到号码,下次再来。") }; var fallbackBulk = Policy<HttpResponseMessage>.Handle<BulkheadRejectedException>().FallbackAsync(message); var fallbackBreak = Policy.WrapAsync(bulk, fallbackBulk);
上面这个就是限流了。
maxParallelization 表示可以并发处理30个请求,maxQueuingActions表示如果并发处于30,那么会加入到队列中,队列中最大能存20个。
如果队列中也存不下,那么就会抛出BulkheadRejectedException这种拒绝异常,一但出现异常,第二个策略就捕获到了,然后给出一些友好的提示。
下一节,网关。