Skip to content

Commit

Permalink
degrade: make cgroup internal
Browse files Browse the repository at this point in the history
  • Loading branch information
WineChord committed Sep 27, 2023
1 parent 9a21850 commit 6d89e3d
Show file tree
Hide file tree
Showing 13 changed files with 810 additions and 222 deletions.
11 changes: 0 additions & 11 deletions degrade/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +0,0 @@
# Change Log

## [0.1.1](https://git.woa.com/trpc-go/trpc-filter/tree/degrade/v0.1.1) (2022-07-16)

### Features
- 升级 trpc-go 到 v0.9.4,适配新版本 filter 签名

## [0.1.0](https://git.woa.com/trpc-go/trpc-filter/tree/degrade/v0.1.0) (2020-06-02)

### Features
- 增加最大并发请求数限制,应对超短时突发流量,和原有熔断互为补充
97 changes: 47 additions & 50 deletions degrade/README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,46 @@
# tRPC-Go [degrade] 熔断保护插件

[![BK Pipelines Status](https://api.bkdevops.qq.com/process/api/external/pipelines/projects/pcgtrpcproject/p-b4b171fff152465397df973569962702/badge?X-DEVOPS-PROJECT-ID=pcgtrpcproject)](http://devops.oa.com:/ms/process/api-html/user/builds/projects/pcgtrpcproject/pipelines/p-b4b171fff152465397df973569962702/latestFinished?X-DEVOPS-PROJECT-ID=pcgtrpcproject)[![Coverage](https://tcoverage.woa.com/api/getCoverage/getTotalImg/?pipeline_id=p-b4b171fff152465397df973569962702)](http://macaron.oa.com/api/coverage/getTotalLink/?pipeline_id=p-b4b171fff152465397df973569962702)[![GoDoc](https://img.shields.io/badge/API%20Docs-GoDoc-green)](http://godoc.oa.com/trpc.group/trpc-go/trpc-filter/degrade)
## degrade 插件使用介绍

## degrade插件使用介绍

* degrade插件主要功能:
* degrade 插件主要功能:

1. 周期检测获取系统负载情况
2. 根据Cpuidle,内存使用率,负载(主要load5)来设置阈值,达到阈值触发熔断保护,抛弃一定百分比的随机流量
3. 可限制最大并发请求数,应对超短时突发流量,和上述互为补充
2. 根据 CPUidle,内存使用率,负载(主要 load5)来设置阈值,达到阈值触发熔断保护,抛弃一定百分比的随机流量
3. 可限制最大并发请求数应对超短时突发流量和上述互为补充

**备注:**

sumeru和123平台上的docker容器是采用软隔离的方式,top,free 命令看到的数据都是宿主机的数据,常规的采集指标(/proc/(mem|cpu))也是宿主机数据。获取Docker容器的资源利用率需要两项基础技术Namespace和cgroup。
假如平台上的 docker 容器是采用软隔离的方式,那么 top,free 命令看到的数据都是宿主机的数据,常规的采集指标(/proc/(mem|cpu))也是宿主机数据。获取 Docker 容器的资源利用率需要两项基础技术 Namespace 和 cgroup。

目前可以针对 cpu 利用率和内存利用率这两个指标做熔断操作。

目前可以针对cpu利用率和内存利用率这两个指标做熔断操作。
cpu 和内存指标计算方式如下:

cpu和内存指标计算方式如下:
一、cpu使用率计算
一、cpu 使用率计算

Sumeru采用时间片的方式来调度每一个容器的CPU使用时间,每个容器在同一个调度周期内可以使用的cpu时间不能超过设定的配额。在cgroup中用cpu.cfs_period_us指定调度周期,用cpu.cfs_quota_us来指定相应容器可以使用cpu的最大时间长度,一个调度周期时内容器可以使用的最大cpu时间是:CPU配额比机器cpu核心数调度周期
平台采用时间片的方式来调度每一个容器的 CPU 使用时间,每个容器在同一个调度周期内可以使用的 cpu 时间不能超过设定的配额。在 cgroup 中用 cpu.cfs_period_us 指定调度周期,用 cpu.cfs_quota_us 来指定相应容器可以使用 cpu 的最大时间长度,一个调度周期时内容器可以使用的最大 cpu 时间是:CPU 配额比机器 cpu 核心数调度周期

调度周期默认是100000微秒
调度周期默认是 100000 微秒

例如,一个48核的宿主机上有一个25%配额的容器,这个容器可以使用的最大cpu时间是
例如,一个 48 核的宿主机上有一个 25% 配额的容器,这个容器可以使用的最大 cpu 时间是

0.25 48 100000 = 1200000(微秒)
0.25 * 48 * 100000 = 1200000(微秒)

计算cpu配额(sumeru容器里按照实际cgroup文件中算,其实为超配)
计算 cpu 配额(容器里按照实际 cgroup 文件中算,其实为超配)

限制cpu核数计算
限制 cpu 核数计算

cpu.cfs_quota_us/cpu.cfs_period_us
cpu核数
cpu 核数

/sys/fs/cgroup/cpuacct/cpuacct.usage_percpu

cgroup中cpuacct.usage有统计了容器从创建以来所使用的cpu时间和
cgroup 中 cpuacct.usage 有统计了容器从创建以来所使用的 cpu 时间和

获取cpuacct.usage
Sleep interval间隔纳秒
获取cpuacct.usage
计算cpu使用率:(两次获取cpuacct.usage的差值/(interval * 容器cpu配额))
获取 cpuacct.usage
Sleep interval 间隔纳秒
获取 cpuacct.usage
计算 cpu 使用率:(两次获取 cpuacct.usage 的差值/(interval * 容器 cpu 配额))
二、内存利用率计算

Total: cgroup 被限制可以使用多少内存,可以从文件里的 hierarchical_memory_limit 获得,但不是所有 cgroup 都限制内存,没有限制的话会获得 2^64-1 这样的值,我们还需要从 /proc/meminfo 中获得 MemTotal,取两者最小。
Expand All @@ -51,42 +50,40 @@ MappedFile: memory/memory.stat 中的 mapped_file、

这里不将共享内存计算入容器使用内存

参考:<http://km.oa.com/group/26857/articles/show/280535?kmref=search&from_page=1&no=2>

## 使用说明

### load5参数值设置参考(常规机器/容器)
### load5 参数值设置参考(常规机器/容器

### 由于目前sumeru、123获取load5不准,因此load5暂时不可用,请大家注意,将load的阈值设置为999即可
### 当获取 load5 不准时可以将 load 的阈值设置为 999

由于5分钟load可能存在对系统负载不敏感的地方,因此根据不断的长期研究和实践,结合在腾讯新闻业务中的使用情况,建议使用如下配置方法
由于 5 分钟 load 可能存在对系统负载不敏感的地方,因此根据不断的长期研究和实践,结合在实际业务中的使用情况,建议使用如下配置方法

```go
load5 := int ((2 * x + 3 * y) / 5)
```

其中x为空闲期平均1分钟负载,y为核心数(一般负载超过核心数认为高负载)
理论为 连续5分钟周期,3分钟高负载即触发 熔断!
其中 x 为空闲期平均 1 分钟负载,y 为核心数(一般负载超过核心数认为高负载)
理论为 连续 5 分钟周期,3 分钟高负载即触发 熔断!

123平台容器的load指标目前未能准确获取,需要设置一个大值以跳过这个指标,例如:10000
假如平台容器的 load 指标不能准确获取,需要设置一个大值以跳过这个指标,例如10000

例如:
使用uptime命令
使用 uptime 命令

```go
[xxx@xxx ~]$ uptime
14:55:45 up 121 days, 15:55, 1 user, load average: 0.28, 0.26, 0.23
load average: 0.28(1min), 0.26(5min), 0.23(15min)

比如 load average: 1min 负载为0.16, 核心数为2, 则load 5min配置load5参考值设置为1.264
比如 load average: 1min 负载为 0.16, 核心数为 2, 则 load 5min 配置 load5 参考值设置为 1.264

(0.16*2+3*2)/5=(0.32+6)/5=6.32/5=1.264

核心数计算:grep -c 'model name' /proc/cpuinfo

```

* 增加import
* 增加 import

```go
import (
Expand All @@ -103,28 +100,28 @@ server:
plugins:
circuitbreaker:
degrade:
load5: 5000 // load 5分钟 触发熔断的阈值,恢复时使用实时load1 <= 本值来判断,更敏感(123平台使用大值跳过这个指标)
cpu_idle: 30 // cpu空闲率,低于30%,进入熔断
memory_use_p : 60 // 内存使用率超过60%,进入熔断
degrade_rate : 60 // 流量保留比例,目前使用随机算法抛弃。 0或100则不启用此插件
interval : 30 //心跳时间间隔,主要控制多久更新一次熔断开关状态, 单位"s"
max_concurrent_cnt : 10000 // 最大并发数
max_timeout_ms : 100 // //超过最大并发请求数时,最多等待MaxTimeOutMs才决定是丢弃还是继续处理
load5: 5000 # load 5 分钟 触发熔断的阈值,恢复时使用实时 load1 <= 本值来判断,更敏感
cpu_idle: 30 # cpu 空闲率,低于 30%,进入熔断
memory_use_p : 60 # 内存使用率超过 60%,进入熔断
degrade_rate : 60 # 流量保留比例,目前使用随机算法抛弃。0 或 100 则不启用此插件
interval : 30 # 心跳时间间隔,主要控制多久更新一次熔断开关状态单位"s"
max_concurrent_cnt : 10000 # 最大并发数
max_timeout_ms : 100 # 超过最大并发请求数时,最多等待 MaxTimeOutMs 才决定是丢弃还是继续处理
```
字段说明如下:
```go
type Config struct {
Load5 float64 `yaml:"load5"` //load 5分钟 触发熔断的阈值,恢复时使用实时load1 <= 本值来判断,更敏感
CPUIdle int `yaml:"cpu_idle"` //cpuidle 触发熔断的阈值
MemoryUserPercent int `yaml:"memory_use_p"` //内存使用率百分比 触发熔断的阈值
DegradeRate int `yaml:"degrade_rate"` //流量保留比例,目前使用随机算法抛弃,迭代加入其他均衡算法
Interval int `yaml:"interval"` //心跳时间间隔,主要控制多久更新一次熔断开关状态
Modulename string `yaml:"modulename"` //模块名,后续用于上报鹰眼或其他日志
Whitelist string `yaml:"whitelist"` //白名单,用于后续跳过不被熔断控制的业务接口
IsActive bool `yaml:"-"` //标志熔断是否生效
MaxConcurrentCnt int `yaml:"max_concurrent_cnt"` //最大并发请求数,<=0时不开启.和上述熔断互为补充,能防止突发流量把服务打死,比如1ms内突然进入100W请求
MaxTimeOutMs int `yaml:"max_timeout_ms"` //超过最大并发请求数时,最多等待MaxTimeOutMs才决定是丢弃还是继续处理
Load5 float64 `yaml:"load5"` // load 5 分钟 触发熔断的阈值,恢复时使用实时 load1 <= 本值来判断,更敏感
CPUIdle int `yaml:"cpu_idle"` // cpuidle 触发熔断的阈值
MemoryUserPercent int `yaml:"memory_use_p"` // 内存使用率百分比 触发熔断的阈值
DegradeRate int `yaml:"degrade_rate"` // 流量保留比例,目前使用随机算法抛弃,迭代加入其他均衡算法
Interval int `yaml:"interval"` // 心跳时间间隔,主要控制多久更新一次熔断开关状态
Modulename string `yaml:"modulename"` // 模块名,后续用于上报鹰眼或其他日志
Whitelist string `yaml:"whitelist"` // 白名单,用于后续跳过不被熔断控制的业务接口
IsActive bool `yaml:"-"` // 标志熔断是否生效
MaxConcurrentCnt int `yaml:"max_concurrent_cnt"` // 最大并发请求数,<=0 时不开启。和上述熔断互为补充能防止突发流量把服务打死,比如 1ms 内突然进入 100W 请求
MaxTimeOutMs int `yaml:"max_timeout_ms"` // 超过最大并发请求数时,最多等待 MaxTimeOutMs 才决定是丢弃还是继续处理
}
```
10 changes: 5 additions & 5 deletions degrade/degrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (

// Config 熔断配置结构体声明
type Config struct {
// Load5 5分钟 触发熔断的阈值,恢复时使用实时load1 <= 本值来判断,更敏感
// Load5 5 分钟 触发熔断的阈值,恢复时使用实时 load1 <= 本值来判断,更敏感
Load5 float64 `yaml:"load5"`
// CPUIdle cpuidle 触发熔断的阈值
CPUIdle int `yaml:"cpu_idle"`
Expand All @@ -54,16 +54,16 @@ type Config struct {
Whitelist string `yaml:"whitelist"`
// IsActive 标志熔断是否生效
IsActive bool `yaml:"-"`
// MaxConcurrentCnt 最大并发请求数,<=0时不开启,控制最大并发请求数,和上述熔断互为补充,能防止突发流量把服务打死,比如1ms内突然进入100W请求
// MaxConcurrentCnt 最大并发请求数,<=0 时不开启,控制最大并发请求数和上述熔断互为补充能防止突发流量把服务打死,比如 1ms 内突然进入 100W 请求
MaxConcurrentCnt int `yaml:"max_concurrent_cnt"`
// MaxTimeOutMs 超过最大并发请求数时,最多等待MaxTimeOutMs才决定是熔断还是继续处理
// MaxTimeOutMs 超过最大并发请求数时,最多等待 MaxTimeOutMs 才决定是熔断还是继续处理
MaxTimeOutMs int `yaml:"max_timeout_ms"`
}

// Degrade 熔断插件默认初始化
type Degrade struct{}

// Filter 声明熔断组件的filter来充当拦截器
// Filter 声明熔断组件的 filter 来充当拦截器
func Filter(
ctx context.Context, req interface{}, handler filter.ServerHandleFunc,
) (interface{}, error) {
Expand All @@ -81,7 +81,7 @@ func Filter(
defer func() {
<-sema
}()
// 达到最大并发请求数, 直接丢弃请求
// 达到最大并发请求数直接丢弃请求
default:
return nil, errs.New(systemDegradeErrNo, errDegardeReturn)
}
Expand Down
40 changes: 36 additions & 4 deletions degrade/go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
module trpc.group/trpc-go/trpc-filter/degrade

go 1.12
go 1.18

require (
github.com/shirou/gopsutil v2.19.12+incompatible
github.com/stretchr/testify v1.8.0
gopkg.in/yaml.v3 v3.0.1
trpc.group/trpc-go/trpc-go v0.0.0-20230919115157-95737ac0b3e8
)

require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/shirou/gopsutil v2.19.12+incompatible
github.com/stretchr/testify v1.7.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
github.com/golang/snappy v0.0.3 // indirect
github.com/google/flatbuffers v2.0.0+incompatible // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/panjf2000/ants/v2 v2.4.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.43.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/automaxprocs v1.3.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.4.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
trpc.group/trpc-go/tnet v0.0.0-20230810071536-9d05338021cf // indirect
trpc.group/trpc/trpc-protocol/pb/go/trpc v0.0.0-20230803031059-de4168eb5952 // indirect
)
Loading

0 comments on commit 6d89e3d

Please sign in to comment.