Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 自适应限流 #166

Closed
wants to merge 17 commits into from
Closed

feat: 自适应限流 #166

wants to merge 17 commits into from

Conversation

WTIFS
Copy link
Contributor

@WTIFS WTIFS commented Sep 4, 2023

Please provide issue(s) of this PR:
Fixes #165

To help us figure out who should review this PR, please put an X in all the areas that this PR affects.

  • Configuration
  • Docs
  • Performance and Scalability
  • Naming
  • HealthCheck
  • Test and Release

Please check any characteristics that apply to this pull request.

  • Does not have any user-facing changes. This may include API changes, behavior changes, performance improvements, etc.

Kratos - BBR

Kratos 是 bilibili 开源的一套 Go 微服务框架。BBR 是其中的一个限流组件。参考了 TCP BBR 的思想,以及 阿里 Sentinel 的算法。

传统的限流思路为:超过一定负载就拦截流量进入,负载恢复就放开流量,这样做有延迟性,最终是按照果来调节因,无法取得良好效果。

BBR 的思路为:根据应用的请求处理时间、请求成功数、最大并发数这些指标,计算当前应用能承载的最大并发请求量,再对比当前系统并发量,判断是否应当拦截本次流量,即所谓"自适应"。

BBR 的源码实现可参考:

插件设计

本插件使用了 kratos 的 BBR 限流器,将其适配成 QuotaBucket 接口(主要实现 GetQuotaWithRelease 判断限流方法),以及 ServiceRateLimiter 接口(实现 InitQuota 初始化方法)。

由于 BBR 限流需要记录请求通过数、当前并发数、请求耗时,因此没有复用原来 QuotaBucket 接口中的 GetQuota 方法,而是新增了一个方法 GetQuotaWithRelease,该方法相比于 GetQuota 方法,返回参数中多了一个 func(),供业务方在业务逻辑处理完成后调用。

由于 CPU 使用率指标为实例单机指标,因此 CPU 限流只适用于单机限流,不适用于分布式限流,未实现分布式限流器需要实现的接口。

初始化 InitQuota

kratos - BBR 初始化需要三个入参:

CPUThreshold: CPU使用率阈值,超过该阈值时,根据应用过去能承受的负载判断是否拦截流量 
window: 窗口采样时长,控制采样多久的数据
bucket: 桶数,BBR 会把 window 分成多个 bucket,沿时间轴向前滑动。如 window=1s, bucket=10 时,整个滑动窗口用来保存最近 1s 的采样数据,每个小的桶用来保存 100ms 的采样数据。当时间流动之后,过期的桶会自动被新桶的数据覆盖掉

这三个入参,从 apitraffic.Rule 结构体中解析,直接使用了结构体中的 MaxAmountValidDurationPrecision 字段

判断限流 GetQuotaWithRelease

调用了 BBR 的 Allow() 方法

其内部执行 shouldDrop() 方法,其执行流程如下:

img.jpg

流程中比较关键的一步是计算应用可承受的最大请求量,由下列方法计算:

func (l *BBR) maxInFlight() int64 {
	return int64(math.Floor(float64(l.maxPASS()*l.minRT()*l.bucketPerSecond)/1000.0) + 0.5)
}
  • maxPass * bucketPerSecond / 1000 为每毫秒处理的请求数
  • l.minRT() 为 单个采样窗口中最小的响应时间
  • 0.5为向上取整
  • 当CPU利用率过载时,就需要通过上述预期公式进行干预。在服务运行期间持续统计当前服务的请求数,即 inflight,通过在滑动窗口内的所有buckets中比较得出最多请求完成数 maxPass,以及最小的耗时 minRT,相乘就得出了预期的最佳请求数 maxFlight
  • maxFlight 表示系统能同时处理的最多请求数,这个水位是一个平衡点,保持该水位可以最大化系统的处理能力,超过该水位则会导致请求堆积。
  • 通过 inflightmaxFlight 对比,如果前者大于后者那么就已经过载,进而拒绝后续到来的请求防止服务过载。

@houseme
Copy link
Member

houseme commented Sep 4, 2023

ci 报错了 https://github.com/polarismesh/polaris-go/actions/runs/6070430967/job/16467286092?pr=166

Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall.go:83:16: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_linux.go:1018:20: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_linux.go:2289:9: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_unix.go:118:7: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/sysvshm_unix.go:33:7: undefined: unsafe.Slice
note: module requires Go 1.17

@WTIFS @chuntaojun 咱们是升级到 1.18 的版本 还是改代码哦

@WTIFS
Copy link
Contributor Author

WTIFS commented Sep 8, 2023

ci 报错了 https://github.com/polarismesh/polaris-go/actions/runs/6070430967/job/16467286092?pr=166

Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall.go:83:16: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_linux.go:1018:20: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_linux.go:2289:9: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/syscall_unix.go:118:7: undefined: unsafe.Slice
Error: ../../../../go/pkg/mod/golang.org/x/[email protected]/unix/sysvshm_unix.go:33:7: undefined: unsafe.Slice
note: module requires Go 1.17

@WTIFS @chuntaojun 咱们是升级到 1.18 的版本 还是改代码哦

@houseme 先不合,改代码吧,后面看看把 bbr 的逻辑放到这里而不是引用 kratos。不过改动很多,应该挺麻烦的

@WTIFS WTIFS closed this Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

限流:支持自适应限流
3 participants