前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >云原生系统之弹性模式

云原生系统之弹性模式

作者头像
有态度的马甲
发布2021-08-05 09:57:08
1.5K0
发布2021-08-05 09:57:08
举报
文章被收录于专栏:精益码农精益码农
大纲

1.云原生系统的弹性模式resiliency pattern 1.1 服务故障的雪崩效应 1.2 回应之前云原生--弹性请求的疑问?

2. 弹性模式:作用在下游请求消息上3. 短期中断的响应码4. Polly经典策略5. Golang 断路器模式 德国哲学家尼采说过:那些杀不死我的东西,只会让我更加强大


hi,好久不见,马甲哥之前意译并连载了《Microsoft Cloud-native toc.pdf》部分内容

什么是云原生现代云原生设计理念.NET微服务谈到云原生,绕不开容器化支撑性服务 & 自动化能力

01

云原生系统的弹性模式

结合最近的工作经验,本次继续聊一聊云原生的弹性模式 (resilience not scale), 这也是回应《现代云原生设计理念》中

“在分布式体系结构中,当服务B不响应来自服务A的网络请求会发生什么? 当服务C暂时不可用,其他调用C的服务被阻塞时该怎么办?”

由于网络原因或自身原因,B、C服务不能及时响应,服务A发起的请求将被阻塞(直到B、C响应),此时若大量请求涌入,服务A的线程资源将被消耗殆尽,服务A的处理性能受到极大影响,进而影响下游依赖的external clients/backend srv。

故障会传播,造成连锁反应,对整个分布式结构造成灾难性后果,这就是服务故障的“雪崩效应”。

当B、C服务不可用,下游客户端/backend srv能做什么? 客观上请求不通,执行预定的弹性策略:重试/断路?

02

弹性模式:作用在下游的请求消息上

弹性模式是系统面对故障仍然保持工作状态的能力,它不是为了避免故障,而是接受故障并尝试去面对它。

Polly是一个全面的.NET弹性和瞬时错误处理库,允许开发者以流畅和线程安全的方式表达弹性策略。

策略

场景

行为

Retry

抖动/瞬时错误,短时间内自动恢复

在特定操作上配置重试行为

Circuit Breaker

在短期内不大可能恢复

当故障超过阈值,在一段时间内快速失败

Timeout

限制调用者等待响应的时间

Bulkhead

将操作限制在固定的资源池,防止故障传播

Cache

自动存储响应

Bulkhead

一旦失败,定义结构化的行为

一般将弹性策略作用到各种请求消息上(外部客户端请求或后端服务请求)

其目的是补偿暂时不可用的服务请求。

03

短期中断的响应码

Http Status code

原因

404

not found

408

request timeout

429

two many requests

502

bad gateway

503

service unavailable

504

gateway timeout

正确规范的响应码能帮助开发者尽快确认故障。

执行故障策略时,也能有的放矢,比如只重试那些由失败引起的操作,对于403UnAuthorized不可重试。

Kubernetes探针踩坑记

04

Polly的经典策略

•Retry:对网络抖动/瞬时错误可以执行retry策略(预期故障可以很快恢复),•Circuit Breaker:为避免无效重试导致的故障传播,在特定时间内如果失败次数到达阈值,断路器打开(在一定时间内快速失败); 同时启动一个timer,断路器进入半开模式(发出少量请求,请求成功则认为故障已经修复,进入关闭状态,重置失败计数器。)

代码语言:javascript
复制

services.AddHttpClient("small")
        //降级
        .AddPolicyHandler(Policy<HttpResponseMessage>.HandleInner<Exception>().FallbackAsync(new HttpResponseMessage(),async b =>
        {
           // 1、降级打印异常
          Console.WriteLine($"服务开始降级,上游异常消息:{b.Exception.Message}");
          // 2、降级后的数据
          b.Result.Content= new StringContent("请求太多,请稍后重试", Encoding.UTF8, "text/html");
          b.Result.StatusCode = HttpStatusCode.TooManyRequests;
          await Task.CompletedTask;
        }))
        //熔断                                                      
        .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>() 
           .CircuitBreakerAsync(
              3,    // 打开断路器之前失败的次数
              TimeSpan.FromSeconds(20), // 断路器的开启的时间间隔
              (ex, ts) =>  //熔断器开启
              {
                  Console.WriteLine($"服务断路器开启,异常消息:{ex.Exception.Message}");
                  Console.WriteLine($"服务断路器开启的时间:{ts.TotalSeconds}s");
              }, 
              () => { Console.WriteLine($"服务断路器重置"); },   //断路器重置事件
              () => { Console.WriteLine($"服务断路器半开启(一会开,一会关)"); }  //断路器半开启事件
            )
        )
        //重试
        .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().RetryAsync(3))
       // 超时 
       .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(2))); 
       

☹️当一个应用存在多个Http调用,按照上面的经典写法,代码中会混杂大量重复、与业务无关的口水代码, 思考如何优雅的对批量HttpClient做弹性策略。

这里提供两个实践:

① 博客园驰名博主edisonchou: 使用AOP框架,动态织入Polly

② CSDN某佚名大牛,使用反射加配置实现的PollyHttpClientServiceCollectionExtension扩展类, 支持在配置文件指定HttpClientName

05

Golang的断路器

go get github.com/sony/gobreaker

func NewCircuitBreaker(st Settings) *CircuitBreaker 实例化断路器对象, 参数如下:

代码语言:javascript
复制
type Settings struct {
    Name          string
    MaxRequests   uint32       #半开状态允许的最大请求数量,默认为0,允许1个请求
    Interval      time.Duration
    Timeout       time.Duration  # 断路器进入半开状态的间隔,默认60s
    ReadyToTrip   func(counts Counts) bool   # 切换状态的逻辑
    OnStateChange func(name string, from State, to State)
}

下面这个示例演示了:请求谷歌网站,失败比例达到60%,就切换到"打开"状态,同时开启60sTimer,到60s进入“半开”状态(允许发起一个请求),如果成功, 断路器进入"关闭"状态;失败则重新进入“打开”状态,并重置60sTimer

代码语言:javascript
复制
package main
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "github.com/sony/gobreaker"
)
var cb *gobreaker.CircuitBreaker
func init() {
    var st gobreaker.Settings
    st.Name = "HTTP GET"
    st.ReadyToTrip = func(counts gobreaker.Counts) bool {
        failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
        return counts.Requests >= 3 && failureRatio >= 0.6
    }
    cb = gobreaker.NewCircuitBreaker(st)
}
// Get wraps http.Get in CircuitBreaker.
func Get(url string) ([]byte, error) {
    body, err := cb.Execute(func() (interface{}, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, err
        }
        return body, nil
    })
    if err != nil {
        return nil, err
    }
    return body.([]byte), nil
}
func main() {
    body, err := Get("http://www.google.com/robots.txt")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(body))
}

总结

本文记录了云原生系统的弹性模式:通过预设策略直面失败,补偿暂时不可用的请求、避免故障传播, 这对于实现微服务高可用、弹性容错相当重要。 •https://blog.csdn.net/weixin_44588495/article/details/106361934•https://blog.csdn.net/qq_26900081/article/details/108071374•https://www.cnblogs.com/edisonchou/p/9159644.html•https://docs.microsoft.com/en-us/dotnet/architecture/cloud-native/application-resiliency-patterns•https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 精益码农 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档