当前位置: 首页 > article >正文

【Kubernetes】API server 限流 之 maxinflight.go

这个文件实现了一个基于信号量(Channel)的简单限流器。

基础知识

总共有四种channel

  1. 带缓冲的channel
    nonMutatingChan、mutatingChan 都是带缓冲的channel ,这类channel 的特点是:
    这允许最多 mutatingLimit /nonMutatingLimit 个请求同时获取令牌并执行,直到缓冲区满了才会阻塞新的请求。

    对带缓冲 channel 的发送操作在缓冲区未满、接收操作在缓冲区非空的情况下是异步的(发送或接收不需要阻塞等待)。

    但当缓冲区满了的情况下,对它进行发送操作的 Goroutine 就会阻塞挂起;当缓冲区为空的情况下,对它进行接收操作的 Goroutine 也会阻塞挂起。

    本代码中限流通道从创建到服务器关闭始终保持打开状态,因为这些通道用于控制并发请求数量的计数器,需要持续使用。

  2. 不带缓冲的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)        // 修改性请求限流器
}

限流处理流程

  1. 请求到达时
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. 特点总结

  1. 简单高效

    • 使用 Channel 实现,开销小
    • 无需复杂的算法
  2. 区分请求类型

    • 读写请求分开限流
    • 更细粒度的控制
  3. 特权用户豁免

    • 系统管理员不受限
    • 确保关键操作可执行
  4. 监控完善

    • 记录限流指标
    • 支持监控告警
  5. 无时间窗口

    • 不同于令牌桶等算法
    • 纯并发数控制

这是一个轻量级但有效的限流方案,适合 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)
}
  • 处理的请求类型getlistwatch 等读取操作
  • 判断依据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)
}
  • 处理的请求类型createupdatedeletepatch 等写入操作
  • 判断依据!nonMutatingRequestVerbs.Has(requestInfo.Verb)
  • 目的:限制并发写入操作的数量

主要区别

  1. 目标请求不同

    • nonMutatingChan 针对读操作
    • mutatingChan 针对写操作
  2. 资源消耗的差异

    • 写操作通常消耗更多资源,可能会修改集群状态
    • 读操作相对轻量,但数量可能更大
  3. 限流阈值不同

    • API Server 配置中可以为这两种通道设置不同的限流阈值
    • 通常写操作的限流阈值会低于读操作
  4. 监控指标分离

    • 代码中对两种请求的监控也是分开的:
      watermark.recordMutating(len(c))
      watermark.recordReadOnly(len(c))
      
  5. 限流效果的差异

    • 当读请求过多时,只会拒绝新的读请求,写请求不受影响
    • 当写请求过多时,只会拒绝新的写请求,读请求不受影响

实现原理

两种通道的底层实现方式相同,都是使用带缓冲的 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
}

意思解释

这是一个前置检查,用于判断是否需要启用限流功能:

  • 如果 nonMutatingLimitmutatingLimit 都为 0,表示不需要对任何类型的请求进行限流
  • 在这种情况下,函数直接返回原始的 handler,不添加任何限流逻辑
  • 相当于完全跳过限流处理,请求会直接传递给下一个处理器

为什么需要这个检查

  1. 性能优化

    • 如果不需要限流,避免创建不必要的通道和记录指标的开销
    • 减少请求处理的额外层级,提高性能
  2. 功能开关

    • 提供一种方式完全禁用限流功能
    • 管理员可以通过配置参数控制是否启用限流
  3. 兼容性

    • 允许那些不需要限流的环境(如小型开发集群)简化配置
    • 保持与低资源环境的兼容性

实际应用

在 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的设计哲学,但有几个可能的原因:

  1. 历史兼容性:可能是从早期版本演化而来,完全重构成本高

  2. 性能考虑:在高频调用的代码路径上,锁可能比通道有更低的开销

  3. 简单直接:对于简单的计数器场景,锁实现可能更直观

  4. 限流部分确实用了通道

    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 &#xff0c;这类channel 的特点是&#xff1a; 这允许最多 mutatingLimit /nonMutatingLimit 个请求同时获取令牌并执…...

