AI摘要 本文详细介绍了流媒体音视频系统的架构设计和关键技术实现，包括整体架构、核心功能技术实现以及技术难点与解决方案。文章从四个层面（基础组件层、核心服务层、算法引擎层、接入层）展开，探讨了视频上传与转码、视频推荐与分发、海量视频存储成本控制、高并发推流与低延迟播放、实时推荐系统高可用等关键技术点，旨在为需要构建OTT/IPTV或类似流媒体系统的朋友提供参考。 此内容根据文章生成，仅用于文章内容的解释与总结 引言做过机顶盒类的流媒体系统运维之后，我才真正体会到：一个“看电视”的背后，其实是一个复杂的架构体系。从内容采集、转码、加密、分发，到终端播放、监控运维，每一环都可能是系统稳定性的瓶颈。相比短视频平台强调的推荐和互动，这类系统更看重 稳定、低延迟和版权合规。一、整体架构短视频系统是典型的 “高并发、海量数据、低延迟” 综合系统，需兼顾 视频生产（上传、转码）、内容分发（推荐、CDN）、用户互动（点赞、评论、分享） 三大核心链路，整体采用 “分层解耦 + 微服务化” 架构设计，从下到上可分为基础组件层、核心服务层、算法引擎层、接入层四层，同时配套监控运维体系保障系统稳定性。1.1 架构图1.2 关键组件说明（1）接入层API 网关：采用 Kong/APISIX，负责请求路由、鉴权（JWT/OAuth2.0）、限流（令牌桶算法）、灰度发布（基于 用户 ID / 地域的流量切分），同时统一处理跨域、请求参数校验，屏蔽后端服务复杂性。负载均衡：使用 Nginx/LVS，结合 DNS 轮询实现 地域级流量分发，将用户请求引导至就近的机房节点，降低网络延迟（如北京用户接入北京机房，广州用户接入广州机房）。灰度发布：在关键功能上线时，特别是一些商业化能力，需要通过用户反馈、数据看板来查看当前功能的实践性，所以在发布时，我们会在网关层配置 用户灰度比例（如采用 Hash 桶散列分布的方式），让一部分用户首先体验到新功能。（2）核心服务层视频生产服务：负责视频 上传、转码、审核 流程，拆分为上传服务（接收客户端视频分片）、转码服务（调用转码集群处理多码率）、审核服务（对接内容审核接口）三个微服务。视频分发服务：核心是 “推流 - 拉流” 链路，包含 推流 服务（接收转码后的视频流，写入 CDN 源站）、拉流 服务（为客户端提供视频播放地址，支持断点续传）、缓存 服务（Redis 缓存热门视频元数据，减少 DB 访问）。互动服务：处理点赞、评论、分享、关注等互动行为，拆分为互动服务（处理点赞 / 评论的增删改查）、关系服务（管理用户关注 / 粉丝关系）、消息服务（推送评论通知 / 关注通知），本文主要关注核心模块的互动服务。用户服务：管理用户基础信息（账号、昵称、头像）、登录态（Session/Token 管理）、权益（会员特权、创作激励），数据存储采用 MySQL 分库分表（按用户 ID 哈希分片）。（3）算法引擎层推荐模块：核心是 “实时推荐 + 离线推荐” 双引擎，离线推荐基于 Spark/Flink 计算用户长期兴趣（T+1 更新），实时推荐基于 Flink 流处理计算用户短期兴趣（秒级更新），最终通过推荐 API 返回个性化视频列表。用户画像模块：基于用户基础信息、互动行为（点赞 / 评论 / 完播率）、观看时长等数据，通过 ES（Elasticsearch）存储用户标签（如 “喜欢美食”“关注汽车”），为推荐、内容审核提供数据支撑。内容审核模块：结合 AI 审核（识别违规画面 / 文字）和人工审核，对上传的视频进行实时过滤，违规视频直接拦截，疑似违规视频进入人工审核队列，保障内容合规性。（4）基础组件层存储组件：视频文件存储采用 对象存储（S3/OSS），支持海量文件存储和高并发访问；结构化数据 （用户信息、互动记录）存储采用 MySQL（主从架构），非结构化数据 （用户画像、日志）存储采用 ES/HDFS；缓存` 采用 Redis（集群模式，分片 + 哨兵），缓存热门视频元数据和用户登录态。计算组件：离线计算 采用 Spark（处理用户画像、推荐模型训练），实时计算采用 Flink（处理实时推荐、用户行为日志），为算法引擎提供算力支撑。消息队列：采用 Kafka（集群模式），解耦 服务间依赖，如视频上传完成后发送 “转码任务” 消息，转码完成后发送 “审核任务” 消息，避免服务直接调用导致的耦合。CDN：对接阿里云 / 腾讯云 CDN，将转码后的视频缓存到全国节点，用户拉取视频时从就近 CDN 节点获取，降低源站压力，提升播放速度。二、核心功能技术实现2.1 视频上传与转码上传流程：客户端采用 “分片上传”（将视频分成 1MB / 片），通过 HTTP/2 协议上传至上传服务，上传服务校验分片完整性后，调用对象存储合并分片，生成视频源文件；同时发送 “转码任务” 到 Kafka，转码服务消费消息后，调用 FFmpeg 转码集群，将源视频转成 480P/720P/1080P 多码率格式，转码完成后更新视频元数据状态。关键技术：分片上传（断点续传，避免网络中断重传整个视频）、异步转码（采用 Celery 任务队列，支持任务优先级，热门用户视频优先转码）、QUIC 协议（弱网环境下提升上传速度，降低丢包率）。2.2 视频推荐与分发推荐流程：用户打开 APP 后，客户端调用推荐 API，推荐服务先从 Redis 获取 “用户短期兴趣标签”（如最近 1 小时点赞的视频类型），再从 Spark 离线计算结果中获取 “用户长期兴趣标签”，结合用户地域、设备型号等信息，从 ES 中筛选符合条件的视频列表，通过负载均衡返回给客户端；同时，用户观看视频的行为（完播 / 点赞 / 评论）实时上报至 Flink 集群，更新用户实时兴趣标签，用于下一次推荐。关键技术：协同过滤算法（基于用户行为相似性推荐）、深度学习模型（如 DeepFM，融合用户特征和视频特征）、缓存预热（将热门推荐列表提前缓存到 Redis，降低推荐服务响应时间）。点赞流程：客户端发送点赞请求到互动服务，互动服务先校验用户登录态，再判断用户是否已点赞（Redis 查询，避免重复点赞），未点赞则更新 MySQL（点赞表，按用户 ID 分表）和 Redis（用户点赞集合），同时发送 “点赞通知” 消息到 Kafka，消息服务消费后推送通知给视频作者。关键技术：Redis 分布式锁（避免并发点赞导致的数据不一致）、MySQL 分表（按用户 ID 哈希分表，支撑亿级用户互动数据存储）、读写分离（点赞查询走 Redis，写入走 MySQL 主库，读走从库）、CDN 预热提升互动拉取的速度。三、技术难点与解决方案3.1 海量视频存储成本控制问题拆解视频单文件 1-2GB，千万级级存量视频需至少 100PB 存储空间，若全用标准存储，年成本超千万元。且视频访问存在 “冷热分化”—— 近 30 天视频播放量占总播放量的 80%，老旧视频访问频次极低，全量高成本存储造成资源浪费。分层存储策略首先，我们定义 “热 / 温 / 冷” 三级数据：热门视频（近 7 天播放量 Top10%、或单日播放量 > 1000 次）存对象存储 SSD 节点（如阿里云 OSS 高性能版），保障毫秒级拉流响应；普通视频（近 30 天有播放、非热门）存 HDD 标准节点（成本为 SSD 的 1/2）；归档视频（超过 3 个月无播放、或用户主动归档）迁移至冷存储（如 OSS 归档版，成本仅为标准存储的 1/5）。具体实现通过定时任务（如 Crontab 离线作业，每日凌晨执行）统计视频播放量，触发存储级别自动迁移。同时提供 “手动归档” API，允许创作者将历史视频转入冷存储，迁移过程中通过 “软链接” 保持播放地址不变，避免用户感知。视频压缩优化转码阶段默认采用 H.265 编码（对比 H.264，相同清晰度下码率降低 30%，即 10MB 视频可压缩至 7MB），同时针对不同场景做适配：短视频（15-60 秒）码率控制在 500-1500kbps（480P→500kbps、720P→1000kbps、1080P→1500kbps），避免码率冗余；长视频（超过 1 分钟）采用 “动态码率”（VBR），画面复杂段提高码率、静态段降低码率，进一步节省 20% 存储。兼容性处理针对不支持 H.265 的老旧设备（如部分 Android 7.0 以下机型），转码时额外生成 H.264 低码率版本，客户端通过设备检测接口选择对应格式，兼顾兼容性与成本。3.2 高并发推流与低延迟播放问题拆解峰值时段（如晚间 8-10 点）每秒新增 1000 + 条推流请求，若直接推至源站，会导致源站带宽瓶颈。同时百万级用户同时拉流时，单节点带宽压力超 10Gbps，易出现丢包、延迟（>3 秒），播放卡顿率超 5% 会显著降低用户留存。推流侧：边缘节点架构设计客户端通过 DNS 解析获取就近边缘节点（如用户在杭州，解析到阿里云杭州边缘节点），视频分片先上传至边缘节点，边缘节点完成 “分片校验 + 临时存储” 后，再异步同步至中心源站（采用增量同步，仅传输新分片），避免源站直接承接高并发推流。技术实现边缘节点部署 “推流代理服务”（基于 Nginx-RTMP 模块二次开发），支持 HTTP/2 和 QUIC 协议；客户端分片上传时，边缘节点返回 “分片唯一标识”，若网络中断，客户端可通过标识续传未完成分片，重传成功率提升至 99%。拉流侧：CDN 多级缓存 + 播放优化CDN 缓存策略采用 “边缘节点→区域节点→源站” 三级缓存，热门视频（播放量 > 10 万次）直接缓存至边缘节点（覆盖全国 300 + 城市），用户拉流时延迟 < 100ms；普通视频 缓存至区域节点（如华东、华南区域中心），边缘节点无缓存时从区域节点拉取，避免直接访问源站。客户端优化实现 “预加载 + 自适应码率” 双机制 —— 用户播放当前视频时，后台预加载下一个推荐视频（预加载 50% 内容），切换视频时实现 “无缝播放”；同时客户端实时检测网速（每 2 秒计算一次下载速率），若网速 < 1Mbps 自动切换至 480P，1-3Mbps 切换至 720P，>3Mbps 切换至 1080P，卡顿率可控制在 1% 以内。3.3 实时推荐系统高可用问题拆解实时推荐依赖三大环节 ——Flink 实时计算（秒级更新用户短期兴趣）、ES 检索（毫秒级筛选视频）、推荐服务 API（聚合结果），任一环节故障（如 Flink 集群重启、ES 分片异常）都会导致推荐列表空白或返回错误内容，影响核心用户体验（推荐列表是短视频 APP 的主要流量入口，不可用会导致 DAU 下降 40%+）。多级降级预案一级降级（实时引擎故障）当 Flink 实时计算集群延迟 > 5 秒（通过监控告警检测），推荐服务自动切换至 “离线推荐 + 短期缓存” 模式 —— 使用 Spark T+1 离线计算的用户长期兴趣标签，结合 Redis 缓存的 “用户最近 1 小时互动视频类型”（如最近点赞过 “美食”），生成推荐列表，避免实时数据缺失导致的兴趣偏差。二级降级（ES 故障）当 ES 集群健康度 <90%（如分片不可用），推荐服务直接返回 Redis 中缓存的 “热门视频列表”（每 10 分钟更新一次，存储 Top1000 条热门视频），同时标记 “降级状态”，前端显示 “热门推荐” 标识，降低用户感知。三级降级（推荐服务过载）通过 Sentinel 配置推荐 API 的 QPS 阈值（如单节点 1000QPS），当并发超阈值时，自动返回 “简化推荐列表”（仅返回 20 条视频，而非默认 50 条），同时拒绝非核心推荐请求（如 “相似视频推荐”），优先保障首页主推荐流可用。数据一致性保障离线推荐结果预加载每日凌晨 Spark 离线计算完成后，将用户长期兴趣推荐列表提前写入 Redis（按用户 ID 分片存储），推荐服务优先从 Redis 读取，减少 ES 访问依赖；同时通过 “版本号机制” 确保 Redis 与 ES 数据同步 —— 当 ES 数据更新时，同步更新 Redis 中的版本号，推荐服务发现版本不一致时触发增量更新。故障恢复自动切换通过监控系统（如 Prometheus+Grafana）实时检测各环节状态，当故障环节恢复（如 Flink 延迟恢复正常、ES 分片重新分配完成），推荐服务自动切换回正常模式，切换过程无感知（通过双活服务部署，旧模式服务继续处理存量请求，新模式服务承接增量请求，待存量请求处理完后下线旧模式）。
微服务下常见的应用发布概念与区别
发布策略在有关微服务、DevOps、Cloud-native的迭代过程中，不可避免的需要“上线”，上线就需要部署，需要部署就意味着有修改，修改则意味着有风险。发布这事儿，也越来越频繁，策略也越来越丝滑、无损，这里聊几个常见的。目前有很多部署模式，本文的目的是对部署方案的总结。包括如下：停机发布蓝绿发布灰度发布滚动发布A/B Test 发布一、停机发布停机发布是一种很古老的发布策略，说白了就是“夜深人静时，停机发布日”。新老迭代，升级发布，要先切断服务，停止旧程序，一顿操作猛如虎，然后再放开外部访问。停机发布也叫Recreate，也不用觉得太low，现在挺多场景都在用，比如部分游戏发布、企业内部管理系统发布。一方面是因为有些场景没必要做那么高级，还有一方面原因是很多时候，这种最安全，只是迭代速度慢，影响范围大。二、蓝绿发布定义蓝绿部署是不停老版本，部署新版本，然后进行测试，确认OK，将流量切到新版本，然后老版本同时也升级到新版本。第一步:初始化状态，流量全部打到版本1中。第二步:发布版本2（发布的代码包含新功能和Bug修复等）第三步:将流量从版本1切换到版本2。第四步：等到版本2测试正常，则删除版本1正在使用的资源（例如实例，数据库），从此正式用版本2。4、蓝绿发布的注意事项当切换到蓝色环境时，需要妥当处理未完成的业务和新的业务。如果你的数据库后端无法处理，会是一个比较麻烦的问题；蓝绿部署在蓝绿阶段，需要平时服务器的数量的两倍，所有，如果基础设施层有更好的支持就更好了。如果应用之间有MQ，需要在业务架构层面做好兼容。蓝绿发布对于增量升级有比较好的支持，但是对于涉及数据表结构变更等等不可逆转的升级，并不完全合适用蓝绿发布来实现，需要结合一些业务的逻辑以及数据迁移与回滚的策略才可以完全满足需求。过去的 10 年里，很多公司都在使用蓝绿部署（发布）来实现热部署，这种部署方式具有安全、可靠的特点。蓝绿部署虽然算不上“ Sliver Bullet”，但确实很实用。不过蓝绿发布在大型的微服务系统已经很难在用了，主要是考虑到服务器成本，以及如果本次改动涉及到的中间件过多，数据迁移和回滚都很麻烦。如果丹丹做应用层面的蓝绿，写代码就需要考虑各种向前和向后的兼容。BlueGreenDeployment中给出的一张图特别形象：三、A/B 测试（A/B Testing）A/B测试跟蓝绿部署完全是两码事。A/B测试是用来测试应用功能表现的方法，例如可用性、受欢迎程度、可见性等等。A/B测试通常用在应用的前端上，不过当然需要后端来支持。A/B测试与蓝绿部署的区别在于，A/B测试目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论，并确信该结论在推广到全部流量可信；蓝绿部署的目的是安全稳定地发布新版本应用，并在必要时回滚。A/B测试和蓝绿部署可以同时使用。四、灰度发布定义：灰度发布是一种增量发布的类型，在原有版本可用的i情况下，采用一种平滑过渡的方式发布。例如：现在有一个应用有100台服务器，我们用一台将某一台的服务器更新到最新版本作为“金丝雀”，然后通过服务路由的方式，将流量切到“金丝雀”的服务中，最后让测试人员测试新版本的性能和表现，以保障整体系统稳定的情况下，尽早发现、调整问题。如果测试没有问题，就一只只发布“金丝雀”，直到这100台的服务器都更新成最新版本。金丝雀发布的来历：注释：矿井中的金丝雀17世纪，英国矿井工人发现，金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯，金丝雀也会停止歌唱；而当瓦斯含量超过一定限度时，虽然鲁钝的人类毫无察觉，金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下，工人们每次下井都会带上一只金丝雀作为“瓦斯检测指标”，以便在危险状况下紧急撤离。灰度发布／金丝雀发布由以下几个步骤组成：1、准备好部署各个阶段的工件，包括：构建工件，测试脚本，配置文件和部署清单文件。2、从负载均衡列表中移除掉“金丝雀”服务器。3、升级“金丝雀”应用（排掉原有流量并进行部署）。4、对应用进行自动化测试。5、将“金丝雀”服务器重新添加到负载均衡列表中（连通性和健康检查）。6、如果“金丝雀”在线使用测试成功，升级剩余的其他服务器。（否则就回滚）灰度发布可以保证整体系统的稳定，在初始灰度的时候就可以发现、调整问题，以保证其影响度。灰度发布/金丝雀部署适用的场景：1、不停止老版本，额外搞一套新版本，不同版本应用共存。2、灰度发布中，常常按照用户设置路由权重，例如90%的用户维持使用老版本，10%的用户尝鲜新版本。3、经常与A/B测试一起使用，用于测试选择多种方案。AB test就是一种灰度发布方式，让一部分用户继续用A，一部分用户开始用B，如果用户对B没有什么反对意见，那么逐步扩大范围，把所有用户都迁移到B上面来。五、滚动发布滚动发布，一般是取出一个或者多个服务器停止服务，执行更新，并重新将其投入使用。周而复始，直到集群中所有的实例都更新成新版本。这种部署方式相对于蓝绿部署，更加节约资源——它不需要运行两个集群、两倍的实例数。我们可以部分部署，例如每次只取出集群的20%进行升级。这种方式也有很多缺点，例如：(1) 没有一个确定OK的环境。使用蓝绿部署，我们能够清晰地知道老版本是OK的，而使用滚动发布，我们无法确定。(2) 修改了现有的环境。(3) 如果需要回滚，很困难。举个例子，在某一次发布中，我们需要更新100个实例，每次更新10个实例，每次部署需要5分钟。当滚动发布到第80个实例时，发现了问题，需要回滚。此时，脾气不好的程序猿很可能想掀桌子，因为回滚是一个痛苦，并且漫长的过程。(4) 有的时候，我们还可能对系统进行动态伸缩，如果部署期间，系统自动扩容/缩容了，我们还需判断到底哪个节点使用的是哪个代码。尽管有一些自动化的运维工具，但是依然令人心惊胆战。并不是说滚动发布不好，滚动发布也有它非常合适的场景。闲言总结在实际的开发实践中，国内一般叫“灰度发布”。这种所谓的灰度，跟上述的金丝雀和滚动发布都不完全一样，我理解是两种理念的结合。就是每次按滚动发布里的操作一样，对现网某台服务器进行升级，然后慢慢放正式流量，验证OK，逐一扩大。当然，在某些组织的工作中，其实在自动化方面，是有可改进空间的。比如灰度某现网服务器时，可以在升级前，自动调整负载均衡，把比重调为0，然后自动慢慢放大到100%，再根据成功率逐一灰度其它节点。
熔断、隔离、重试、超时，高可用架构流量治理核心策略全掌握
熔断、隔离、重试、超时，高可用架构流量治理核心策略全掌握对于人类的身体健康来说，“三高”是个大忌，但在计算机界，系统的“三高”却是健康的终极目标。本文将介绍一下流量治理是如何维持这种“三高”系统的健康，保障数据流动的均衡与效率，就如同营养顾问在维持人类健康饮食中所起的作用一般。分享概要一、可用性的定义二、流量治理的目的三、流量治理的手段四、总结一、可用性的定义在探讨高可用架构之前，让我们以 O2 系统为例，解释一下何谓可用性。O2 是腾讯内部的一个广告投放系统，专注于提升投放效率、分析广告效果，拥有自动化广告投放、AIGC 自动化素材生产等多种功能。其整体架构概览如下：一个完善的架构应该具备3个能力，也就是身体的“三高”：高性能；高可用；易扩展。理解高可用时，通常参考两个关键指标：平均故障间隔（Mean Time Between Failure，简称 MTBF）表示两次故障的间隔时间，也就是系统正常运行的平均时间，这个时间越长，说明系统的稳定性越高；故障恢复时间（Mean Time To Repair，简称 MTTR）表示系统发生故障后恢复的时间，这个时间越短，说明故障对用户的影响越小。可用性（Availability）的计算公式：Availability= MTBF / (MTBF + MTTR) * 100%这个公式反映了一个简单的事实：只有当系统故障间隔时间越长，且恢复时间越短，系统的整体可用性才会更高。因此，在设计高可用系统时，我们的核心目标是延长 MTBF，同时努力缩短 MTTR，以减少任何潜在故障对服务的影响。二、流量治理的目的在保障系统高可用性的过程中，流量治理扮演着关键角色：它不仅帮助平衡和优化数据流，还提高了系统对不同网络条件和故障情况的适应性，是确保服务高效连续运行的不可或缺的环节。流量治理的主要目的包括：网络性能优化通过流量分配、负载均衡等技术，确保网络资源的高效利用，减少延迟和避免拥塞；服务质量保障确保关键应用和服务的流量优先级，以保障业务关键操作的流畅运行；故障容错和弹性在网络或服务出现问题时，通过动态路由和流量重定向等机制，实现故障转移和自我恢复，以维持服务的持续可用性；安全性实施流量加密、访问控制和入侵检测等措施，保护网络和数据不受未授权访问或攻击；成本效益通过有效管理流量，降低带宽需求和相关成本，同时提高整体系统效率。三、流量治理的手段1. 熔断微服务系统中，一个服务可能会依赖多个服务，并且有一些服务也依赖于它。当“媒体中心”服务的其中一个依赖服务出现故障（比如用户服务），媒体中心只能被动地等待依赖服务报错或者请求超时；下游连接池会被逐渐耗光；入口请求大量堆积，CPU、内存等资源被逐渐耗尽，最终导致服务宕掉。而依赖“媒体中心”服务的上游服务，也会因为相同的原因出现故障，一系列的级联故障最终会导致整个系统不可用；合理的解决方案是引入熔断器和优雅降级，通过尽早失败来避免局部不稳定而导致的整体雪崩。1）传统熔断器当请求失败比率达到一定阈值之后，熔断器开启，并休眠一段时间（由配置决定）。这段休眠期过后，熔断器将处于半开状态，在此状态下将试探性地放过一部分流量，如果这部分流量调用成功后，再次将熔断器关闭，否则熔断器继续保持开启并进入下一轮休眠周期。引入传统熔断器的请求时序图：传统熔断器实现 关闭、打开、半开 三个状态：关闭（Closed）：默认状态。允许请求到达目标服务，同时统计在窗口时间内的成功和失败次数，如果达到错误率阈值将会切换为“打开”状态；打开（Open）：对应用的请求会立即返回错误响应或执行预设的失败降级逻辑，而不调用目标服务；半开（Half-Open）：进入“打开”状态会维护一个超时时间，到达超时时间后开始进入该状态，允许应用程序一定数量的请求去调用目标服务。熔断器会对成功执行的调用进行计数，达到配置的阈值后会认为目标服务恢复正常，此时熔断器回到“关闭”状态；如果有请求出现失败的情况，则回到“打开”状态，并重新启动超时计时器，再给系统一段时间来从故障中恢复。当进入 Open 状态时会拒绝所有请求；进入 Closed 状态时瞬间会有大量请求，这时服务端可能还没有完全恢复，会导致熔断器又切换到 Open 状态；而 Half-Open 状态存在的目的在于实现了服务的自我修复，同时防止正在恢复的服务再次被大量打垮；所以传统熔断器在实现上过于一刀切，是一种比较刚性的熔断策略。2）Google SRE 熔断器是否可以做到在熔断器 Open 状态下（但是后端未 Shutdown）仍然可以放行少部分流量呢？Google SRE 熔断器提供了一种算法：客户端自适应限流（client-side throttling）。解决的办法就是客户端自行限制请求速度，限制生成请求的数量，超过这个数量的请求直接在本地回复失败，而不会真正发送到服务端。该算法统计的指标依赖如下两种，每个客户端记录过去两分钟内的以下信息（一般代码中以滑动窗口实现）。requests：客户端请求总量注：The number of requests attempted by the application layer(at the client, on top of the adaptive throttling system)accepts：成功的请求总量 - 被 accepted 的量注：The number of requests accepted by the backendGoogle SRE 熔断器的工作流程：在通常情况下（无错误发生时） requests == accepts ；当后端出现异常情况时，accepts 的数量会逐渐小于 requests；当后端持续异常时，客户端可以继续发送请求直到 requests = K∗accepts，一旦超过这个值，客户端就启动自适应限流机制，新产生的请求在本地会被概率（以下称为p）丢弃；当客户端主动丢弃请求时，requests 值会一直增大，在某个时间点会超过 K∗accepts，使 p 计算出来的值大于 0，此时客户端会以此概率对请求做主动丢弃；当后端逐渐恢复时，accepts 增加，（同时 requests 值也会增加，但是由于 K 的关系，K*accepts的放大倍数更快），使得 (requests − K×accepts) / (requests + 1) 变为负数，从而 p == 0，客户端自适应限流结束。客户端请求被拒绝的概率（Client request rejection probability，以下简称为 p）p 基于如下公式计算（其中 K 为倍率 - multiplier，常用的值为 2）。当 requests − K∗accepts <= 0 时，p == 0，客户端不会主动丢弃请求；反之， p 会随着 accepts 值的变小而增加，即成功接受的请求数越少，本地丢弃请求的概率就越高。客户端可以发送请求直到 requests = K∗accepts， 一旦超过限制， 按照 p 进行截流。对于后端而言，调整 K 值可以使得自适应限流算法适配不同的服务场景。降低 K 值会使自适应限流算法更加激进（允许客户端在算法启动时拒绝更多本地请求）；增加 K 值会使自适应限流算法变得保守一些（允许服务端在算法启动时尝试接收更多的请求，与上面相反）。熔断本质上是一种快速失败策略。旨在通过及时中断失败或超时的操作，防止资源过度消耗和请求堆积，从而避免服务因小问题而引发的雪崩效应。2. 隔离微服务系统中，隔离策略是流量治理的关键组成部分，其主要目的是避免单个服务的故障引发整个系统的连锁反应。通过隔离，系统能够局部化问题，确保单个服务的问题不会影响到其他服务，从而维护整体系统的稳定性和可靠性。常见的隔离策略：1）动静隔离动静隔离通常是指将系统的动态内容和静态内容分开处理。动态内容：指需要实时计算或从数据库中检索的数据，通常由后端服务提供；可以通过缓存、数据库优化等方法来提高动态内容的处理速度。静态内容：指可以直接从文件系统中获取的数据，例如图片、音视频、前端的 CSS、JS 文件等静态资源；可以存储到 OSS 并通过 CDN 进行访问加速。2）读写隔离读写隔离通常是指将读操作和写操作分离到不同的服务或实例中处理。大部分的系统里读写操作都是不均衡的，写数据可能远远少于读数据；读写隔离得以让读服务和写服务独立扩展。DDD中有一种常用的模式：CQRS（Command Query Responsibility Segregation，命令查询职责分离）来实现读写隔离。写服务：负责处理所有的写操作，例如创建、更新和删除数据；通常会有一个或多个数据库或数据存储，用于保存系统的数据。读服务：负责处理所有的读操作，例如查询和检索数据；可以有独立的数据库或数据存储，也可以使用缓存来提高查询的性能。事件驱动：当写服务处理完一个写操作后，通常会发布一个事件，通知读服务数据已经发生变化；读服务可以监听这些事件，并更新其数据库或缓存，以保证数据的一致性。独立扩展：通过 CQRS 模式，读服务和写服务可以独立地进行扩展；如果系统的读负载较高，可以增加读服务的实例数量；如果写负载较高，可以增加写服务的实例数量。3）核心隔离核心隔离通常是指将资源按照 “核心业务”与 “非核心业务”进行划分，优先保障“核心业务”的稳定运行AI助手。核心/非核心故障域的差异隔离（机器资源、依赖资源）；核心业务可以搭建多集群通过冗余资源来提升吞吐和容灾能力；按照服务的核心程度进行分级。1级：系统中最关键的服务，如果出现故障会导致用户或业务产生重大损失；2级：对于业务非常重要，如果出现故障会导致用户体验受到影响，但不会导致系统完全无法使用；3级：会对用户造成较小的影响，不容易注意或很难发现；4级：即使失败，也不会对用户体验造成影响。4）热点隔离热点隔离通常是指一种针对高频访问数据（热点数据）的隔离策略。可以帮助微服务系统更高效地处理热点数据的访问请求；需要有机制来识别和监控热点数据；分析系统的历史访问记录；观察系统的监控告警信息等。将访问频次最高的 Top K 数据缓存起来，可以显著减少对后端存储服务的访问压力，同时提高数据访问的速度；可以创建一个独立的缓存服务来存储和管理热点数据，实现热点数据的隔离。5）用户隔离用户隔离通常是指按照不同的分组形成不同的服务实例。这样某个服务实例宕机了也只会影响对应分组的用户，而不会影响全部用户。基于 O2-SAAS 系统的租户概念，按照隔离级别的从高到低有如下几种隔离方式：每个租户有独立的服务与数据库网关根据 tenant\_id 识别出对应的服务实例进行转发每个租户有共享的服务与独立的数据库：用户服务根据 tenant\_id 确定操作哪一个数据库每个租户有共享的服务与数据库：用户服务根据 tenant\_id 确定操作数据库的哪一行记录6）进程隔离进程隔离通常是指系统中每一个进程拥有独立的地址空间，提供操作系统级别的保护区。一个进程出现问题不会影响其他进程的正常运行，一个应用出错也不会对其他应用产生副作用。容器化部署便是进程隔离的最佳实践：7）线程隔离线程隔离通常是指线程池的隔离，在应用系统内部，将不同请求分类发送给不同的线程池，当某个服务出现故障时，可以根据预先设定的熔断策略阻断线程的继续执行。如图，接口A 和 接口B 共用相同的线程池，当 接口A 的访问量激增时，接口C 的处理效率就会被影响，进而可能产生雪崩效应；使用线程隔离机制，可以将 接口A 和 接口B 做一个很好的隔离。8）集群隔离集群隔离通常是指将某些服务单独部署成集群，或对于某些服务进行分组集群管理。具体来说就是每个服务都独立成一个系统，继续拆分模块，将功能微服务化：9）机房隔离机房隔离通常是指在不同的机房或数据中心部署和运行服务，实现物理层面的隔离。机房隔离的主要目的有两个：解决数据容量大、计算和 I/O 密集度高的问题将不同区域的用户隔离到不同的地区，比如将湖北的数据存储在湖北的服务器，浙江的数据存储在浙江的服务器，这种区域化的数据管理能有效地分散流量和系统负载；增强数据安全性和灾难恢复能力通过在不同地理位置建立服务的完整副本（包括计算服务和数据存储），系统可以实现异地多活或冷备份。这样，即使一个机房因自然灾害或其他紧急情况受损，其他机房仍能维持服务，确保数据安全和业务连续性。3. 重试如何在不可靠的网络服务中实现可靠的网络通信，这是计算机网络系统中避不开的一个问题。微服务架构中，一个大系统被拆分成多个小服务，小服务之间大量的 RPC 调用，过程十分依赖网络的稳定性。网络是脆弱的，随时都可能会出现抖动，此时正在处理中的请求有可能就会失败。场景：O2 Marketing API 服务调用媒体接口拉取数据。对于网络抖动这种情况，解决的办法之一就是重试。但重试存在风险，它可能会解决故障，也可能会放大故障。对于网络通信失败的处理一般分为以下几步：感知错误：通过不同的错误码来识别不同的错误，在 HTTP 中 status code 可以用来识别不同类型的错误。重试决策：这一步主要用来减少不必要的重试，比如 HTTP 的 4xx 的错误，通常 4xx 表示的是客户端的错误，这时候客户端不应该进行重试操作，或者在业务中自定义的一些错误也不应该被重试。根据这些规则的判断可以有效的减少不必要的重试次数，提升响应速度。重试策略：重试策略就包含了重试间隔时间，重试次数等。如果次数不够，可能并不能有效的覆盖这个短时间故障的时间段，如果重试次数过多，或者重试间隔太小，又可能造成大量的资源(CPU、内存、线程、网络)浪费。对冲策略：对冲是指在不等待响应的情况主动发送单次调用的多个请求，然后取首个返回的回包。如果重试之后还是不行，说明这个故障不是短时间的故障，而是长时间的故障。那么可以对服务进行熔断降级，后面的请求不再重试，这段时间做降级处理，减少没必要的请求，等服务端恢复了之后再进行请求，这方面的工程实现很多，比如 go-zero 、 sentinel 、hystrix-go。1）重试方式如何在不可靠的网络服务中实现可靠的网络通信，这是计算机网络系统中避不开的一个问题。同步重试：程序在调用下游服务失败的时候重新发起一次；实现简单，能解决大部分网络抖动问题，是比较常用的一种重试方式。异步重试：如果服务追求数据的强一致性，并且希望在下游服务故障的时候不影响上游服务的正常运行，此时可以考虑使用异步重试；将请求信息丢到消息队列中，由消费者消费请求信息进行重试；上游服务可以快速响应请求，由消费者异步完成重试。2）最大重试次数无限重试可能会导致系统资源（网络带宽、CPU、内存）的耗尽，甚至引发重试风暴。应评估系统的实际情况和业务需求来设置最大重试次数：设置过低，可能无法有效地处理该错误；设置过高，同样可能造成系统资源的浪费。3）退避策略我们知道重试是一个 trade-off 问题：一方面要考虑到本次请求时长过长而影响到的业务的忍受度；一方面要考虑到重试对下游服务产生过多请求带来的影响。退避策略基于重试算法实现。重试算法有多种，思路都是在重试之间加上一个间隔时间。① 线性间隔（Linear Backoff）每次重试间隔时间是固定的，比如每 1s 重试一次。② 线性间隔+随机时间（Linear Jitter Backoff）有时候每次重试间隔时间一致可能会导致多个请求在同一时间请求；加入随机时间可以在线性间隔时间的基础上波动一个百分比的时间。③ 指数间隔（Exponential Backoff）间隔时间是指数型递增，例如等待 3s、9s、27s 后重试。④ 指数间隔+随机时间（Exponential Jitter Backoff）与 Linear Jitter Backoff 类似，在指数递增的基础上添加一个波动时间。上面有两种策略都加入了 扰动（jitter），目的是防止 惊群问题 （Thundering Herd Problem） 的发生。所谓惊群问题当许多进程都在等待被同一事件唤醒的时候，当事件发生后最后只有一个进程能获得处理。其余进程又造成阻塞，这会造成上下文切换的浪费所以加入一个随机时间来避免同一时间同时请求服务端还是很有必要的。⑤ gRPC 实现gRPC 便是使用了 指数间隔+随机时间 的退避策略进行重试：GRPC Connection Backoff Protocol （github.com/grpc/grpc/blob/master/doc/connection-backoff.md）/* 伪代码 */ConnectWithBackoff()current\_backoff = INITIAL\_BACKOFFcurrent\_deadline = now() + INITIAL\_BACKOFFwhile (TryConnect(Max(current\_deadline, now() + MIN\_CONNECT\_TIMEOUT))!= SUCCESS)SleepUntil(current\_deadline)current\_backoff = Min(current\_backoff * MULTIPLIER, MAX\_BACKOFF)current\_deadline = now() + current\_backoff +UniformRandom(-JITTER * current\_backoff, JITTER * current\_backoff)关于伪代码中几个参数的说明：INITIAL\_BACKOFF：第一次重试等待的间隔；MULTIPLIER：每次间隔的指数因子；JITTER：控制随机的因子；MAX\_BACKOFF：等待的最大时长，随着重试次数的增加，我们不希望第N次重试等待的时间变成几十分钟这样不切实际的值；MIN\_CONNECT\_TIMEOUT：一次成功的请求所需要的时间，即使是正常的请求也会有响应时间，重试时间间隔需要大于这个响应时间才不会出现请求明明已经成功，但却进行重试的操作。4）重试风暴通过一张图来简单介绍下重试风暴：DB 负载过高时，Service C 对 DB 的请求出现失败；因为配置了重试机制，Service C 对 DB 发起了最多 3 次请求；链路上为了避免网络抖动，上游的服务均设置了超时重试 3 次的策略；这样在一次业务请求中，对 DB 的访问可能达到 3^(n) 次。此时负载高的 DB 便被卷进了重试风暴中，最终很可能导致服务雪崩。应该怎么避免重试风暴呢？笔者整理了如下几种方式：① 限制单点重试：一个服务不能不受限制地重试下游，很容易造成下游服务被打挂；除了设置最大重试次数，还需要限制重试请求的成功率。② 引入重试窗口：基于断路器的思想，限制 请求失败/请求成功 的比率，给重试增加熔断功能；常见的实现方式是引入滑动窗口。这里介绍一下重试窗口：内存中为每一类 RPC 调用维护一个滑动窗口，窗口分多个 bucket；bucket 每秒生成 1 个，记录 1 秒内 RPC 的请求结果数据（成功/失败 次数）；新的 bucket 生成时，淘汰最早的一个 bucket；新的请求到达该 RPC 服务并且失败时，根据窗口内 失败/成功 比率以及失败次数是否超过阈值来判断是否可以重试。比如阈值设置 0.1，即失败率超过 10% 时不进行重试。③ 限制链路重试多级链路中如果每层都配置重试可能导致调用量指数级扩大；核心是限制每层都发生重试，理想情况下只有最下游服务发生重试；Google SRE 中指出了 Google 内部使用特殊错误码的方式来实现。关于 Google SRE 的实现方式，大致细节如下：统一约定一个特殊的 status code ，它表示：调用失败，但别重试；任何一级重试失败后，生成该 status code 并返回给上层；上层收到该 status code 后停止对这个下游的重试，并将错误码再传给自己的上层。该方法可以有效避免重试风暴，但请求链路上需要上下游服务约定好重试状态码并耦合对于的逻辑，一般需要在框架层面上做出约束。5）对冲策略有时候我们接口只是偶然会出问题，并且我们的下游服务并不在乎多请求几次，那么我们可以考虑对冲策略AI助手。对冲是指在不等待响应的情况下主动发送单次调用的多个请求，然后取首个返回的回包。请求流程第一次正常的请求正常发出；在等待固定时间间隔后，没有收到正确的响应，第二个对冲请求会被发出；再等待固定时间间隔后，没有收到任何前面两个请求的正确响应，第三个会被发出；一直重复以上流程直到发出的对冲请求数量达到配置的最大次数；一旦收到正确响应，所有对冲请求都会被取消，响应会被返回给应用层。与普通重试的区别对冲在超过指定时间没有响应就会直接发起请求，而重试则必须要服务端响应后才会发起请求。所以对冲更像是比较激进的重试策略。使用对冲的时候需要注意一点是，因为下游服务可能会做负载均衡策略，所以要求请求的下游服务一般是要求幂等的，能够在多次并发请求中是安全的，并且是符合预期的。普通重试时序图：对冲重试时序图：4.降级降级是从系统功能角度出发，人为或自动地将某些不重要的功能停掉或者简化，以降低系统负载，这部分释放的资源可以去支撑更核心的功能。目的是为了提升系统的可用性，同时要寻找到用户体验与降级成本的平衡点；降级属于有损操作。简而言之，弃卒保帅。1）降级策略以 O2 系统举例，有以下几类降级策略：虽说故障是不可避免的，要达到绝对高可用一般都是使用冗余+自动故障转移，这个时候其实也不需要降级措施了。但是这样带来的成本较高，而且可用性、成本、用户体验3者本身之间是需要权衡的，一般来说他们之前会是这样的关系：2）自动降级适合触发条件明确可控的场景，比如请求调用失败次数大于一定的阈值，服务接口超时等情况；对于一些旁路服务，服务负载过高也可以直接触发自动降级。3）手动降级降级操作都是有损的，部分情况下需要根据对业务的影响程度进行手动降级；通常需要先制定降级的分级策略，影响面由浅至深。4）执行降级降级的策略还是比较丰富的，因此需要从多个角度去化简首先，将一部分判断条件简单的降级通过自动化手段去实现；其次，根据对业务的影响程度，对降级进行分级，达到有层次的降级效果；最后，通过高频演练，确保降级的有效性。5）与限流的区别降级依靠牺牲一部分功能或体验保住容量，而限流则是依靠牺牲一部分流量来保住容量。一般来说，限流的通用性会更强一些，因为每个服务理论上都可以设置限流，但并不是每个服务都能降级，比如 O2 系统中的登录服务和用户服务，就不可能被降级（没有这两个服务，用户都没法使用系统了）。5. 超时超时是一件很容易被忽视的事情。早期架构发展阶段，大家或多或少有过遗漏设置超时或者超时设置太长导致系统被拖慢甚至挂起的经历。随着微服务架构的演进，超时逐渐被标准化到 RPC 中，并可通过微服务治理平台快捷调整超时参数。传统超时会设定一个固定的阈值，响应时间超过阈值就返回失败。在网络短暂抖动的情况下，响应时间增加很容易产生大规模的成功率波动。服务的响应时间并不是恒定的，在某些长尾条件下可能需要更多的计算时间，为了有足够的时间等待这种长尾请求响应，我们需要把超时设置足够长，但超时设置太长又会增加风险，超时的准确设置经常困扰我们。1）超时策略目前业内常用的超时策略有：固定超时时间；EMA 动态超时。2）超时控制超时控制的本质是 fail fast，良好的超时控制可以尽快清空高延迟的请求，尽快释放资源避免请求堆积。服务间超时传递一个请求可能由一系列 RPC 调用组成，每个服务在开始处理请求前应检查是否还有足够的剩余时间处理，也就是应该在每个服务间传递超时时间。如果都使用每个 RPC 服务设置的固定超时时间，这里以上图为例：A -> B，设置的超时时间为 3s；B 处理耗时为 2s，并继续请求 C；如果使用了超时传递那么 C 的超时时间应该为 1s，这里不采用所以超时时间为配置的 3s；C 继续执行耗时为 2s，此时最上层（A）设置的超时时间已截止；C -> D的请求对 A 来说已经失去了意义。进程内超时传递上图流程如下：一个进程内串行调用了 MySQL、Redis 和 Service B，设置总的请求时间为 3s；请求 MySQL 耗时 1s 后再请求 Redis，这时的超时时间为 2s，Redis 执行耗时 500 ms；再请求 Service B，这时超时时间为 1.5s。由于每个组件或服务都会在配置文件中配置固定的超时时间，使用时应该取实际剩余时间与配置的超时时间中的最小值。Context 实现超时传递3）EMA 动态超时如果我们的微服务系统对这种短暂的时延上涨具备足够的容忍能力，可以考虑基于 EMA 算法动态调整超时时长。EMA 算法引入“平均超时”的概念，用平均响应时间代替固定超时时间，只要平均响应时间没有超时即可，而不是要求每次请求都不能超时。算法实现当平均响应时间（EMA）大于超时时间限制（Thwm），说明平均情况表现很差，动态超时时长（Tdto）就会趋近于超时时间限制（Thwm），降低弹性；当平均响应时间（EMA）小于超时时间限制（Thwm），说明平均情况表现很好，动态超时时长（Tdto）就可以超出超时时间限制（Thwm），但会低于最大弹性时间（Tmax），具备一定的弹性。算法实现参考：github.com/jiamao/ema-timeout总而言之：总体情况不能超标；平均情况表现越好，弹性越大；平均情况表现越差，弹性越小。适用条件：固定业务逻辑，循环执行；程序大部分时间在等待响应，而不是 CPU 计算或者处理 I/O 中断；服务是串行处理模式，容易受异常、慢请求阻塞；响应时间不宜波动过大；服务可以接受有损。使用方法EMA 动态超时根据业务的请求链路有两种用法：用于非关键路径：Thwm 设置的相对小，当非关键路径频繁耗时增加甚至超时时，降低超时时间，减少非关键路径异常带来的资源消耗，提升服务吞吐量。用于关键路径：Thwm 设置的相对大，用于长尾请求耗时比较多的场景，提高关键路径成功率。在2）小节有提到，一般超时时间会在链路上传递，避免上游已经超时，下游继续浪费资源请求的情况。这个传递的超时时间一般是没有考虑网络耗时或不同服务器的时钟不一致的，所以会存在一定的偏差。4）超时策略的选择超时策略的选择：剩余资源 = 资源容量 - QPS 单次请求消耗资源请求持续时长 – 资源释放所需时长关键路径选择固定超时；非关键路径开启 EMA 动态超时，防止一直出问题导致服务耗时增加、吞吐量降低。5）超时时间的选择合理的设置超时可以减少服务资源消耗、避免长时间阻塞、降低服务过载的概率；超时时间过长容易引起降级失效、系统崩溃；超时时间过短因⽹络抖动⽽告警频繁，造成服务不稳定。如何选择合适的超时阈值？超时时间选择需要考虑的几个点：被调服务的重要性；被调服务的耗时 P99、P95、P50、平均值；网络波动；资源消耗；用户体验。6. 限流预期外的突发流量总会出现，对我们系统可承载的容量造成巨大冲击，极端情况下甚至会导致系统雪崩。当系统的处理能力有限时，如何阻止计划外的请求继续对系统施压，这便是限流的作用之处。限流可以帮助我们应对突发流量，通过限制服务的请求率来保护服务不被过载。除了控制流量，限流还有一个应用目的是用于控制用户行为，避免无用请求，比如频繁地下载系统中的数据表格。限流一般来说分为客户端限流和服务端限流两类。1）客户端限流在客户端限流中，由于请求方和被请求方的关系明确，通常采用较为简单的限流策略，如结合分布式限流和固定的限流阈值。客户端的限流阈值可被视作被调用方对主调方的配额。合理设定限流阈值的方法包括：容量评估：通过单机压测确定服务的单机容量模型，并与下游服务协商以了解他们的限流阈值容量规划：根据日常运行、运营活动和节假日等不同场景，提前进行容量评估和规划全链路压测：通过模拟真实场景的压测，评估现有限流值的合理性在限流算法方面，大家也都已经耳熟能详。像滑动窗口、漏桶和令牌桶均是常用的限流算法。这些算法各有特点，能有效管理客户端的请求流量，保障系统的稳定运行。这里笔者简单梳理了一张常用的限流算法的思维导图，主要阐述每个算法的局限性，需要根据实际应用场景选择合适的算法：2）服务端限流服务端限流旨在通过主动丢弃或延迟处理部分请求，以应对系统过载的情况。服务端限流实现的两个关键点：① 如何判断系统是否过载，常用的判断依据包括：资源使用率；请求成功率；响应时间；请求排队时间。② 过载时如何选择要丢弃的请求，常用的判断依据包括：按照主调方（客户端）的重要性来划分优先级；根据用户的重要性进行区分。关于服务端限流在业界内的实践应用，笔者这里整理了两个示例：开源的 Sentinel 采用类似 TCP BBR 的限流方法。它基于利特尔法则，计算时间窗口内的最大成功请求数 （MaxPass） 和最小响应时间（MinRt）。当 CPU 使用率超过 80% 时，根据 MaxPass 和 MinRt 计算窗口内理论上可以通过的最大请求量，进而确定每秒的最大请求数。如果当前处理中的请求数超过此计算值，则进行请求丢弃。微信后台则使用请求的平均排队时间作为系统过载的判断标准。当平均等待时间超过 20 毫秒时，它会以一定的降速因子来过滤部分请求。相反，如果判断平均等待时间低于 20 毫秒，则会逐渐提高请求的通过率。这种“快速降低，缓慢提升”的策略有助于防止服务的大幅波动。四、总结想要让系统长期“三高”，流量治理只是众多策略的其中一个，其他还有像存储高可用、缓存、负载均衡、故障转移、冗余设计、可回滚设计等等均是确保系统长期稳定运行的关键因素，笔者也期待在后续就这些策略再和大家进行分享。本文在介绍高可用架构中流量治理部分时，我们详细讨论了从熔断机制到隔离策略、重试逻辑、降级方案，以及超时和限流控制等多种手段，这里简单归纳一下：熔断 机制，包括传统熔断器和 Google SRE 模型，作为防止系统过载的重要工具隔离 策略，如动静隔离、读写隔离和机房隔离，通过物理或逻辑上分离资源和请求，减少单点故障的影响重试 策略，包括同步和异步重试，以及各种退避机制，帮助在失败时优雅地恢复服务。降级 操作，区分自动和手动降级，作为服务负载过重时的应急措施超时 控制，通过精细的策略来避免长时间等待和资源浪费限流 包括客户端和服务端限流，确保系统在高负载下仍能稳定运行综合这些策略，我们可以构建出一个既高效又稳健的系统，它能够在各种网络条件和负载情况下保持高性能、高可用和易扩展。这些流量治理的手段不仅确保了服务的连续性和可靠性，还提高了用户体验和系统的整体效率。最后想说，高可用的本质就是面向失败设计。它基于一个现实且务实的前提：系统中的任何组件都有可能出现故障。因此，在架构设计时，我们不仅要接受故障的可能性，而且要学会拥抱故障。这意味着从一开始就将容错和恢复能力纳入设计考虑，通过增强系统的弹性、自适应性和恢复机制来应对可能出现的故障和变化。这种方法确保了在面对各种挑战时，系统能够保持持续的运行和服务质量。
导读本文详细介绍了 SRE 最应该监控的指标，通过引入 Google 4 Golden Signals、RED、USE 等方法论，并配合 Prometheus 配置说明，全面梳理讲解了各类关键指标的定义、计算方法和应用场景，帮助读者深入理解 SRE 监控的核心要素。这些知识是算是 SRE 的必备知识了。现代系统越来越复杂，包含众多组件，以有时不可预测的方式相互作用。为了有效监控这些系统，您需要结构化的方法来帮助您关注最重要的指标。四个黄金指标代表了一种以服务为中心的监控方法，能够直接让您了解用户体验和系统健康状况。这些信号由 Google 的 SRE 团队开发和记录，已成为现代可观测性实践的核心。四大黄金指标是：Latency（延迟）响应请求的时间Traffic（流量）接收到的总请求数Errors（错误）请求失败的数量Saturation（饱和度）服务“满”的程度这些指标共同提供了服务健康状况的全面视图。延迟和错误指标告诉您用户正在体验的服务质量。流量指标帮助您了解系统上的负载，并为其他指标提供上下文。饱和度指标表明您的服务接近其极限，并且可能会很快出现降级。让我们深入探讨这些信号，了解它们如何共同形成一个全面的监控策略。理解四大黄金指标1. Latency（延迟）延迟衡量处理请求所需的时间。这通常以毫秒或秒为单位进行测量，提供了从用户角度来看服务性能的见解。在衡量延迟时，区分成功的请求和失败的请求至关重要。失败的请求往往具有误导性的低延迟（例如，立即返回错误响应）或极高的延迟（如超时）。通过将这些测量值分开，可以更清楚地了解服务的实际性能。类似于 RED 方法论中的持续时间指标，延迟应该作为分布而不是平均值来衡量。这意味着跟踪百分位数，例如：中位数（第 50 百分位或 p50 ），这代表了典型的用户体验第 90 百分位数（ p90 ），显示最慢的 10% 请求的表现第 99 百分位数（ p99 ），突出显示您最慢的 1% 请求的体验这是如何使用 Prometheus 和 Node.js 实现延迟跟踪的示例：import { Histogram } from 'prom-client'; const requestLatency = new Histogram({ name: 'http_request_duration_seconds', help: 'HTTP request latency in seconds', labelNames: ['method', 'route', 'status_code'], buckets: [0.01, 0.05, 0.1, 0.5, 1, 2.5, 5, 10] }); function latencyMiddleware(req, res, next) { const start = Date.now(); res.on('finish', () => { const duration = (Date.now() - start) / 1000; requestLatency.observe( { method: req.method, route: req.route?.path || req.path, status_code: res.statusCode }, duration ); }); next(); }监控延迟有助于您设置合理的服务级别目标（SLO），并能快速检测性能降级，尤其是在只有部分请求受到影响的情况下。2. Traffic（流量）流量指标衡量系统所承受的需求。这通常以每秒请求数的形式表示，但具体单位会因服务类型而异。流量指标是您监控策略的基础元素。它们为解释其他指标提供了关键背景，并帮助您检测异常，例如使用量的意外激增或下降。对于不同的系统，流量可能被测量为：对于 Web 服务，每秒 HTTP 请求量对于数据库，每秒查询数对于队列系统，每秒处理的消息的数量支付系统的每秒交易量在实现流量指标时，你通常会使用一个随时间单调增加的计数器：import { Counter } from 'prom-client'; const requestCounter = new Counter({ name: 'http_requests_total', help: 'Total number of HTTP requests', labelNames: ['method', 'route'] }); function trafficMiddleware(req, res, next) { requestCounter.inc({ method: req.method, route: req.route?.path || req.path }); next(); }流量指标也有助于容量规划。通过分析历史流量模式，您可以预测未来的需求，并适当调整基础设施规模。通过流量指标，季节性模式、一周中的不同变化以及增长趋势都会变得清晰可见。3. Errors（错误）错误指标跟踪失败请求的速率。这包括任何没有按预期完成的请求，无论是由于显式错误、超时还是不正确的结果。错误指标帮助你从用户的角度理解服务的可靠性。它们通常以绝对速率（每秒错误数）和总请求数的百分比来跟踪。在实现错误指标时，明确你的服务中什么是“错误”非常重要。对于 Web 服务，HTTP 状态码在 4xx 和 5xx 范围内的通常被认为是错误，尽管你可能希望将客户端错误（4xx）和服务器错误（5xx）区分开来。这是如何使用 Prometheus 实现错误跟踪的方法：import { Counter } from 'prom-client'; const errorCounter = new Counter({ name: 'http_request_errors_total', help: 'Total number of HTTP request errors', labelNames: ['method', 'route', 'status_code', 'error_type'] }); function errorHandler(err, req, res, next) { errorCounter.inc({ method: req.method, route: req.route?.path || req.path, status_code: res.statusCode || 500, error_type: err.name || 'UnknownError' }); res.status(500).send('Something went wrong'); }错误指标应包含足够的细节以帮助进行故障排除。添加错误类型、受影响的端点或错误来源等维度可以为调试问题提供有价值的上下文。4. Saturation（饱和度）满载指标表示您的服务“满载”到何种程度。它们衡量系统当前使用的资源比例，并帮助预测系统何时可能开始经历性能降级。饱和度通常是这四个信号中最复杂的，因为它因系统类型和资源而异。对于一个 Web 服务，饱和度可能涉及：线程池利用率数据库连接池使用情况内存消耗CPU 利用率网络带宽使用情况满载指标的关键洞察是，大多数系统在达到 100%利用率之前就开始降级。通过识别并监控系统中最受限的资源，您可以在影响用户之前预测和防止性能问题。这是如何为连接池实现饱和度监控的一个示例：import { Gauge } from 'prom-client'; const connectionPoolGauge = new Gauge({ name: 'db_connection_pool_usage_ratio', help: 'Database connection pool usage ratio', labelNames: ['pool_name'] }); // Update the gauge regularly function updateConnectionPoolMetrics() { const poolSize = db.pool.max; const activeConnections = db.pool.used; connectionPoolGauge.set( { pool_name: 'main' }, activeConnections / poolSize ); } setInterval(updateConnectionPoolMetrics, 5000);对于许多系统，饱和度的最佳衡量标准是存在排队现象。当系统开始排队工作而不是立即处理时，这是饱和的明显迹象。队列长度或等待时间等指标可以提供即将出现的问题的早期预警。使用 Prometheus 实现四大黄金信号Prometheus 已经成为云原生环境中事实上的标准用于指标收集。其基于拉取的架构、强大的查询语言和广泛的生态系统使其成为实现“四大黄金信号”的优秀选择。1. 安装 Prometheus如果你是第一次使用 Prometheus，可以从设置一个基本实例开始。你可以将 Prometheus 作为 Docker 容器运行：docker run -p 9090:9090 -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus你的 prometheus.yml 配置文件应该包含你的服务的目标：global: scrape_interval: 15s scrape_configs: - job_name: 'your-service' static_configs: - targets: ['localhost:8080']2. 暴露指标端点在你的服务中，你需要暴露一个 Prometheus 可以抓取的端点。对于 Node.js 应用程序，可以使用 prom-client 库：import express from 'express'; import promClient from 'prom-client'; const app = express(); const register = promClient.register; // Expose metrics endpoint app.get('/metrics', async (req, res) => { res.set('Content-Type', register.contentType); res.end(await register.metrics()); }); app.listen(8080);四个黄金信号与其他监控框架的比较四个黄金指标并不是监控的唯一框架。它们可以与其他方法如 RED（速率、错误、持续时间）和 USE（利用率、饱和度、错误）相结合：RED 关注服务级别的指标，与四个黄金信号类似，但不明确包含饱和度。USE 侧重于资源级别的指标，并且非常关注基础设施健康。四个黄金信号处于这些方法之间，提供了一个平衡的观点，既包括服务性能（延迟、错误），也包括资源健康（饱和度）。流量指标则为所有三种框架提供了上下文。为了进行全面监控，请考虑使用这三个框架的所有元素：对于服务健康状况：跟踪延迟、流量和错误（来自四大黄金指标或 RED）。对于资源健康：跟踪饱和度（来自四大黄金指标）和详细的 USE 指标。对于业务影响：在这些技术指标上添加自定义业务指标（极为关键）。基于四大黄金信号设置警报四大黄金指标提供了警报的基础。以下是每个信号的一些警报策略：Latency（延迟）警报基于持续增加的高百分位延迟（例如 p99），因为这通常表示正在出现的问题。根据您的服务水平目标（SLO）设置阈值。示例 Prometheus 警报:- alert: HighLatency expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, route)) > 2 for: 5m labels: severity: warning annotations: summary: "High latency on {{ $labels.route }}" description: "P99 latency for {{ $labels.route }} is above 2 seconds"Traffic（流量）当流量模式出现显著偏差时发出警报。这包括意外下降（可能表明上游问题）和意外峰值（可能表明攻击或病毒内容）。示例 Prometheus 警报:- alert: LowTraffic expr: sum(rate(http_requests_total[5m])) < 10 for: 10m labels: severity: warning annotations: summary: "Unusually low traffic detected" description: "Request rate has fallen below 10 rps for 10 minutes"Errors（错误）当错误率或百分比超过 SLO 时发出警报。不同的服务可能对错误有不同的容忍度。示例 Prometheus 警报：- alert: HighErrorRate expr: sum(rate(http_request_errors_total[5m])) / sum(rate(http_requests_total[5m])) > 0.05 for: 5m labels: severity: critical annotations: summary: "High error rate detected" description: "Error rate is above 5% for 5 minutes"Saturation（饱和度）当资源接近其容量限制时发出警报。具体的阈值取决于系统的特性，但常见的做法是在达到 100%利用率之前发出警报。示例 Prometheus 警报：- alert: HighConnectionPoolSaturation expr: avg(db_connection_pool_usage_ratio) > 0.8 for: 5m labels: severity: warning annotations: summary: "Connection pool nearing capacity" description: "Database connection pool usage is above 80% for 5 minutes"最后思考四大黄金指标提供了一种强大的框架，用于监控和可观测性在现代服务中。通过实现这些指标，您可以获得对用户体验和系统健康状况的可见性，从而能够快速识别、诊断和解决问题。虽然具体实现细节可能因您的特定技术栈而异，但基本原则保持不变：监控延迟、流量、错误和饱和度，以获得服务健康状况的全面视图。通过将四大黄金指标与其他监控框架（如 RED 和 USE）结合使用
作为运维or测试从业者，运维or测试的产品或需求上线后，出现了Bug，作为运维or测试人员应该怎么办，是个值得考虑的问题。一、首先要做到应急响应，控制影响范围。成立应急小组：立即召集产品、开发、测试、运维等关键角色，明确分工（如开发定位问题、测试复现、产品评估影响）。评估严重性：根据Bug的影响范围（如用户无法支付 vs. 界面错位）和业务优先级（如核心功能受损）决定响应级别。严重Bug（导致系统崩溃/数据丢失）：优先考虑回滚至稳定版本，暂停新功能。一般Bug：保留现场日志，准备热修复或补丁。协助回滚或热修复如果Bug严重影响核心功能（如支付、登录），测试团队需配合运维快速验证回滚方案，确保旧版本功能正常。若采用热修复（Hotfix），需验证补丁包在真实环境中的兼容性（如不同设备、操作系统版本）。评估影响范围通过日志分析、用户反馈和监控工具（如Sentry、ELK）统计受影响用户比例，判断是否需要紧急响应。示例：电商App下单失败，测试需确认是否仅限某地区、某支付渠道或特定机型。二、问题定位与复现，还原现场精准归因复现Bug根据用户操作路径（如点击顺序、输入数据）复现问题，记录关键信息：环境：网络状态（4G/WiFi）、设备型号、系统版本。数据：触发Bug的输入值（如超长文本、特殊字符）。依赖条件：第三方服务状态（如短信网关、地图API）。工具辅助：使用Charles/Fiddler抓包，或Mock第三方服务模拟异常场景。协助开发定位根因提供测试环境的数据库快照、接口请求记录，帮助开发对比代码变更（如Git Diff）。针对偶发Bug，通过压力测试（JMeter/LoadRunner）验证是否因高并发导致资源竞争或死锁。三、修复验证多维度保障修复质量核心场景回归测试优先验证Bug修复后的功能，同时覆盖关联功能（例如修复支付失败后，需验证退款流程是否正常）。对复杂逻辑使用边界值测试（如金额为0、超大数值、负数）。自动化测试快速验证将复现步骤转化为自动化测试用例（如Selenium/Appium脚本），加入持续集成（CI）流程。示例：通过自动化脚本模拟用户从商品页到支付的完整流程，每日定时执行。灰度发布中的测试策略在灰度阶段（如10%用户）实时监控关键指标：功能层面：核心流程成功率（如下单、支付）。性能层面：接口响应时间、内存泄漏。通过A/B测试对比新旧版本，确保修复不引入新问题。四、复盘与优化从被动响应到主动预防根因分析运维测试团队需回答关键问题：为何测试阶段未发现？ → 测试用例缺失？环境差异？数据覆盖不全？上线流程是否有漏洞？ → 未做生产环境预检？灰度策略不完善？示例：某Bug因测试环境数据库版本与生产环境不一致导致，需标准化环境配置。更新测试用例库将线上Bug转化为新的测试用例，补充到回归测试套件中。针对复杂场景设计**“破坏性测试”**（Chaos Testing），例如：模拟第三方API超时/返回异常数据。强制中断进程，验证系统容错能力（如支付中途断网）。优化测试策略加强代码变更关联测试：通过代码覆盖率工具（如JaCoCo）检查新增代码是否被测试覆盖。分层测试策略：五、预防措施：构建更健壮的测试体系上线前风险控制新增需求需标注风险等级（如涉及第三方服务、核心链路改动为高风险），分配更多测试资源。进行生产环境预检：检查配置文件、数据库脚本、依赖服务版本是否一致。监控与预警配置自动化监控告警（如Prometheus+AlertManager），关注：业务指标：订单失败率、用户投诉激增。技术指标：接口500错误率、CPU/内存异常。建立测试-运维协作机制：将监控数据反馈至测试团队，优化测试场景。流程规范化制定《线上问题应急手册》，明确测试团队在故障处理中的职责（如复现、验证、回归）。推行质量门禁：代码合并需通过自动化测试+代码Review，关键需求需团队交叉测试。实施的基准原则速度与严谨兼顾：快速响应的同时，避免修复引入新问题。数据驱动决策：依赖日志、监控和用户反馈，而非直觉。闭环思维：从问题发现到预防措施形成闭环，持续提升质量体系。运维、测试团队不仅是“找Bug的人”，更是质量防线的主导者。通过每一次线上问题的处理，迭代测试策略，才能逐步逼近“零缺陷”目标。阅读后若有收获，不吝关注，分享等操作！
如果运维面试遇到水货面试官，怎么办？
一、为什么运维面试中会有"水货"面试官？企业面试流程的必然产物大厂日均处理上千份简历，需多层筛选：运维面试的特殊性传统运维：偏重"会操作就行"云原生时代：需要懂架构、开发、网络、安全等复合能力多数初级面试官自身技术栈尚未更新二、识别运维岗的"水货"面试官典型特征问题维度单一只问："K8s怎么排错？"（却不考察架构设计能力）死磕："Docker和虚拟机的区别能说5点吗？"脱离实际场景要求手写复杂的PromQL却不解释业务场景纠结"如何用Shell实现链表"（实际工作根本用不到）预设立场明显听到非大厂经历就皱眉用"我们阿里/腾讯是这样做的"打压候选人行为识别法环节专业面试官水货面试官自我介绍追问技术选型细节直接跳过进入八股文项目讨论关注架构决策过程只问用了哪些工具故障处理考察系统性思维纠结某条具体命令三、运维岗的针对性应对策略1. 基础版：安全过关遇到八股文轰炸： # 错误回答 "我不太记得TCP三次握手的细节..." # 正确话术 "实际运维中我们更关注TCP连接状态监控，比如通过`ss -ant`查看TIME_WAIT堆积情况， 曾经通过调整`tcp_tw_reuse`参数解决过生产环境端口耗尽问题"被问冷门命令：# 当被问"如何用awk统计日志5xx错误"时 # 追加实际案例： "我们团队还开发了自动化分析工具，当5xx超过阈值时会自动触发扩容"2. 进阶版：反客为主场景引导术：if 面试官只问监控: 主动展开："我们之前用Grafana实现过智能基线告警， 当CPU使用率偏离历史模式30%就触发预警" elif 面试官只问部署: 补充："除了CI/CD流水线，我们还建立了渐进式发布机制， 通过服务网格实现1%流量灰度测试"技术深度试探：当被问"如何优化K8s性能"时： [普通回答]："调整kubelet参数" [高手回答]："需要分场景： - 大规模集群：启用拓扑感知调度 - 混合云环境：配置节点亲和性 - 敏感型应用：设置CPU管理器策略"3. 终极版：价值展示构建技术人设：# 不是单纯说"用过Prometheus" # 而是： "我们改造了Prometheus的存储层，使TSDB压缩效率提升40%， 这是当时的性能对比图表（手机展示）"抛出技术钩子："最近在研究eBPF实现网络可观测性， 您觉得在Service Mesh场景下XDP和TC哪种方案更优？" （引导技术讨论）四、特殊场景处理遇到"工具人"型面试官对策：主动展示架构能力当对方只问"怎么部署Nginx"时： → 延伸："我们基于Nginx+Lua实现了动态流量染色， 这是架构图（提前准备pad展示）"遇到"压力测试"型面试官话术模板："这个问题可以从三个层面分析： 1. 理想场景下（理论） 2. 我们实际遇到的情况（经验） 3. 行业最新方案（趋势）"五、面试后的关键动作技术备忘录：## 今日技术收获 - 某厂在用__方案解决Ingress性能瓶颈 - 行业开始流行__监控模式反向评估表：| 维度 | 评分 | 备注 | | -------- | ---- | -------------------- | | 技术深度 | ⭐⭐ | 仍在使用传统运维思维 | | 行业视野 | ⭐ | 未提及云原生最佳实践 |六、运维面试的降维打击准备随身武器库：手机预存：架构设计图性能优化报告故障复盘文档总结：运维面试的本质是价值匹配游戏，遇到水货面试官时：识别特征 → 2. 调整策略 → 3. 展示比较优势记住：优秀的运维工程师永远供不应求，有时候不是你不够好，只是对方看不懂你的好。
聊一下当前主流的微服务框架背景：2015 年至今：广泛应用与演进事件：微服务架构在全球范围内得到广泛应用，并持续演进。背景：随着容器化技术（如 Docker）、编排工具（如 Kubernetes）和服务网格（如 Istio）的兴起，微服务架构的实施变得更加高效和可靠。意义：技术多样性：企业可以根据业务需求选择最适合的技术栈。自动化运维：通过 DevOps 和 CI/CD 工具，实现了微服务的自动化部署和监控。未来趋势：无服务器架构（Serverless）和边缘计算（Edge Computing）等新兴技术正在与微服务架构深度融合，推动其进一步演进。框架图服务拆分：将单体应用拆分为多个小型服务，每个服务负责一个特定的业务功能。独立部署：每个服务可以独立部署和更新，不影响其他服务。技术多样性：不同的服务可以使用不同的技术栈，选择最适合的技术实现。轻量级通信：服务之间通过轻量级的通信机制（如HTTP、gRPC）进行交互。自动化运维：通过自动化工具实现服务的部署、监控和扩展。微服务架构细分类型基于业务功能的微服务：根据业务功能划分服务，如用户服务、订单服务、支付服务等。基于数据域的微服务：根据数据域划分服务，如用户数据服务、产品数据服务等。基于技术栈的微服务：根据技术栈划分服务，如Java服务、Python服务等。微服务架构的优缺点优点灵活性：每个服务可以独立开发、部署和扩展，提高了系统的灵活性。可维护性：服务拆分后，代码库更小，更容易理解和维护。技术多样性：不同的服务可以使用不同的技术栈，选择最适合的技术实现。容错性：单个服务的故障不会影响整个系统的运行。缺点复杂性：服务数量增加后，系统的复杂性也随之增加，需要更多的管理和协调。分布式系统问题：微服务架构引入了分布式系统的复杂性，如网络延迟、数据一致性等问题。运维成本：需要更多的自动化工具和运维人员来管理大量的服务。java微服务框架图未来发展服务网格（Service Mesh）：服务网格将成为微服务架构的重要组成部分，提供更强大的服务发现、负载均衡、故障恢复等功能。无服务器架构（Serverless）：无服务器架构将进一步简化微服务的部署和运维，开发者只需关注业务逻辑的实现。自动化运维：随着DevOps和AIOps的发展，微服务架构的自动化运维将更加成熟，减少人工干预。边缘计算：微服务架构将逐渐向边缘计算扩展，实现更低的延迟和更高的可用性。