60 路并发出图率从 20% 到 90%:我踩的四个坑

前言

我写了一套串行排队调度模块,上线压测后,8 秒出图率纹丝不动——还是 20%。

这不是一篇"怎么排查出图慢"的技术教程,那种文章 CSDN 上够多了。这篇写的是我在排查过程中犯的四个错:为什么错、错在哪一层、下一次怎么避开

如果你也在做服务端性能优化,大概率会踩到同类坑——不是因为技术不够,是因为排查路径本身有陷阱。


背景

  • 基于 WVP + ZLMediaKit 自研国标视频平台
  • 60 路摄像头瞬时并发实时预览
  • 压测暴露:8 秒出图率 19%,累计 5596 次点播失败 2584 次,90% 是 RTP 收流超时
  • 服务器 CPU/内存/磁盘/网络全程无压力——瓶颈不在本机

全链路耗时拆开一看:WVP 下发 SIP 0~2s,下级应答占大头,TCP 通道建立约 4.5s,ZLMediaKit 首帧仅 1~2s。

延迟卡在下游设备/平台,不是在自家服务里。


坑一:没做对照实验就开始写代码

我做了什么

看到 60 路瞬时并发失败率 80%,第一反应是"并发太高冲击下游了"。于是写了一个 SerialRequestProcessor:有界阻塞队列 + 独立工作线程,把并行请求转成串行、按间隔分批下发。

30ms / 80ms / 100ms 三档间隔可调,队列上限控制,3s 超时直接返回失败。开发 + 自测 + 部署,完整走了一遍。

压测结果

下发间隔 8 秒出图率
30ms 10%~25%
80ms 0%~20%
100ms 18%~22%
TCP + 100ms 11%~21%

无论怎么调,出图率都在 20% 上下,跟不串行的基线一模一样。

我错在哪

我在用写代码替代做实验。

对照实验只需要 30 分钟:开一组保活、关一组保活,同时压测,结果就出来了。但我选了写代码——因为写代码更"可控"。代码写完、跑通、部署,每一环都在我自己掌控的范围内。做对照实验需要协调下游配合——可能要改设备配置、要找运维开权限、要跟其他团队对齐窗口——这些事比写代码更"麻烦"。

工程师的本能是往自己最舒适的方向走。 排查问题时的舒适区就是"我再写个工具/模块/中间件来解决它"。但瓶颈如果在你的控制范围之外,写再多代码都是绕路。

正确做法

在动手写任何优化代码之前,先回答一个问题:"同样的设备、同样的并发,什么条件下它其实是快的?"

这个问题只能用对照实验回答。一行代码都不需要写。


坑二:把"并发冲击"和"吞吐上限"搞混了

我当时的假设

"60 路瞬时并发 → 下游被冲垮 → 串行平滑下发 → 下游处理得过来"

实际发生的事

下游有个固定的单位时间吞吐上限。不管你是一秒内砸 60 个请求过去,还是用 6 秒均匀喂给它,60 个请求的总处理量不变。下游该处理的还是要处理,该排队的还是排队。

串行只是把拥堵往后平移了几秒,没有让拥堵消失。

打个比方:麦当劳厨房一小时最多出 200 个汉堡。你在前台同时来 60 个客人点单,和每隔 6 秒来一个客人——对厨房来说,汉堡还是要做 60 个,速度不会变快。串行调度就是让客人排成一列、间隔入场——看起来有序了,但最后那个人还是要等一样久。

怎么区分两种问题

  • 瞬时冲击:下游队列短、处理快,只是同一毫秒来了太多请求 → 串行/削峰有效
  • 吞吐上限:下游单位时间只能处理 N 个,总任务量 > N × 窗口 → 串行无效

如果你的链路耗时拆解里,单次处理耗时本身就很大(比如 5s+),那基本就是吞吐问题而不是冲击问题——单个任务就要这么久,来多少都是排队。


坑三:先信了直觉,再看数据——顺序反了

正确顺序

先看数据分布,再形成假设。不同失败类型的占比直接指向不同瓶颈位置:

失败分布特征 指向瓶颈位置
RTP 收流超时占大头 下游媒体处理
SIP INVITE 无响应超时占大头 下游信令处理或我方调度
失败均匀分布在各类 网络或系统资源

我的实际顺序

我的数据里 RTP 收流超时占 90.2%,SIP 超时仅 3.8%——这意味着信令是通的,媒体数据没来。瓶颈在下游推流侧,不在我方信令调度侧。

这个数据我很早就拿到了。但我没有用它来修正假设,而是继续沿着"并发冲击"的直觉往下走,直到写完了串行模块、压测打了脸才回头。

数据在你手上,但你选择性地看它。 这是一个需要刻意训练的肌肉——拿到数据的第一个动作不应该是"找证据支持我的假设",而是"这个数据如果否定了我的假设,它会怎么否"。


坑四:找到了解法,但没有追问"解法意味着什么"

找到解法

串行方案失败后,回到最基本的问题——同样设备、同样并发,"快"的场景和"慢"的场景差在哪?

两组对照压测:

组别 8 秒出图率
无保活(设备无活跃媒体会话) ~20%
有保活(设备维持常驻录像) 60%+
有保活 + 移除串行 90%

差的只有一个变量:设备侧是否已有活跃的媒体通道。

原理

关掉常驻录像,每次预览请求下游都要从头走:SIP 会话创建 → RTP 通道初始化 → 编码器冷启动 → 推流。冷启动的代价在并发场景下被放大。

开了常驻录像,会话和通道长期活跃,编码器始终在工作。点播请求过来,只需要在已有通道上快速切换——跳过了整个初始化链路。

我漏掉的追问

解法找到了,但我当时没继续往下想。真正有价值的问题在后面:

"这个解法暴露了什么?"

暴露了我方架构的一个默认假设:设备默认不休眠、通道即用即建。这个假设在低并发场景下成立,在并发场景下崩溃。不是下游性能有问题,是我方架构在设计之初就没有为"高并发冷启动"这个场景做过应对。

"竞品为什么没这个问题?"

排查后发现:竞品默认开启了设备常驻保活录像,媒体通道始终预热。不是他们技术更强,是他们的默认配置就避开了这个坑。这件事告诉我——工程问题不一定是技术差距,很多是默认配置的差距。 搞清楚竞品怎么配的,比闷头优化代码快十倍。


可复用的排查框架

从这个坑里爬出来之后,我整理了一个四次排查顺序——以后遇到类似问题,按这个来,不再绕路:

第 1 层:系统硬件(5 分钟排除)

top / iostat -x / sar -n DEV

任一持续 > 80% 才进入深入排查。否则直接下一层。

第 2 层:媒体服务

看首帧耗时、缓冲队列深度。本次首帧 1~2s,排除。

第 3 层:业务调度

看并发策略和重试机制。但这一步的核心动作不是"优化调度",是看失败分布:

  • RTP 超时多 → 直接跳到第 4 层,不要在调度层浪费时间
  • SIP 超时多 → 这层值得深入

第 4 层:下游级联(最常被忽略)

设备/平台并发承载 + 通道保活能力。本次真实瓶颈。

核心原则:失败分布决定排查方向。先看数据分布,再选排查路径。


总结

四个坑,根因是一个:用写代码替代了科学方法。

本质 下一次
没做对照实验就写代码 用编码舒适区逃避跨团队协作 先回答"它什么时候是快的"
混淆瞬时冲击和吞吐上限 没有区分峰值的两种成因 看单次处理耗时判断
先信直觉再看数据 证实偏差主导了排查路径 先看数据分布再形成假设
找到解法就停 没有追问解法暴露了什么 继续问"竞品为什么没这问题"

文中设备编号、项目地名、内网 IP 均已做脱敏处理。如果你遇到过类似的排查弯路,或者有不同的问题定位思路,欢迎留言讨论。

posted @ 2026-07-27 09:15  荣--  阅读(0)  评论(0)    收藏  举报