Solr中得Core和Collection的作用和关系

Solr中得Core和Collection的作用和关系 一&#xff0c; 总结 在Apache Solr中&#xff0c;Core和Collection 是两个核心概念&#xff0c;他们分别用于单机模式和分布式模式&#xff08;SolrCloud&#xff09;中&#xff0c;用于管理和组织数据。 二&#xff0c;Core 定义&am…...

AI驱动的自动化留给人类的时间不多了

时间紧迫&#xff01;时间紧迫&#xff01;时间紧迫&#xff01; 关于AI工作流催生的行业任务自动化时间窗口&#xff0c;结合技术成熟度、成本效益、行业特性等维度&#xff0c;可划分为以下阶段&#xff1a; 一、技术渗透阶段&#xff08;2025-2028年&#xff09; 高重复性任…...

嵌入式Qt的动平衡仪完整设计方案

一、系统架构总览 #mermaid-svg-R5q0e12ntMzsskep {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-R5q0e12ntMzsskep .error-icon{fill:#552222;}#mermaid-svg-R5q0e12ntMzsskep .error-text{fill:#552222;stroke:#5…...

使用 Containerd 通过 HTTP 协议拉取 Harbor 私有镜像仓库的镜像

在 Kubernetes 1.24及以上版本环境中&#xff0c;docker不再被支持&#xff0c;主要使用Containerd 是常用的容器运行。默认情况下&#xff0c;Containerd 使用 HTTPS 协议与镜像仓库通信。然而&#xff0c;在某些场景下&#xff08;如测试环境或内部网络&#xff09;&#xff…...

Python解决“比赛配对”问题

Python解决“比赛配对”问题 问题描述测试样例解决思路代码 问题描述 小R正在组织一个比赛&#xff0c;比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制&#xff1a; 如果当前队伍数为 偶数&#xff0c;那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛&#xff0c;…...

Dify在Ubuntu20.04系统的部署

文章目录 一、dify 介绍1.核心功能优势2.应用场景 二、dify 安装(docker方式)1.代码库下载2.配置文件修改3.启动docker 容器 三、遇到问题与解决1.使用sudo docker compose up -d报错2.使用service docker start报错 一、dify 介绍 Dify 是一款开源的大语言模型&#xff08;LL…...

达梦:内存相关参数

目录 28个相关参数1. 内存池相关MEMORY_POOLMEMORY_N_POOLSMEMORY_BAK_POOL 2. 大缓冲区相关HUGE_BUFFERHUGE_BUFFER_POOLS 3. 共享缓冲区相关BUFFERBUFFER_POOLSBUFFER_MODEMAX_BUFFER 4. 快速池相关FAST_POOL_PAGES 5. 回收池相关RECYCLE_POOLS 6. 回滚段池相关ROLLSEG_POOLS…...

计算机毕设-基于springboot的融合多源高校画像数据与协同过滤算法的高考择校推荐系统的设计与实现(附源码+lw+ppt+开题报告)

博主介绍&#xff1a;✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围&#xff1a;Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…...

《Qt动画编程实战:轻松实现头像旋转效果》

《Qt动画编程实战&#xff1a;轻松实现头像旋转效果》 Qt 提供了丰富的动画框架&#xff0c;可以轻松实现各种平滑的动画效果。其中&#xff0c;旋转动画是一种常见的 UI 交互方式&#xff0c;广泛应用于加载指示器、按钮动画、场景变换等。本篇文章将详细介绍如何使用 Qt 实现…...

SpringBoot3—快速入门

一、简介 &#xff08;1&#xff09;前置知识 Java17Spring、SpringMVC、MyBatisMaven、IDEA &#xff08;2&#xff09;环境要求 &#xff08;3&#xff09;SpringBoot3是什么 核心概念&#xff1a;Spring Boot 底层是 Spring&#xff0c;能简单、快速地创建一个独立的、生…...

