限流与熔断初探

golang 专栏收录该内容
18 篇文章 0 订阅

1 技术方案

1.1 限流/熔断开关和阈值在ETCD中配置

在公共包go-common中封装一个方法,在ETCD中设置限流/熔断开启/关闭的开关,将父类context传递进去,读取环境变量,开关开启则进行限流和熔断(限流/熔断阈值写死,从环境变量中读取)

优点:目标明确,工作量、技术实现可预知

缺点:需要人为开启/关闭限流、熔断开关,限流/熔断阈值固定,必须达到该阈值才会出发,不够灵活

1.2 根据打点统计结果决定限流/熔断开启时机(实时计算)

当同一个用户对同一个模型或报表多次执行请求时(例如报表下载、查看线图),请求次数超过阈值,自动开发限流;

当OLAP对同一个(模型/报表)请求多次返回查询超时或者报错时,这种返回异常超过设置的阈值,自动开启熔断,即在短时间内再次请求(该模型/报表)时,启明星不再向OLAP查询,而是直接报提示信息:OLAP查询资源紧张/异常,等一定时间后(ETCD配置)再查询的提示信息。

优点:视实时请求量、OLAP返回情况进行自动限流/熔断,不需要人为介入,比较灵活;

缺点:需要事先对各个接口打点,工作量大,情景复杂,实现难度高。

1.3 通过kong网关进行限流和熔断

1.3.1 kong网关简介

在微服务架构中,由于系统和服务的细分,导致系统结构变得非常复杂, 为了跨平台,为了统一集中管理api,同时为了不暴露后置服务。甚至有时候需要对请求进行一些安全、负载均衡、限流、熔断、灰度等中间操作,基于此类种种的客观需求一个类似综合前置的系统就产生了,这就是API网关(API Gateway)。API网关作为分散在各个业务系统微服务的API聚合点和统一接入点,外部请求通过访问这个接入点,即可访问内部所有的REST API服务。目前常用的微服务网关有kong、zuul、gateway。

1.3.2 优缺点分析

优点:可以利用现有框架进行功能扩展
缺点:需要接入grpc服务,前期工作量大
(1)grpc服务接入
Golang项目通过sidecar部署grpc-proxy和grpcserver(详见:grpc-proxy(bridge)),实现grpc服务接入网关。

(2)grpc-proxy服务包括bridge服务以及proxy服务,其中bridge包括了服务注册以及http请求桥接为grpc请求,proxy包括了服务发现功能。

(3)golang可使用grpcx创建以及启动服务,使用原生的grpc服务,需实现diagnose服务

1.4 实施节点安排

第一阶段(年前):可以先实现方案1.1
第二阶段(年后):等打点、缓存都加过之后,可以优化到方案1.2
第二阶段(年后):go项目接入kong网关后,可以逐步优化到方案1.3

2 限流

2.1 限流(rate limit)技术方案

限流器,从字面上理解就是用来限制流量,有时候流量突增(可预期的比如“双11”,不可预期的微博的热门话题等),会将后端服务压垮,甚至直接宕机,使用限流器能限制访问后端的流量,起到一个保护作用,被限制的流量,可以根据具体的业务逻辑去处理,直接返回错误或者返回默认值等等。

2.2 限流算法

2.2.1 令牌桶算法

一个存放固定容量令牌的桶,按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数为0时,则拒绝新的请求。

令牌桶算法限制的是平均流入速率,并允许一定程度的突发流量。

2.2.2 漏桶算法

一个固定容量的漏桶,按照固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新的请求被拒绝。

漏桶算法限制的是平均流入速率,主要目的是平滑突发流入速率。

https://github.com/uber-go/ratelimit

https://github.com/juju/ratelimit

https://github.com/lyft/ratelimit

基于令牌桶算法和漏桶算法来实现的限速限流,Golang实现:https://github.com/yangwenmai/ratelimit

2.3 限流方式

2.3.1 应用级限流

  • 限流总并发/连接/请求数
  • 通过池化技术限流总资源数(连接池、线程池)
  • 限流某个接口的总并发/请求数
  • 限流某个接口的时间窗口请求数(某接口每秒的请求数)
  • 平滑限流某接口的请求数

2.3.2 分布式限流

分布式限流最关键的是将限流服务做成原子化,如可以使用Redis + Lua或Ngnix + Lua的方式实现这两种技术可以实现高并发和高性能。

2.3.3 接入层限流

接入层通常指请求流量的入口,主要的目的有:负载均衡、非法请求过滤、请求聚合、缓存、降级、限流、A/B测试、服务质量监控等。

2.3.4 节流

有时想要在特定的时间窗口内对重复的相同事件最多只处理一次,或者想要限制多个连续的相同事件最小执行时间间隔,那么可以使用节流(throttle)是心啊,其防止多个相同事件连续重复执行。

3 降级与熔断(circuit breaker)

在开发高并发系统时,可以通过使用缓存、限流、降级等方式保护系统,维护服务的稳定性和可用性。

当访问量剧增、服务异常(如响应时间过长或者不响应)、非核心服务影响到核心系统的性能时,仍然需要保证关键服务是可用的,这是就需要使用服务降级方案。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

服务降级的目的是保证核心服务可用,即使是有损的。

3.1 降级分类

1.按照是否自动化分为:自动降级、人工开关降级
2.按照功能分类:接口降级、服务降级

3.2 熔断(circuit breaker)技术方案

