🎬 视频后台流水线:结算工厂(内存盘 5 图)

视频在外部厂商做完(轮询到 DONE)只是半成品:回厂还要走「算钱」(结算)与「转存 COS + 终态」(收尾)两条流水线。排队看队列深度与丢件率,产能看并发上限配置,兜底看收割器(90s 无动静主动推进,skip_claimed=互斥跳过属正常)。覆盖 m131 · m132 · m133 · m134 · m135 · 后端内存仪表盘「🎬 视频后台流水线」组(内存盘 5 图)。

🎬 结算工厂:视频做完只是半成品,回厂还要过算钱与转存两条流水线 视频轮询到 DONE 生成完工 → 进后台工厂 ① 结算流水线(算钱 settlement) 门口排队 settlement_ queue_depth 工位 ×N VIDEO_SETTLEMENT_ CONCURRENCY 结局 outcome 成功 / retrying / exhausted retrying = 再试一次 · exhausted = 试到没脾气放弃 retrying/exhausted 高 → 这单钱算不出来(收入问题) 队列满 → 丢投递(dropped),兜底扫描会补投,但延迟恶化 ② 收尾流水线(转存 finalization) 门口排队 finalization_ queue_depth 工位 ×N VIDEO_FINALIZER_ CONCURRENCY 售后两步 下载转存 COS → 打终态标记 成功视频的售后:转存到 COS,再打终态 retrying/exhausted 高 → 成品可能拿不到 队列满丢投递同上(兜底扫描补投) 收割器 reaper(无人认领处理员 · 服务端自主轮询) 用户关了页面 → 90s 无动静 → 服务端主动去推进结局(推进的是结局,不是重做视频) advanced 推进成功(结局落地) forced_timeout 超 24h 强制收敛 skip_claimed 别的节点刚碰过:互斥跳过 = 正常 advance_error 推进失败 → 查日志 stale_tasks 待收割持续走高 = 无人认领的积压在变多(对照队列深度) 队列 × 工位对照(调参方向) 队在涨、工位没开满 → 调大并发 调大还不行:收尾侧看转存链路 结算侧先看锁冲突面板(1205 / 1213) 事故现场 · 钱算不出来 结算 exhausted 持续 > 0:这批单子收不上钱 → 收入完整性问题(对照计费归因 unattributed) 收尾 exhausted:用户的成品可能拿不到 读图顺序:队列(积压?丢件?)→ 工位(开满没)→ 结局(retrying/exhausted 卡在哪)→ 收割器(谁在兜底)。

💡 一句话理解

把视频做完想成「客人点的外卖做好了」:生成完成(轮询到 DONE)只是出了制作间,后面还有一整座后台工厂。工厂里两条流水线:结算(算这单用了多少、该收多少钱)和收尾(把成品下载转存到 COS、打上终态标记)。产能怎么读?队列深度=门口排了多少单,丢件率=队伍满到往外丢了多少次(丢了不等于没了——有兜底扫描补投,但延迟会恶化),并发上限=工厂开了几个工位(配置值),最后还有一位收割器:用户关了页面、任务 90s 无动静时,由服务端主动推进它的结局。

🧩 费曼拆解 讲给完全没接触过的小白

很多人以为「视频生成完成」就是终点。其实在系统里,轮询到 DONE 只是半成品进厂:钱还没算(用户用了多少秒的 GPU、按哪个价目)、文件还躺在厂商那边(要下载下来转存到我们自己的 COS、打上终态,用户才能稳定拿到)。这两件事各有各的流水线——结算和收尾,各有一条队、各开若干工位、各自记「结局」。

流水线的健康怎么读?三看。一看队:队列深度(Gauge)直接读,持续上涨=消化不足;队伍满了系统会「丢投递」——别慌,任务不丢,有兜底扫描会补投,但延迟会恶化,所以丢件率是比队列深度更严重的信号。二看结局:每个活干完会记 outcome——成功、retrying(再试一次)、exhausted(试到没脾气放弃)。retrying/exhausted 高不是「有点慢」,是「卡住了」:结算卡住=定价或用量数据异常,这单钱算不出来(收入问题);收尾卡住=转存/转码链路异常,用户的成品可能拿不到。三看工位:并发上限是配置值(Gauge),拿它和队列对照就知道产能瓶颈在哪。