【Eureka 缓存机制】

今天简单介绍一下Eureka server 的缓存机制吧✌️✌️✌️ 一、先来个小剧场&#xff1a;服务发现的"拖延症" 想象你是个外卖小哥&#xff08;客户端&#xff09;&#xff0c;每次接单都要打电话问调度中心&#xff08;Eureka Server&#xff09;&#xff1a;“现在…...

Python基于机器学习的微博舆情情感分析系统,微博评论情感分析可视化系统(全新升级)

大家好&#xff0c;今天为大家带来的是Python基于机器学习的微博舆情情感分析系统&#xff0c;微博评论情感分析可视化系统&#xff0c;这个系统在原本的系统上进行优化升级。 算法从开源框架的 snlow &#xff0c;到支持机器学习的 lstm 算法可以手动输入语句&#xff0c;进行…...

Matlab地图绘制教程第2期—水陆填充图

上一期分享了海岸线图的绘制方法&#xff1a; 本着由浅入深的理念&#xff0c;本期再来分享一下水陆填充图的绘制方法。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;Matlab地图绘制教程系列&#xff0c;旨在降低大家使用Matlab进行地图类科研绘图的门槛&#xff0c;…...

云创智城YunCharge 新能源二轮、四轮充电解决方案(云快充、万马爱充、中电联、OCPP1.6J等多个私有单车、汽车充电协议)之新能源充电行业系统说明书

云创智城YunCharge 新能源充电行业系统说明书 ⚡官方文档 ⚡官网地址 1. 引言 随着全球环境保护和能源危机的加剧&#xff0c;新能源汽车行业得到了快速发展&#xff0c;充电基础设施建设也随之蓬勃发展。新能源充电行业系统旨在提供高效、便捷的充电服务&#xff0c;满足电…...

(八)Java-Collection

一、Collection接口 1.特点 Collection实现子类可以存放多个元素&#xff0c;每个元素可以是Object&#xff1b; 有些Collection的实现类&#xff0c;可以存放重复的元素&#xff0c;有些不可以&#xff1b; 有些Collection的实现类&#xff0c;有些是有序的&#xff08;Li…...

小程序高度问题背景scss

不同的机型&#xff0c;他的比例啥的都会不一样&#xff0c;同样的rpx也会有不同的效果。所以这里选择了取消高度。 <view class"box-border" :style"{padding-top: ${navHeight}px,}"><!-- 已登录 --><view v-if"userStore.userInfo&…...

HTML 日常开发常用标签

文章目录 HTML 日常开发常用标签1、基本结构标签2、内容标签3、多媒体标签4、表单标签5、列表和定义标签6、表格标签7、链接和图像8、元数据9、语义化标签&#xff08;HTML5新增&#xff09;10、框架和内联11、交互12、过时或不推荐使用的标签 HTML 日常开发常用标签 1、基本结…...

vue3表单验证的时候访问接口如果有值就通过否则不通过.主动去触发校验

页面有个身份证号码的校验。校验完身份证格式是否符合之后还要去访问接口查询这个用户是否存在。如果存在才通过验证。否则就校验不通过 <el-form ref"ruleFormRef" :model"form" label-width"140px" label-position"right" label…...

Cuppa CMS v1.0 任意文件读取(CVE-2022-25401)

漏洞简介&#xff1a; Cuppa CMS v1.0 administrator/templates/default/html/windows/right.php文件存在任意文件读取漏洞 漏洞环境&#xff1a; 春秋云镜中的漏洞靶标&#xff0c;CVE编号为CVE-2022-25401 漏洞复现 弱口令行不通 直接访问administrator/templates/defau…...

C# Dictionary 使用指南

