【Kubernetes】API server 限流 之 maxinflight.go
这个文件实现了一个基于信号量(Channel)的简单限流器。
基础知识
总共有四种channel
-
带缓冲的channel
nonMutatingChan、mutatingChan 都是带缓冲的channel ,这类channel 的特点是:
这允许最多 mutatingLimit /nonMutatingLimit 个请求同时获取令牌并执行,直到缓冲区满了才会阻塞新的请求。对带缓冲 channel 的发送操作在缓冲区未满、接收操作在缓冲区非空的情况下是异步的(发送或接收不需要阻塞等待)。
但当缓冲区满了的情况下,对它进行发送操作的 Goroutine 就会阻塞挂起;当缓冲区为空的情况下,对它进行接收操作的 Goroutine 也会阻塞挂起。
本代码中限流通道从创建到服务器关闭始终保持打开状态,因为这些通道用于控制并发请求数量的计数器,需要持续使用。
-
不带缓冲的channel
对无缓冲 channel 类型的发送与接收操作,一定要放在两个不同的 Goroutine 中进行,否则会导致 deadlock。
| 通道类型 | 创建方式 | 主要特点 | 典型使用场景 |
|---|---|---|---|
| 无缓冲通道 | make(chan T) | 同步、阻塞 | 信号通知、精确同步 |
| 带缓冲通道 | make(chan T, size) | 异步、缓冲 | 限流控制、任务队列 |
| 只读通道 | <-chan T | 只能接收 | 消费者模式、类型安全 |
| 只写通道 | chan<- T | 只能发送 | 生产者模式、类型安全 |
| nil 通道 | var ch chan T | 阻塞操作 | 条件化通信、禁用分支 |
Kubernetes 的 maxinflight 限流器选择带缓冲通道作为实现方式,正是利用了其特有的信号量特性、非阻塞操作和容量控制能力。
当 select 语句中没有 default 分支,而且所有 case 中的 channel 操作都阻塞了的时候,整个 select 语句都将被阻塞,直到某一个 case 上的 channel 变成可发送,或者某个 case 上的 channel 变成可接收,select 语句才可以继续进行下去。
Kubernetes API Server maxinflight.go 限流分析
这个文件实现了一个基于信号量(Channel)的简单限流器,主要使用了两种限流通道:
1. 限流分类
var nonMutatingChan chan bool // 非修改性请求的限流通道
var mutatingChan chan bool // 修改性请求的限流通道
请求分类
- 非修改性请求(Non-Mutating):get、list、watch 操作
- 修改性请求(Mutating):create、update、patch、delete 等操作
2. 限流实现机制
核心实现使用了带缓冲的 Channel 作为信号量:
if nonMutatingLimit != 0 {nonMutatingChan = make(chan bool, nonMutatingLimit) // 非修改性请求限流器
}
if mutatingLimit != 0 {mutatingChan = make(chan bool, mutatingLimit) // 修改性请求限流器
}
限流处理流程
- 请求到达时:
select {
case c <- true: // 尝试获取令牌// 处理请求defer func() {<-c // 释放令牌}()handler.ServeHTTP(w, r)
default:// 获取不到令牌时返回 429 Too Many RequeststooManyRequests(r, w, retryAfter)
}
这里的select 实现了快速失败策略,所有的请求,要么被处理,要么直接扔到default中。 实现了非阻塞并发
2. 特权用户处理:
// 系统管理员(system:master)不受限流影响
if currUser, ok := apirequest.UserFrom(ctx); ok {for _, group := range currUser.GetGroups() {if group == user.SystemPrivilegedGroup {handler.ServeHTTP(w, r)return}}
}
3. 监控指标
使用 watermark 记录并发请求数:
type requestWatermark struct {phase stringreadOnlyObserver, mutatingObserver fcmetrics.RatioedGaugereadOnlyWatermark, mutatingWatermark intlock sync.Mutex
}
指标更新
- 定期更新 inflight 使用指标
- 记录最大并发数
- 分别统计读写请求
4. 特点总结
-
简单高效
- 使用 Channel 实现,开销小
- 无需复杂的算法
-
区分请求类型
- 读写请求分开限流
- 更细粒度的控制
-
特权用户豁免
- 系统管理员不受限
- 确保关键操作可执行
-
监控完善
- 记录限流指标
- 支持监控告警
-
无时间窗口
- 不同于令牌桶等算法
- 纯并发数控制
这是一个轻量级但有效的限流方案,适合 API Server 的需求。
Kubernetes API Server 中的两种限流通道分析
Kubernetes API Server 在 maxinflight.go 文件中实现了两种不同的限流通道,分别用于处理不同类型的请求:
1. 非修改性请求限流通道 (nonMutatingChan)
var nonMutatingChan chan bool
if nonMutatingLimit != 0 {nonMutatingChan = make(chan bool, nonMutatingLimit)klog.V(2).InfoS("Initialized nonMutatingChan", "len", nonMutatingLimit)
}
- 处理的请求类型:
get、list、watch等读取操作 - 判断依据:
nonMutatingRequestVerbs.Has(requestInfo.Verb) - 目的:限制并发读取操作的数量
2. 修改性请求限流通道 (mutatingChan)
var mutatingChan chan bool
if mutatingLimit != 0 {mutatingChan = make(chan bool, mutatingLimit)klog.V(2).InfoS("Initialized mutatingChan", "len", mutatingLimit)
}
- 处理的请求类型:
create、update、delete、patch等写入操作 - 判断依据:
!nonMutatingRequestVerbs.Has(requestInfo.Verb) - 目的:限制并发写入操作的数量
主要区别
-
目标请求不同
nonMutatingChan针对读操作mutatingChan针对写操作
-
资源消耗的差异
- 写操作通常消耗更多资源,可能会修改集群状态
- 读操作相对轻量,但数量可能更大
-
限流阈值不同
- API Server 配置中可以为这两种通道设置不同的限流阈值
- 通常写操作的限流阈值会低于读操作
-
监控指标分离
- 代码中对两种请求的监控也是分开的:
watermark.recordMutating(len(c)) watermark.recordReadOnly(len(c))
- 代码中对两种请求的监控也是分开的:
-
限流效果的差异
- 当读请求过多时,只会拒绝新的读请求,写请求不受影响
- 当写请求过多时,只会拒绝新的写请求,读请求不受影响
实现原理
两种通道的底层实现方式相同,都是使用带缓冲的 channel 作为信号量:
select {
case c <- true: // 尝试获取令牌// 处理请求并记录指标defer func() {<-c // 释放令牌}()handler.ServeHTTP(w, r)
default:// 无法获取令牌,返回 429 Too Many RequeststooManyRequests(r, w, retryAfter)
}
这种区分读写请求的限流设计,使 Kubernetes API Server 能够在高负载情况下更有效地分配资源,确保系统的稳定性和可用性。
限流参数 nonMutatingLimit 、mutatingLimit 意义:
这段代码:
if nonMutatingLimit == 0 && mutatingLimit == 0 {return handler
}
意思解释
这是一个前置检查,用于判断是否需要启用限流功能:
- 如果
nonMutatingLimit和mutatingLimit都为 0,表示不需要对任何类型的请求进行限流 - 在这种情况下,函数直接返回原始的
handler,不添加任何限流逻辑 - 相当于完全跳过限流处理,请求会直接传递给下一个处理器
为什么需要这个检查
-
性能优化
- 如果不需要限流,避免创建不必要的通道和记录指标的开销
- 减少请求处理的额外层级,提高性能
-
功能开关
- 提供一种方式完全禁用限流功能
- 管理员可以通过配置参数控制是否启用限流
-
兼容性
- 允许那些不需要限流的环境(如小型开发集群)简化配置
- 保持与低资源环境的兼容性
实际应用
在 Kubernetes API Server 配置中,可以通过这些参数控制限流:
apiServer:maxRequestsInflight: 400 # nonMutatingLimit 参数maxMutatingRequestsInflight: 200 # mutatingLimit 参数
如果将这两个值都设为 0,API Server 将不会对任何请求进行并发限制,可能适用于:
- 开发/测试环境
- 低负载集群
- 有外部限流机制的环境
这提供了一个简单的开关,使管理员能够灵活控制是否启用 API Server 的内置限流功能。
基于"通过通信共享内存"原则分析maxinflight.go
从Go语言的设计哲学"不要通过共享内存来通信,而要通过通信来共享内存"(Don’t communicate by sharing memory; share memory by communicating)来看,maxinflight.go有几个违背此原则的地方:
违背原则的地方
1. 共享状态管理方式
// 使用互斥锁保护共享状态
type requestWatermark struct {phase stringreadOnlyObserver, mutatingObserver fcmetrics.RatioedGaugelock sync.Mutex // 互斥锁readOnlyWatermark, mutatingWatermark int
}// 全局共享变量
var watermark = &requestWatermark{phase: metrics.ExecutingPhase,
}
这里使用了传统的"共享内存+锁"的并发控制模式,而不是Go推荐的基于通道的模式。
2. 记录指标的方法
func (w *requestWatermark) recordMutating(mutatingVal int) {w.mutatingObserver.Set(float64(mutatingVal))w.lock.Lock()defer w.lock.Unlock()if w.mutatingWatermark < mutatingVal {w.mutatingWatermark = mutatingVal}
}
这里直接修改共享状态,而不是通过消息传递。
3. 周期性更新指标
go wait.Until(func() {watermark.lock.Lock()readOnlyWatermark := watermark.readOnlyWatermarkmutatingWatermark := watermark.mutatingWatermarkwatermark.readOnlyWatermark = 0watermark.mutatingWatermark = 0watermark.lock.Unlock()metrics.UpdateInflightRequestMetrics(watermark.phase, readOnlyWatermark, mutatingWatermark)
}, inflightUsageMetricUpdatePeriod, stopCh)
使用锁直接访问和修改共享状态,而不是使用通道接收信息。
符合Go哲学的重构方案
根据"通过通信共享内存"的原则,可以这样重构:
// 定义度量指标更新消息
type MetricUpdate struct {ReadOnly intMutating int
}// 创建通道
var metricCh = make(chan MetricUpdate)
var readOnlyMetricCh = make(chan int)
var mutatingMetricCh = make(chan int)// 启动指标收集器goroutine
func startMetricCollector(stopCh <-chan struct{}) {readOnlyMax := 0mutatingMax := 0// 定期更新指标ticker := time.NewTicker(inflightUsageMetricUpdatePeriod)defer ticker.Stop()for {select {case val := <-readOnlyMetricCh:if val > readOnlyMax {readOnlyMax = val}case val := <-mutatingMetricCh:if val > mutatingMax {mutatingMax = val}case <-ticker.C:// 更新并重置最大值metrics.UpdateInflightRequestMetrics(metrics.ExecutingPhase, readOnlyMax, mutatingMax)readOnlyMax = 0mutatingMax = 0case <-stopCh:return}}
}// 记录指标的新函数
func recordReadOnly(val int) {select {case readOnlyMetricCh <- val:// 成功发送default:// 通道已满,丢弃}
}func recordMutating(val int) {select {case mutatingMetricCh <- val:// 成功发送default:// 通道已满,丢弃 }
}
为什么现有实现没有使用通道模式
尽管代码违背了Go的设计哲学,但有几个可能的原因:
-
历史兼容性:可能是从早期版本演化而来,完全重构成本高
-
性能考虑:在高频调用的代码路径上,锁可能比通道有更低的开销
-
简单直接:对于简单的计数器场景,锁实现可能更直观
-
限流部分确实用了通道:
nonMutatingChan = make(chan bool, nonMutatingLimit) mutatingChan = make(chan bool, mutatingLimit)这部分确实体现了Go的设计哲学,使用通道的缓冲区容量来限制并发请求数
总结
maxinflight.go中的代码部分遵循了Go的设计哲学(使用通道进行限流),但指标收集部分仍然采用了传统的"共享内存+锁"模式。一个更符合Go哲学的实现应该将指标收集也改为基于通道的模式,消除所有对共享内存的直接访问和修改。
相关文章:
【Kubernetes】API server 限流 之 maxinflight.go
这个文件实现了一个基于信号量(Channel)的简单限流器。 基础知识 总共有四种channel 带缓冲的channel nonMutatingChan、mutatingChan 都是带缓冲的channel ,这类channel 的特点是: 这允许最多 mutatingLimit /nonMutatingLimit 个请求同时获取令牌并执…...
推荐算法工程师的技术图谱和学习路径
推荐算法工程师的技术图谱和学习路径可以从多个维度进行概述,可以总结如下: 一、技术图谱 推荐算法工程师需要掌握的技术栈主要分为以下几个方面: 数学基础: 微积分、线性代数、概率论与统计学是推荐算法的基础,用于理解模型的数学原理和优化算法。高等数学、最优化理论…...
Milvus高性能向量数据库与大模型结合
Milvus | 高性能向量数据库,为规模而构建Milvus 是一个为 GenAI 应用构建的开源向量数据库。使用 pip 安装,执行高速搜索,并扩展到数十亿个向量。https://milvus.io/zh Milvus 是什么? Milvus 是一种高性能、高扩展性的向量数据…...
轮式机器人在复杂地形中如何选择合适的全局路径规划算法?
已思考完成 收起 嗯,用户问的是轮式机器人在复杂地形中如何选择合适的全局路径规划算法。首先,我需要理解复杂地形可能带来的挑战,比如崎岖的路面、动态障碍物、非结构化环境等等。轮式机器人在这里的运动控制需要考虑地形通过性、稳定性&…...
Metal学习笔记九:光照基础
光和阴影是使场景流行的重要要求。通过一些着色器艺术,您可以突出重要的对象、描述天气和一天中的时间并设置场景的气氛。即使您的场景由卡通对象组成,如果您没有正确地照亮它们,场景也会变得平淡无奇。 最简单的光照方法之一是 Phong 反射模…...
【字符串】最长公共前缀 最长回文子串
文章目录 14. 最长公共前缀解题思路:模拟5. 最长回文子串解题思路一:动态规划解题思路二:中心扩散法 14. 最长公共前缀 14. 最长公共前缀 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符…...
Linux提权之详细总结版(完结)
这里是我写了折磨多提权的指令的总结 我这里毫无保留分享给大家哦 首先神魔是提权 我们完整的渗透测试的流程是(个人总结的) 首先提升权限是我们拿到webshell之后的事情,如何拿到webshell,怎末才能拿到webshell,朋友们等我更新,持续更新中,下一篇更新的是windows提权 好了 废…...
week 3 - More on Collections - Lecture 3
一、Motivation 1. Java支持哪种类型的一维数据结构? Java中用于在单一维度中存储数据的数据结构,如arrays or ArrayLists. 2. 如何在Java下创建一维数据结构?(1-dimensional data structure) 定义和初始化这些一…...
Pwntools 的详细介绍、安装指南、配置说明
Pwntools:Python 开源安全工具箱 一、Pwntools 简介 Pwntools 是一个由 Security researcher 开发的 高效 Python 工具库,专为密码学研究、漏洞利用、协议分析和逆向工程设计。它集成了数百个底层工具的功能,提供统一的 Python API 接口&am…...
PLC(电力载波通信)网络机制介绍
1. 概述 1.1 什么是PLC 电力载波通讯即PLC,是英文Power line Carrier的简称。 电力载波是电力系统特有的通信方式,电力载波通讯是指利用现有电力线,通过载波方式将模拟或数字信号进行高速传输的技术。最大特点是不需要重新架设网络…...
Qt监控系统远程回放/录像文件远程下载/录像文件打上水印/批量多线程极速下载
一、前言说明 在做这个功能的时候,着实费了点心思,好在之前做ffmpeg加密解密的时候,已经打通了极速加密保存文件,主要就是之前的类中新增了进度提示信号,比如当前已经处理到哪个position位置,发个信号出来…...
自学微信小程序的第八天
DAY8 1、使用动画API即可完成动画效果的制作,先通过wx.createAnimation()方法获取Animation实例,然后调用Animation实例的方法实现动画效果。 表40:wx.createAnimation()方法的常用选项 选项 类型 说明 duration number 动画持续时间,单位为毫秒,默认值为400毫秒 timing…...
【java】@Transactional导致@DS注解切换数据源失效
最近业务中出现了多商户多租户的逻辑,所以需要分库,项目框架使用了mybatisplus所以我们自然而然的选择了同是baomidou开发的dynamic.datasource来实现多数据源的切换。在使用初期程序运行都很好,但之后发现在调用com.baomidou.mybatisplus.ex…...
003 SpringBoot集成Kafka操作
4.SpringBoot集成Kafka 文章目录 4.SpringBoot集成Kafka1.入门示例2.yml完整配置3.关键配置注释说明1. 生产者优化参数2. 消费者可靠性配置3. 监听器高级特性4. 安全认证配置 4.配置验证方法5.不同场景配置模板场景1:高吞吐日志收集场景2:金融级事务消息…...
Android SystemUI开发(一)
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.java frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java 关键文件 SystemUI 关键服务 简介 Dependency.class:处理系统依赖关系,提供资源或服…...
C#贪心算法
贪心算法:生活与代码中的 “最优选择大师” 在生活里,我们常常面临各种选择,都希望能做出最有利的决策。比如在超市大促销时,面对琳琅满目的商品,你总想用有限的预算买到价值最高的东西。贪心算法,就像是一…...
Vue程序下载
Vue是一个基于JavaScript(JS)实现的框架,想要使用它,就得先拿到Vue的js文件 Vue官网 Vue2:Vue.js Vue3:Vue.js - 渐进式 JavaScript 框架 | Vue.js 下载并安装vue.js 第一步:打开Vue2官网&a…...
【UCB CS 61B SP24】Lecture 17 - Data Structures 3: B-Trees学习笔记
本文以 2-3-4 树详细讲解了 B 树的概念,逐步分析其操作,并用 Java 实现了标准的 B 树。 1. 2-3 & 2-3-4 Trees 上一节课中讲到的二叉搜索树当数据是随机顺序插入的时候能够使得树变得比较茂密,如下图右侧所示,时间复杂度也就…...
机器学习决策树
一、香农公式 熵: 信息增益: 信息增益信息熵-条件熵 前者是初始信息熵大小,后者是因为条件加入后带来的确定性增加 信息增益表示得知特征X的信息而使得类Y的信息的不确定性减少的程度 信息增益越大说明影响越大 二、代码 ""&…...
Spring Boot + MyBatis 实现 RESTful API 的完整流程
后端开发:Spring Boot 快速开发实战 引言 在现代后端开发中,Spring Boot 因其轻量级、快速开发的特性而备受开发者青睐。本文将带你从零开始,使用 Spring Boot MyBatis 实现一个完整的 RESTful API,并深入探讨如何优雅地处理异…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