和限流器对依赖服务的保护机制不一样,熔断器是当依赖的服务已经出现故障时,为了保证自身服务的正常运行不再访问依赖的服务,防止雪崩效应。

熔断器有三种状态:

  • 关闭状态:服务正常,并维护一个失败率统计,当失败率达到阀值时,转到开启状态;
  • 开启状态:服务异常,调用 fallback 函数,一段时间之后,进入半开启状态
  • 半开启装态:尝试恢复服务,失败率高于阀值,进入开启状态,低于阀值,进入关闭状态
    github.com/afex/hystrix-go,提供了 go 熔断器实现,使用也很方便。

(1)首先创建一个熔断器:

使用hystrix-go创建熔断器:

// 熔断器
hystrix.ConfigureCommand(
    "addservice", // 熔断器名字,可以用服务名称命名,一个名字对应一个熔断器,对应一份熔断策略
    hystrix.CommandConfig{
        Timeout:                100,  // 超时时间 100ms
        MaxConcurrentRequests:  2,    // 最大并发数,超过并发返回错误
        RequestVolumeThreshold: 4,    // 请求数量的阀值,用这些数量的请求来计算阀值
        ErrorPercentThreshold:  25,   // 错误数量阀值,达到阀值,启动熔断,25%
        SleepWindow:            1000, // 熔断尝试恢复时间
    },
)

hystrix提供了阻塞和非阻塞两种使用方式,完整代码可以参考如下链接: https://github.com/hatlonely/hellogolang/blob/master/sample/addservice/cmd/client/main.go

(2)阻塞方式使用 Do 方法,返回一个 err

熔断的阻塞方式:

err := hystrix.Do("addservice", func() error {
    // 正常业务逻辑,一般是访问其他资源
    var err error
    // 设置总体超时时间 10 ms 超时
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res, err = client.Add(
        ctx, req,
        // 这里可以再次设置重试次数,重试时间,重试返回码
        grpc_retry.WithMax(3),
        grpc_retry.WithPerRetryTimeout(time.Duration(5)*time.Millisecond),
        grpc_retry.WithCodes(codes.DeadlineExceeded),
    )
    return err
}, func(err error) error {
    // 失败处理逻辑,访问其他资源失败时,或者处于熔断开启状态时,会调用这段逻辑
    // 可以简单构造一个response返回,也可以有一定的策略,比如访问备份资源
    // 也可以直接返回 err,这样不用和远端失败的资源通信,防止雪崩
    // 这里因为我们的场景太简单,所以我们可以在本地在作一个加法就可以了
    fmt.Println(err)
    res = &addservice.AddResponse{V: req.A + req.B}
    return nil
})

(3)非阻塞方式使用 Go 方法,返回一个 error 的 channel,建议在有多个资源需要并发访问的场景下使用

熔断的非阻塞方式:

err1 := hystrix.Go("addservice", func() error {
    var err error
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res1, err = client.Add(ctx, req)
    if err == nil {
        success <- struct{}{}
    }
    return err
}, nil)
 
// 有 fallback 处理
err2 := hystrix.Go("addservice", func() error {
    var err error
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res2, err = client.Add(ctx, req)
    if err == nil {
        success <- struct{}{}
    }
    return err
}, func(err error) error {
    fmt.Println(err)
    res2 = &addservice.AddResponse{V: req.A + req.B}
    success <- struct{}{}
    return nil
})
 
for i := 0; i < 2; i++ {
    select {
    case <-success:
        fmt.Println("success", i)
    case err := <-err1:
        fmt.Println("err1:", err)
    case err := <-err2:
        // 这个分支永远不会走到,因为熔断机制里面永远不会返回错误
        fmt.Println("err2:", err)
    }
}

go-kit微服务实践,http restful api、日志功能、限流、服务监控、服务发现与注册、api网关、服务链路跟踪、服务熔断、jwt身份认证:https://github.com/vincecfl/go-kit-one

https://github.com/rubyist/circuitbreaker

当程序进行远程调用时,通常使用熔断器(机制)/断路器circuit breaker。远程调用通常会在超时之前挂起一段时间。如果应用程序发出大量这些请求,许多资源可能会被拥堵在一起,等待这些超时发生。熔断器通过包装这些远程调用,并在发生定义数量的故障或超时后跳闸。当熔断器被绊倒时,任何之后的调用都将避免进行远程调用并将错误返回给调用者。同时,熔断器将定期允许再次尝试某些调用请求,如果这些调用成功,将关闭访问链路。

参考

微服务组件之限流器与熔断器:https://studygolang.com/articles/13254

Golang实现请求限流的几种办法:https://blog.csdn.net/micl200110041/article/details/82013032

测试代码: https://github.com/hatlonely/hellogolang/blob/master/sample/addservice/cmd/client/main.go

Circuit Breaker Pattern: https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589784(v%3dpandp.10)

hystrix-go: https://github.com/afex/hystrix-go/

限流:漏桶算法和令牌桶算法:http://maiyang.github.io/%E6%8A%80%E6%9C%AF/%E7%AE%97%E6%B3%95/2017/05/28/rate-limit-algorithm

维基百科:Token_bucket:https://en.wikipedia.org/wiki/Token_bucket

维基百科:Leaky_bucket:https://en.wikipedia.org/wiki/Leaky_bucket

接口限流实践:http://www.cnblogs.com/LBSer/p/4083131.html

流量调整和限流技术:http://colobu.com/2014/11/13/rate-limiting/
微服务最强开源流量网关Kong:https://www.jianshu.com/p/934d18ff2efd

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值