最后是收割器——工厂的「无人认领处理员」。正常情况下前端用户会轮询自己的视频;但用户可能做完就关页面,任务从此没人看着。收割器盯的就是这种:一个任务 90s 无动静,服务端主动去推进它的结局(是推进,不是重做)。它也有自己的结局字典:advanced(推进成功)、forced_timeout(超 24h 强制收敛)、skip_claimed(别的节点刚碰过,互斥跳过——这是互斥机制在正常工作,不是错误)、advance_error(推进失败,要查日志)。待收割的 stale_tasks 持续走高,就是「没人看着的积压」在变多。

类比里的东西系统里对应的东西
半成品进厂视频轮询到 DONE,进入后台结算/收尾流程
算钱流水线siliang_video_settlement_*:队列、工位、结局;retrying/exhausted 高=定价或用量数据异常
转存流水线siliang_video_finalization_*:下载转存 COS + 打终态;卡住=成品可能拿不到
门口排队 / 丢件queue_depth(Gauge 直读)+ dropped 速率;丢投递≠丢任务,兜底扫描补投,但延迟恶化
工位数VIDEO_FINALIZER_CONCURRENCY / VIDEO_SETTLEMENT_CONCURRENCY 配置 Gauge
无人认领处理员收割器 reaper:90s 无动静主动推进结局;skip_claimed=互斥跳过属正常;stale 持续走高=积压

🛠 动手验证(4 步,在 Grafana 里亲手做一遍)

  1. 打开 m133 队列深度——确认收尾/结算两条队都在低位起伏;找不到「持续上涨」的线,说明工厂消化正常。
  2. 对照 m134 并发上限——念出两个配置值;再回头想:如果队列涨而工位没开满,调参方向就是加并发。
  3. 打开 m131/m132 结局分布——确认成功占绝对主流,retrying 偶发、exhausted 稳定为 0;任何 exhausted 都值得点开看一眼。
  4. 打开 m135 收割器——找到 skip_claimed 那条线:它有读数是正常的(互斥跳过);真正要盯的是 stale_tasks 是否持续走高。

🧠 必知必会 看懂本课全部面板的地基

两条流水线的分工
结算=算钱(DONE 之后计价),收尾=转存 COS+打终态(用户拿成品的保障)。指标前缀 settlement / finalization 别混。
# 两套前缀,两个工厂车间
siliang_video_settlement_outcome_total    # 算钱的结局
siliang_video_finalization_outcome_total  # 转存的结局
outcome 三态
每个活干完记一次结局(Counter → rate by outcome):成功 / retrying(再试一次)/ exhausted(试到没脾气放弃)。
# 读法
sum by (outcome) (rate(siliang_video_settlement_outcome_total[5m]))
# → retrying 偶发可忍 · exhausted 非零就是事故信号
两种「卡住」两种后果
结算卡住=定价或用量数据异常,这单钱算不出来(收入问题);收尾卡住=转存/转码链路异常,成品可能拿不到(体验问题)。
# 面板原话浓缩
# 结局卡在结算 → 收入问题(对照计费归因 unattributed)
# 结局卡在收尾 → 用户的成品可能拿不到
队列深度与丢件率
队列深度是 Gauge 直接读;持续上涨=消化不足。队列满开始丢投递时任务不丢(兜底扫描补投),但延迟恶化——丢件率比深度更严重。
# 两条队 + 丢件(主图谱口径)
sum(siliang_video_finalization_queue_depth)
  + sum(siliang_video_settlement_queue_depth)
  + rate(…_dropped_total[5m])   # 丢件率 > 0 = 满到往外丢了