C# Dictionary 使用指南 1. 简介 Dictionary<TKey, TValue> 是 C# 中一个非常常用的泛型集合类&#xff0c;用于存储键值对&#xff08;Key-Value Pair&#xff09;。它可以根据键快速查找对应的值&#xff0c;因此在需要快速查找和检索数据的场景下非常高效。 2. 基本…...

基于Spark的电商供应链系统的设计与实现

目录 1.研究背景与意义 2、国内外研究现状 3、相关理论与技术 &#xff08;一&#xff09;分布式计算系统Spark &#xff08;二&#xff09;数据仓库Hive &#xff08;三&#xff09;读取服务器本地磁盘的日志数据Flume &#xff08;四&#xff09;分布式消息队列Kafka …...

MYSQL数据备份与恢复(mysqldump)

MySQL备份之mysqldump 表级别备份还原 格式&#xff1a;mysqldump [OPTIONS] database [tables] 实例&#xff1a;把db_user数据库中的tb_student数据表进行备份 备份&#xff1a;#mysqldump db_user tb_student > /tmp/sqlbak/tb_student.sql -p 还原&#xff1a;#mysql 数…...

从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(二)

1.安装mogondb数据库 参考MongoDB安装配置教程&#xff08;详细版&#xff09;_mongodb安装详细步骤-CSDN博客 安装mondbcompass数据库连接工具 参考https://www.mongodb.com/zh-cn/docs/compass/current/connect/ 2.后端服务 1.创建src文件夹 并在src文件夹下创建 index…...

server.servlet.session.timeout: 12h(HTTP 会话的超时时间为 12 小时)

从你提供的配置文件&#xff08;应该是 Spring Boot 的 application.yml 或 application.properties 文件&#xff09;来看&#xff0c;以下部分与会话超时时间相关&#xff1a; server:servlet:session:timeout: 12h # timeout: 30cookie:name: VENDER_SID会话超时时间的…...

MySQL--聚集索引、辅助索引、回表查询和覆盖索引的原理

在MySQL中&#xff0c;索引是提高查询性能的核心工具。理解聚集索引、辅助索引、回表查询和覆盖索引的原理&#xff0c;对于优化数据性能至关重要。以下是对这些概念的详细解释以及优化方法。 一、聚集索引&#xff08;Clustered Index&#xff09; 聚集索引决定了表中数据的…...

使用vscode导出Markdown的PDF无法显示数学公式的问题

我的硬件环境是M2的MacBook air&#xff0c;在vscode中使用了Markdown PDF来导出md文件对应的PDF。但不管导出html还是PDF文件&#xff0c;数学公式都是显示的源代码。 我看了许多教程&#xff0c;给的是这个方法&#xff1a;在md文件对应的html文件中加上以下代码&#xff1a…...

从“记住我”到 Web 认证:Cookie、JWT 和 Session 的故事

文章目录 1. 初识 HTTP&#xff1a;一场没有记忆的对话2. Cookie&#xff1a;网站的“记忆” &#x1f36a;3. Session&#xff1a;服务端的“记忆” &#x1f3af;4. JWT&#xff1a;让用户自己带着“身份证” &#x1f511;5. Cookie vs Session vs JWT 总结 &#x1f4ca;6.…...

Idea编译项目很久之后,提示 Error:java:OutOfMemoryError:insufficient memory

项目挺老的的了&#xff0c;平常项目启动&#xff0c;也要挺久的&#xff0c;但是最起码能启动成功&#xff0c;今天下午的时候&#xff0c;项目启动了十几分&#xff0c;一直在转圈&#xff0c;后面控制台输出了这一行异常 Error:java:OutOfMemoryError:insufficient memory …...

wordpress使用CorePress主题设置项总结

宝塔面板设置 软件商店中安装的软件有&#xff1a;&#xff08;宝塔网站加速3.1&#xff09;&#xff08;Nginx 1.18.0&#xff09;&#xff08;MySql 5.6.50&#xff09;&#xff08;PHP-5.6&#xff09;&#xff08;phpMyAdmin 4.4&#xff09;&#xff08;Python项目管理器 …...