并发上限是配置 Gauge
工位数 = VIDEO_FINALIZER_CONCURRENCY / VIDEO_SETTLEMENT_CONCURRENCY 的当前值。它回答「产能开了多少」,和队列对照决定调参方向。
# 工位配置值(大数字面板)
max(siliang_video_finalization_worker_concurrency)
max(siliang_video_settlement_worker_concurrency)
收割器与 skip_claimed
用户关页面后 90s 无动静,服务端主动推进结局。skip_claimed=别的节点刚碰过、互斥跳过——正常机制,不是错误;advance_error 才是要查的。
# 收割器结局字典
# advanced=推进成功 · forced_timeout=超 24h 强制收敛
# skip_claimed=互斥跳过(正常) · advance_error=推进失败
stale_tasks 积压表
待收割任务数持续走高 = 没人看着的积压在变多。它是 Gauge,取 max 抹抓取抖动。
# 主图谱口径:收割器速率 + 待收割存量
sum by (outcome) (rate(siliang_video_reaper_outcome_total[5m]))
  + max(siliang_video_reaper_stale_tasks)
结算失败与计费的关系
结算 exhausted 的单子=钱算不出来=少收钱。排查速查表把它和计费归因 unattributed 列成同一条「账单对不上」路径。
# 速查表路径(账单/消耗对不上)
# c27 归因失败 unattributed → c28 查询耗时 → m131 结算 retrying/exhausted

📋 逐面板精讲 5 张图一张不落

🟠 视频后台结算结果

它是什么:视频轮询到 DONE 之后,后台工厂「算钱」环节的结局分布(成功 / retrying / exhausted),按 outcome 拆线。算钱这步要拿着定价规则和用量数据对账,对不上就会重试——重试到没脾气就是 exhausted。

回答什么问题:每一单的钱都算出来了吗?卡住的那些卡在什么环节(定价规则?用量数据?)?

✅ 健康长啥样:成功占绝对主流;retrying 偶发脉冲后消失;exhausted 长期为 0。

🚨 危险长啥样:retrying/exhausted 高 = 定价或用量数据异常——这单钱算不出来(收入问题)。动作:按 outcome 分线确认 exhausted 是否非零,翻结算日志定位是定价规则还是用量数据的问题,并对照计费归因 unattributed 估算少收了多少钱。

# 主图谱口径:结算结局速率
sum by (outcome) (rate(siliang_video_settlement_outcome_total[5m]))

联动:主图谱 · 视频后台结算结果 ↗ · 相关课程:计费归因与灵感检索 · rate():算速度

🟠 视频后台收尾结果

它是什么:成功视频的「售后」结局:下载转存到 COS、再打终态标记——两步走完,用户才能稳定拿到成品。结局同样记成功 / retrying / exhausted。

回答什么问题:做完的视频有没有都妥善「入库」?用户现在拿不到成品,是不是收尾卡住了?

✅ 健康长啥样:成功主流;偶发 retrying(COS 抖一下)很快收敛;exhausted 为 0。

🚨 危险长啥样:retrying/exhausted 高 = 转存/转码链路异常——用户的成品可能拿不到。动作:先看 COS 侧是否抖动/限流(对照容器网络收发面板),再查转存任务日志;此问题直接触发用户投诉,优先级高于结算。

# 主图谱口径:收尾结局速率
sum by (outcome) (rate(siliang_video_finalization_outcome_total[5m]))

联动:主图谱 · 视频后台收尾结果 ↗ · 相关课程:容器网络收发(COS 流量) · outcome 通用读法

🟠 视频后台队列深度(积压)

它是什么:工厂门口的实时排队数:收尾 / 结算两条队各自的深度(Gauge 直读),外加丢件率(队列满到往外丢投递的速率)。它是整条流水线「消化能力」的体温计。

回答什么问题:工厂消化得动吗?积压在变严重还是缓解?有没有已经严重到「往外丢件」?

✅ 健康长啥样:两条队深度低位起伏、峰谷与业务节奏同步;丢件率恒为 0。

🚨 危险长啥样:深度持续上涨 = 消化不足;丢弃率 > 0 = 已经满到往外丢了——任务不丢(有兜底扫描补投)但延迟恶化。动作:立刻对照 m134 工位配置决定调参方向,并查两条流水线的 retrying 卡点。

# 主图谱口径:两条队深度 + 丢件率
sum(siliang_video_finalization_queue_depth / siliang_video_settlement_queue_depth)
  + rate(siliang_video_finalization_queue_dropped_total[5m])  # dropped 指标名以仪表盘口径卡为准

联动:主图谱 · 视频后台队列深度 ↗ · 相关课程:worker 体检单(消化端产能) · Counter vs Gauge(深度是 Gauge)

🟠 视频后台并发上限(配置)

它是什么:工厂开了几个工位——VIDEO_FINALIZER_CONCURRENCY 与 VIDEO_SETTLEMENT_CONCURRENCY 的当前值,以配置 Gauge 形式展示。它不是「正在干几个活」,而是「允许同时干几个活」。

回答什么问题:积压是产能没开够,还是链路本身卡住了?调参该往哪个方向调?

✅ 健康长啥样:工位配置值稳定;与队列深度对照,队列不积压即说明当前产能匹配。

🚨 危险长啥样:队排在涨而工位没开满 = 产能闲置,动作是调大并发;调大还不行 = 瓶颈在链路:收尾侧看转存链路(COS),结算侧先看锁冲突面板(1205/1213)——按面板原话的调参路径走,别反复拧工位数。

# 主图谱口径:两个工位配置值
max(siliang_video_finalization_worker_concurrency / siliang_video_settlement_worker_concurrency)

联动:主图谱 · 视频后台并发上限 ↗ · 相关课程:数据库锁冲突(结算侧瓶颈) · 容器 CPU(并发调大后先看够不够核)

🟠 视频收割器(服务端自主轮询)

它是什么:「无人认领处理员」:用户关了页面、没人轮询的视频任务(90s 无动静),由服务端主动去推进结局。面板画它的结局速率(advanced / forced_timeout / skip_claimed / advance_error)加上待收割存量 stale_tasks。

回答什么问题:有多少「没人看着」的任务在被兜底推进?推进机制本身健康吗(互斥正常?失败多不多)?

✅ 健康长啥样:advanced 是主流;skip_claimed 有读数属正常(别的节点刚碰过,互斥跳过——互斥机制在正常工作);stale_tasks 低位平稳。

🚨 危险长啥样:stale 待收割持续走高 = 没人看着的积压任务在变多(用户端异常或投递堆积);advance_error 高 = 推进失败,翻日志。动作:stale 抬升先对照 m133 队列深度分辨是「进得多」还是「推不动」,再对应扩容或修推进链路。

# 主图谱口径:收割器结局速率 + 待收割存量
sum by (outcome) (rate(siliang_video_reaper_outcome_total[5m]))
  + max(siliang_video_reaper_stale_tasks)

联动:主图谱 · 视频收割器 ↗ · 相关课程:后台任务租约与收尸(同款兜底思想) · outcome 分布通用心法

🏭 生产实战 real world

场景 1 · 「视频卡住不出片」的标准排查路径

用户反馈视频做完了一直转圈。排查速查表的既定路径:队列 → 工位 → 结局 → 收割器,四步定位。

# 第 1 步:两条队积压吗?丢件了吗?
sum(siliang_video_finalization_queue_depth) + sum(siliang_video_settlement_queue_depth)
rate(…_dropped_total[5m])   # dropped 指标名以仪表盘口径卡为准

# 第 2 步:工位开满了吗?(没开满=调大并发;开满了=链路卡)
max(siliang_video_finalization_worker_concurrency)
max(siliang_video_settlement_worker_concurrency)

# 第 3 步:卡在哪条流水线?
sum by (outcome) (rate(siliang_video_finalization_outcome_total[5m]))
sum by (outcome) (rate(siliang_video_settlement_outcome_total[5m]))

# 第 4 步:兜底推没推进?(stale 高=没人看着的积压在变多)
max(siliang_video_reaper_stale_tasks)

判读:队列不高+结局 retrying = 单笔任务卡链路(看转存/定价日志);队列高+工位没满 = 调并发;全链路 exhausted = 事故级,按场景 2/3 深挖。

场景 2 · 结算 exhausted 告警:这单钱算不出来

exhausted 意味着试到没脾气放弃——这单的账永远算不出来了,直接少收钱。值得一条独立的告警。

# 结算放弃 = 收入问题,非零即报
- alert: VideoSettlementExhausted
  expr: sum(rate(siliang_video_settlement_outcome_total{outcome="exhausted"}[15m])) > 0
  labels: {severity: critical}
  annotations: {summary: "视频结算 exhausted:有单子钱算不出来(收入问题)"}

# retrying 抬升 = 即将变成 exhausted 的预警(示例阈值,按基线调)
- alert: VideoSettlementRetryingHigh
  expr: sum(rate(siliang_video_settlement_outcome_total{outcome="retrying"}[15m])) > 0.1
  labels: {severity: warning}
  annotations: {summary: "视频结算重试增多:定价或用量数据可能异常"}

收尾侧同理把 finalization 前缀换上——它的后果是「成品拿不到」,同样 critical。

场景 3 · 队列积压:先判「产能不足」还是「链路卡死」

同样是队列上涨,处理方向完全相反:产能不足要调大工位,链路卡死调工位没用。

# 判据:队列在涨时,工位有没有开满?
# A. 没开满 → 产能闲置 → 调大 VIDEO_*_CONCURRENCY
max(siliang_video_finalization_worker_concurrency)

# B. 已开满 → 链路卡 → 收尾看转存链路(COS),结算先看锁冲突
sum(rate(siliang_db_lock_errors_total[5m])) by (operation, code)
# → 1205/1213 出现:结算工位全耗在等锁上,调工位无效,先修锁

判读:调大并发后队列掉头向下 = 产能问题解决;不动 = 转回链路排查,别继续拧参数。

场景 4 · 收割器健康巡检:skip_claimed 别误伤

巡检收割器时的关键区分:skip_claimed 有读数是互斥在正常工作;要盯的是 stale 与 advance_error。

# 结局速率按 outcome 拆开
sum by (outcome) (rate(siliang_video_reaper_outcome_total[5m]))
# → advanced 主流 · skip_claimed 有读数=互斥正常 · advance_error 才要查

# 存量:待收割持续走高 = 无人认领的积压在变多
max_over_time(siliang_video_reaper_stale_tasks[10m])
# 抬升时对照队列深度:进得多(扩容)还是推不动(修推进链路)
sum(siliang_video_finalization_queue_depth)

场景 5 · 账单对不上:把结算失败接进计费排查

排查速查表把「账单/消耗对不上」的路修到了这里:归因失败与结算失败都要看,它们是「少收钱」的两个源头。

# 源头 1:计费归因失败(找不到主人的消费)
rate(siliang_workspace_consumption_unattributed_total[5m])

# 源头 2:视频结算卡住(这单钱算不出来)
sum by (outcome) (rate(siliang_video_settlement_outcome_total{outcome=~"retrying|exhausted"}[15m]))
# → 两个数都该长期贴地;非零的时段就是少收钱的窗口

判读:对账差异 ≈ 两个源头各自的时间窗积分——先补账,再修各自链路。

⚠️ 常见坑 pitfalls

坑 1 · 把 skip_claimed 当错误处理 — 看到 skip_claimed 曲线非零就报警。原因:它是「别的节点刚碰过,互斥跳过」——分布式互斥机制在正常工作,面板原话明确「正常」。正解:要盯的是 stale_tasks 与 advance_error,skip_claimed 只用于确认互斥健在。
# 错: outcome="skip_claimed" 非零即报      # 天天误报
# 对: 盯 stale_tasks 走高 + advance_error 非零
坑 2 · 丢投递=丢任务 — 看到丢件率 > 0 就宣布「用户的视频丢了」。原因:队列满丢的是「投递」不是「任务」,兜底扫描会补投。正解:丢件率 > 0 的正确读法是「延迟开始恶化」,把它当积压升级信号。
# 错: dropped > 0 → "数据丢了,全量重跑"
# 对: dropped > 0 → 队列满的信号:扩产能/查卡点,任务有兜底
坑 3 · 积压只会调工位 — 队列涨就把 CONCURRENCY 翻倍,越调越糟。原因:链路卡死时(转存慢/结算等锁)工位再大也没用,反而放大资源挤占。正解:按面板路径:队涨+工位没满才调并发;调大无效,收尾看转存、结算看锁冲突。
# 错: 队列涨 → CONCURRENCY ×2 → CPU 打满 → 更多任务卡住
# 对: 对照工位配置 → 没满先调;满了查链路(COS / 1205-1213)
坑 4 · 把 retrying 当最终失败 — 看到 retrying 就按事故上报。原因:retrying=「再试一次」,还有救;exhausted 才是「试到没脾气放弃」。正解:retrying 抬升当预警盯守,exhausted 非零才是事故。
# 错: retrying 线一动就 pager
# 对: retrying 持续抬升 → 预警 · exhausted > 0 → critical
坑 5 · 结算/收尾指标混用 — 想查「钱算不出来」却打开了 finalization 面板。原因:两套前缀两条流水线,后果完全不同(收入问题 vs 体验问题)。正解:settlement=算钱,finalization=转存 COS+终态;先对后果再选前缀。
# 错: "对不上账" → 查 finalization_outcome_total
# 对: "对不上账" → 查 settlement(retrying/exhausted)+ unattributed
坑 6 · 把收割器当重做机器 — 担心收割器会把视频重做一遍、重复扣钱。原因:收割器「推进的是结局」,不是重做生成——用户关页面后它替前端把状态推进到终态。正解:按面板口径理解:90s 无动静 → 服务端主动推进结局(advanced/forced_timeout)。
# 错: "reaper 会不会重复生成、重复计费?"   # 不会:它只推进结局
# 对: 90s 无动静 → 推进结局;超 24h 才 forced_timeout 强制收敛
坑 7 · stale_tasks 直读不抹抖 — 抓取瞬间抖动被当成「积压暴涨」。原因:它是 Gauge,单点读数有抓取噪声。正解:用 max_over_time 抹窗口内的最大值再判趋势。
# 错: siliang_video_reaper_stale_tasks 单点报警
# 对: max_over_time(siliang_video_reaper_stale_tasks[10m]) 持续走高才报

🎓 费曼自测 合上书,能讲出来才算懂

Q1 · 视频轮询到 DONE 之后,系统还要做哪两件事?各自失败意味着什么?

参考答案:结算(拿着定价规则和用量数据算这单多少钱)和收尾(下载转存到 COS、打终态标记,用户才能稳定拿成品)。结算失败(retrying/exhausted)意味着这单钱算不出来——收入问题;收尾失败意味着用户的成品可能拿不到——体验问题。两条流水线各有自己的队列、工位和结局计数。

Q2 · 队列满了开始丢投递,任务会丢吗?这个信号该怎么读?

参考答案:不会丢。丢的只是「投递」,有兜底扫描会补投。但丢件率 > 0 说明队列已经满到往外丢了,延迟开始恶化——所以它比队列深度上涨更严重,正确的读法是「消化不足已升级」,动作是对照工位配置调产能、查两条流水线的卡点,而不是宣布数据丢失。

Q3 · 收割器是什么?skip_claimed 为什么不是错误?

参考答案:收割器是「无人认领处理员」——用户关了页面后,视频任务 90s 无动静,服务端主动去推进它的结局(只推进结局,不是重做视频)。skip_claimed 表示别的节点刚碰过这个任务、本节点按互斥规则跳过——这说明互斥机制在正常工作,防止多节点重复推进,所以它有读数是健康的表现;真正要警惕的是 stale_tasks 持续走高和 advance_error。

Q4 · 队列深度持续上涨,你的调参决策树是什么?

参考答案:第一步对照并发上限面板:队列在涨而工位没开满 → 产能闲置,调大 VIDEO_*_CONCURRENCY。第二步调大后队列还涨 → 瓶颈在链路:收尾侧看转存链路(COS 抖动/限流),结算侧先看锁冲突面板(1205 等太久 / 1213 死锁)。原则:先确认「工位没开满」才动配置,否则治的是症状不是病根。

← 上一课:👷 RAG / Review Worker 体检单 📚 课程目录 下一课:🟢 biz 业务后端:核心盘精简镜像 →