怎么用 这是格式与深度的参考,不是让你照抄的模板。数字全部是示例,直接抄进简历一问就穿——必须换成你自己的真实数据,而且要能说清是怎么测出来的。下面每个模块都有「数字是怎么测的」和「面试追问」两段,写不出那两段的模块就不要往简历上写

面试题库(电商 / 短视频 / 社区 / 本地生活 / 旅游 / 企业服务 多业务场景)

项目背景设定 短视频平台的直播服务端,Java + Spring Cloud + 媒体服务器 + CDN。同时在播房间数百到数千,头部房间观众数万。核心链路是开播推流 → 转码分发 → 观众拉流 → 弹幕礼物互动 → 下播生成回放
为什么选这三个模块 直播和点播(前面的短视频内容平台)看起来相近,实际差异很大:点播的内容是已经存在的文件,直播的内容正在产生,所以「断流」「时延」「实时审核」这些点播压根没有的问题成了核心;弹幕的扇出是 N 平方级的(一条弹幕要推给房间里所有人),和短视频的 Feed 完全不同;礼物涉及真实资金,可靠性要求和点赞不是一个量级。

模块一:直播房间与推拉流调度

  1. 直播房间与推拉流调度(推流鉴权 + 断流容忍 + 按热度分档转码)★★★
    简历这样写 直播房间与推拉流调度(Spring Boot + 媒体服务器回调鉴权 + 房间状态机 + 断流容忍窗口 + 按热度分档转码):推流地址采用房间绑定的时效签名并由媒体服务器回调业务侧二次鉴权防盗推;主播断流不立即结束直播,先进入容忍窗口(保留房间与观众连接),超时未恢复才结束并生成回放;转码按观众规模动态分档(开播只出原画,观众数超阈值才开多码率),并对预估有量的房间预热 CDN 首片。开播到首个观众可见首帧 P95 约 1.8s,主播网络抖动导致的直播误结束改造后未再出现,冷门房间不做多档转码使转码资源占用明显下降
    展开完整拆解
    为什么要这么设计

    直播和点播最根本的差异是:内容正在产生,而且产生的过程随时会中断。这带来一串点播完全没有的问题。

    第一版是照着「能推能拉就行」做的,四个问题很快暴露。

    一是推流地址被盗推。推流地址是固定的字符串,抓包或者主播不小心泄露之后,别人拿这个地址往你的房间推流,观众看到的是完全无关的内容。而且我们无法区分谁在推——地址对了就收。这在内容安全上是很严重的问题。

    二是主播断流就直接结束直播。主播过隧道、进电梯、切换 WiFi,几秒钟的断流让我们判定「主播下播了」,房间关闭、几千观众被踢出去,主播恢复后要重新开播、观众全散了。主播的反馈是「你们的直播老是自己断」。

    三是所有房间都开多档转码,成本失控。同时在播几千个房间,绝大多数只有几个观众甚至没人看。给这些房间也出三档清晰度,转码集群的资源全浪费在没人看的内容上。

    四是房间状态在并发下错乱。「主播主动下播」和「断流超时自动结束」可能同时发生,两个流程都去改房间状态,结果出现了「已结束的房间又变回直播中」这种状态,回放生成也跑了两次。

    所以四个设计:推流地址签名加媒体服务器回调二次鉴权断流进容忍窗口而不是立即结束转码按热度分档房间状态用状态机条件更新

    整体链路
    开播(推流鉴权是两层,地址签名不够) │ ├─ 主播点开播 → 业务侧校验资质(实名、无封禁、有直播权限) │ ├─ 生成推流地址:房间 ID + 过期时刻 + 签名 │ 签名 = HMAC(房间ID + 过期时刻 + 主播ID, 密钥) │ 地址短时效(几十分钟),过期前主播端自动换新 │ ├─ 房间状态置为「准备中」 │ ├─ 主播端用该地址推流到媒体服务器 │ ├─ 媒体服务器收到推流请求 → 回调业务侧鉴权(关键的第二层) │ 业务侧校验:签名有效 / 未过期 / 房间状态是准备中或直播中 │ / 该房间当前没有其他推流在进行 │ → 返回允许或拒绝 │ 为什么需要这一层:地址可能泄露,而回调时业务侧掌握 │ 实时状态(是否已有人在推、主播是否被临时封禁) │ ├─ 鉴权通过 → 媒体服务器接流 → 房间状态改「直播中」 │ └─ 转码与分发(见下) 转码按热度分档(不给没人看的房间浪费资源) │ ├─ 开播时只出原画(不转码,直接转封装分发) │ 绝大多数房间就停在这一档,成本接近于零 │ ├─ 观众数超过阈值 → 动态开启多码率转码 │ 理由:观众多了必然有网络差的用户需要低清晰度 │ 观众少时那几个人凑合看原画影响面很小 │ ├─ 头部房间(按主播粉丝量预判)→ 开播即开多档 │ 避免开播瞬间涌入的观众都卡在原画上 │ └─ 观众数回落 → 延迟关闭多档(不要频繁开关) 断流容忍(这是主播体验的关键) │ ├─ 媒体服务器检测到推流中断 → 回调业务侧 │ ├─ 业务侧不立即结束,房间状态改为「异常中断」 │ 房间保留、观众连接保留、弹幕通道保留 │ 观众端展示「主播网络异常,正在重连」而不是直接退出 │ ├─ 容忍窗口内主播恢复推流 → 状态回到「直播中」 │ 观众几乎无感知(只是画面卡了一段) │ ├─ 窗口超时未恢复 → 走正常结束流程 │ └─ 窗口长度的取舍 太短(几秒)→ 抖动就断,等于没有容忍 太长(几分钟)→ 主播真下播了观众还在等,体验也差 分钟级是合理量级,并且要在观众端显示倒计时或提示 下播收尾(多个动作,必须幂等且有序) │ ├─ 房间状态条件更新为「已结束」(where status in 直播中/异常中断) │ 影响行数 0 → 说明已被别的流程结束,直接返回 │ ├─ 通知观众端房间已结束 ├─ 结算本场礼物流水(见模块二) ├─ 生成回放(分段录制的文件合并转码,见模块三) ├─ 落本场数据(时长、峰值观众、礼物收入) └─ 释放媒体服务器与转码资源
    分步拆解
    1. 推流地址签名是第一层,但不够。签名能防「随便造一个地址」,但防不住地址泄露后的盗推(签名还在有效期内)。所以必须有第二层。
    2. 媒体服务器接流前回调业务侧二次鉴权,这是关键的一层。回调时业务侧掌握实时状态:这个房间当前有没有其他推流在进行、主播是不是刚被封禁、房间是不是已经结束。这些是签名本身表达不了的信息。
    3. 同一房间只允许一路推流。回调鉴权时检查「该房间是否已有推流」,有就拒绝新的。这一条直接挡住了盗推——即使地址泄露,主播自己在推的时候别人推不进来。
    4. 推流地址要短时效并支持主播端自动换新。时效越短泄露的价值越低。但不能短到影响长时间直播——主播端要在过期前自动申请新地址并平滑切换。
    5. 房间状态必须用状态机条件更新。「主播主动下播」和「断流超时自动结束」会并发,条件更新保证只有一个生效,另一个影响行数为 0 直接返回。否则会出现状态回退和回放生成两次。
    6. 断流不能立即结束,必须有容忍窗口。房间保留、观众连接保留、弹幕通道保留,观众端展示「主播网络异常,正在重连」。这是主播端体验最重要的一处设计——它把「一次抖动毁掉整场直播」变成了「画面卡了一下」。
    7. 容忍窗口的长度是明确的取舍。太短等于没有容忍,太长会让观众在主播真下播后干等。分钟级是合理量级,并且要在观众端给出提示或倒计时,让观众知道在等什么。
    8. 转码按热度分档,开播只出原画。同时在播几千个房间,绝大多数没人看。观众数超阈值才开多码率——因为观众多了必然有网络差的用户需要低清晰度,观众少时那几个人凑合看原画影响面很小。
    9. 头部房间要开播即开多档。按主播粉丝量预判,避免开播瞬间涌入的大量观众都卡在原画上。这是「按预期热度而不是当前热度」做决策。
    10. 观众数回落时要延迟关闭多档,不能频繁开关。转码任务的启停有开销,观众数在阈值附近波动会导致反复开关。加一个回落的延迟和滞后阈值。
    11. 下播收尾的多个动作必须幂等。结算礼物、生成回放、落数据,每一步都可能因为重试而执行两次。用场次 ID 做幂等键——尤其是礼物结算,重复执行就是资金问题。
    关键决策与取舍

    自建媒体服务器还是用云直播。云直播(厂商提供推流拉流转码 CDN 全套)接入快、运维省,但成本按流量计费,量大之后很贵,而且定制受限(比如要在流里插审核标记、要自定义转码参数)。自建可控但要维护媒体服务器集群、处理协议细节、自己做监控。我们的选择是混合:主要流量走自建,突发和边缘地区溢出到云服务。判断依据是「流量规模」和「定制需求强度」——如果只是几十个房间,直接用云服务,自建是过度设计。

    直播延迟和流畅度是直接冲突的。低延迟协议(WebRTC 之类)延迟能到几百毫秒,但抗弱网能力差、CDN 支持不如 HTTP 系协议成熟、成本高;HTTP 系协议(HLS 之类)延迟几秒到十几秒,但极其稳定且 CDN 分发成本低。我们的做法是按场景分:普通直播(观众只看不互动)走 HTTP 系协议,接受几秒延迟换稳定和低成本;需要强互动的场景(连麦、答题、抢购)才用低延迟协议「按互动强度选协议」这个判断比一味追求低延迟正确。

    踩过的坑:断流容忍窗口内主播重连,但推流被自己的旧连接挡住了。主播网络抖动,旧的推流连接在媒体服务器侧还没被清理(TCP 层还没超时),主播重连时回调鉴权发现「该房间已有推流」,于是拒绝了他。主播反复重连都失败,最后只能等旧连接超时。修法是回调鉴权时如果发现已有推流,要判断那路推流是否还在实际收到数据(有没有心跳、最近有没有数据帧),僵死的旧连接要主动踢掉让新连接进来。教训是:「同一房间只允许一路推流」这个限制必须配合「僵死连接检测」,否则会挡住合法的重连。

    踩过的坑二:并发的下播导致回放生成两次、礼物结算两次。主播点了下播,同时断流超时也触发了自动结束,两个流程都往下走,回放生成了两份,礼物结算跑了两遍(幸好有幂等表挡住了资金部分,但回放确实生成了两份占存储)。修法是房间状态用条件更新做唯一入口——只有把状态成功改为「已结束」的那个流程才继续执行收尾动作,另一个影响行数为 0 直接返回。教训是:多个触发源的收尾流程,必须用一个状态变更作为「谁有权执行收尾」的裁决点。

    没做的部分:没做转码任务的优先级调度。头部房间的转码应该优先于长尾房间,我们是按先到先转。活动期间转码集群紧张时,这个缺陷会让头部房间的多档也上不去。

    数字是怎么测的

    开播到首帧可见:从「主播端开始推流」到「观众端渲染出第一帧」的端到端时间。这个链路很长(推流建连、媒体服务器接流、转封装、CDN 回源、观众端拉流解码),所以要分段打点才能说清瓶颈在哪。P95 约 1.8s。要说明用的是什么协议——HTTP 系协议的首帧本身就比低延迟协议慢(要等一个切片),不说协议这个数字没有可比性。

    「误结束未再出现」怎么验证:构造场景——主播端主动断开推流若干秒后恢复,验证房间未结束、观众连接保留、弹幕通道保留、恢复后状态回到直播中还要测容忍窗口超时的分支:断流后不恢复,验证窗口超时后正常结束并生成回放。以及那个坑:断流后立刻重连,验证不会被自己的僵死旧连接挡住。

    转码资源下降:对比「全量房间开多档」和「按热度分档」两种策略下的转码集群占用。用「明显下降」这种定性描述而不是精确百分比——因为它完全取决于房间的观众数分布(长尾房间占比越高降幅越大),报一个固定百分比会被追问穿。可以报的是「同时在播房间中实际开启多档转码的比例」,这个口径清晰。

    不要报「直播成功率 99.9%」。直播中断的主要原因是主播的网络和设备,平台改不了。可以报的是「因平台原因导致的直播中断次数」——绝对数,口径清晰,而且是我们能控制的。

    面试追问
    Q:推流地址被别人拿到了,他就能往你的房间推流吗? A:光靠地址签名防不住,必须有第二层。签名能防「随便造一个地址」,但地址泄露后签名还在有效期内,所以要在媒体服务器接流前回调业务侧二次鉴权。这一层的价值是业务侧掌握实时状态:这个房间当前有没有其他推流在进行、主播是不是刚被封禁、房间是不是已经结束——这些是签名本身表达不了的。其中「同一房间只允许一路推流」这一条直接挡住了盗推:主播自己在推的时候别人推不进来。但这里有个必踩的坑:主播网络抖动重连时,旧的推流连接在媒体服务器侧还没被清理(TCP 还没超时),回调鉴权发现「已有推流」就拒绝了他,主播反复重连都失败。修法是回调时要判断已有的那路推流是否还在实际收到数据(心跳、最近的数据帧),僵死连接主动踢掉。「唯一推流」这个限制必须配合「僵死连接检测」,否则会挡住合法重连。另外地址要短时效并支持主播端自动换新,降低泄露价值。
    Q:主播过隧道断网几秒,直播就没了? A:不该没。断流必须进「容忍窗口」而不是立即结束。媒体服务器检测到推流中断后回调业务侧,业务侧把房间状态改为「异常中断」而不是「已结束」,房间保留、观众连接保留、弹幕通道保留,观众端展示「主播网络异常,正在重连」而不是直接被踢出去。容忍窗口内主播恢复推流就回到「直播中」,观众几乎无感知(只是画面卡了一段);窗口超时未恢复才走正常结束流程。这是主播体验最重要的一处设计——它把「一次抖动毁掉整场直播、几千观众全散」变成了「画面卡了一下」。窗口长度是明确的取舍:太短(几秒)等于没有容忍,太长(几分钟)主播真下播了观众还在干等,分钟级是合理量级,并且要在观众端给出提示或倒计时让观众知道在等什么。
    Q:直播延迟怎么降?为什么不都用低延迟协议? A:因为延迟和流畅度、成本是直接冲突的。低延迟协议(WebRTC 系)能做到几百毫秒,但抗弱网能力差、CDN 分发支持不如 HTTP 系成熟、成本明显更高;HTTP 系协议(HLS 之类)延迟几秒到十几秒,但极其稳定、CDN 分发成本低、几乎所有播放器都支持。所以我的做法是按互动强度分场景选:普通直播(观众只看不互动、弹幕有几秒延迟无所谓)走 HTTP 系协议,用几秒延迟换稳定和低成本;只有需要强互动的场景才用低延迟协议——连麦(双向对话,延迟高了没法交流)、答题(要同时看到题目)、直播抢购(要公平)。「按互动强度选协议」比一味追求低延迟正确,因为绝大多数观看行为对几秒延迟无感,而为此付出的稳定性和成本代价是实打实的。
    Q:为什么不给所有直播都出多档清晰度? A:因为绝大多数房间没人看,转码成本会全浪费在没人看的内容上。同时在播几千个房间,观众数是极度长尾分布的——头部几十个房间有几万观众,绝大多数只有几个人甚至零观众。给这些房间也出三档清晰度,转码集群的资源就全耗在那里了。所以按热度分档:开播只出原画(不转码,直接转封装分发,成本接近零),观众数超过阈值才动态开启多码率——理由是观众多了必然有网络差的用户需要低清晰度,观众少时那几个人凑合看原画影响面很小。头部主播要例外:按粉丝量预判,开播即开多档,避免开播瞬间涌入的大量观众都卡在原画上——这是「按预期热度而不是当前热度」做决策。还有个细节:观众数回落时要延迟关闭并设滞后阈值,因为转码任务启停有开销,观众数在阈值附近波动会导致反复开关。

模块二:弹幕与礼物

  1. 弹幕与礼物(合并下推 + 分级可靠性 + 礼物资金幂等)★★★
    简历这样写 直播间弹幕与礼物链路(长连接房间广播 + 窗口合并下推 + 优先级抽样 + 礼物幂等与余额条件扣减 + 榜单 ZSet):弹幕不逐条推送而是按时间窗口合并成批下推,超热房间做抽样与优先级(付费与高等级优先),并明确弹幕允许丢失;礼物走幂等下单 + 余额条件扣减 + 异步入账,与弹幕的可靠性等级严格区分;打赏榜单用 Redis ZSet 实时维护。压测单房间 5000 条/秒弹幕下,合并后的推送次数由与弹幕量持平降至每秒数次;礼物接口 P95 约 35ms,重复赠送与余额超扣在压测中未再出现
    展开完整拆解
    为什么要这么设计

    这个模块最重要的认知是:同一个直播间里,弹幕和礼物的可靠性要求相差一个量级,必须用完全不同的策略。第一版把它们当成同一类「互动消息」处理,两头都出问题。

    一是弹幕逐条推送导致扇出爆炸。一万人的房间,一条弹幕要推一万次。热门房间每秒几千条弹幕,扇出就是每秒几千万次推送,长连接网关直接被打满,连礼物消息也推不出去了。

    二是弹幕全量下推客户端也显示不了。每秒几千条弹幕,屏幕上根本放不下,客户端收到也是丢掉。我们花巨大成本推过去的东西,客户端直接扔了。

    三是礼物用了和弹幕一样的「尽力投递」,出了资损。用户送了礼物,扣了钱,但推送消息丢了,主播和其他观众没看到礼物特效,用户来投诉说钱扣了礼物没到。反过来也出过重复扣款——客户端超时重试,服务端处理了两次。

    四是打赏榜单实时算不动。榜单要按本场累计打赏金额排序,每次有礼物就重新聚合,热门房间下这个查询扛不住。

    所以四个设计:弹幕按窗口合并下推超热房间抽样并明确弹幕可丢礼物走幂等加余额条件扣减且必达榜单用 ZSet 增量维护

    整体链路
    弹幕(允许丢失,追求低成本高吞吐) │ ├─ 发送:客户端发弹幕 → 服务端本地关键词过滤 → 进房间队列 │ 命中高危词直接丢弃(不入队,发送者无感知或提示违规) │ 发送频率限制:同一用户每秒最多几条 │ ├─ 合并下推(这是扇出降下来的关键) │ 按房间维度攒一个时间窗口(比如 300ms) │ 窗口内的弹幕打包成一个批次消息 │ → 一万人的房间,每秒推 3 次而不是推几千次 │ ├─ 超热房间抽样(客户端显示不下,推过去也是浪费) │ 窗口内弹幕数超过上限 → 按优先级抽样保留 │ 优先级:付费用户 > 高等级 > 主播关注 > 普通 │ 被抽掉的弹幕对发送者仍显示(本地回显) │ → 发送者觉得自己发出去了,观感上没有损失 │ ├─ 分发:按房间广播到该房间所有连接 │ 连接按房间分组(订阅模型),不遍历全部连接 │ └─ 明确契约:弹幕允许丢失 不做 ack、不做重传、不做离线补齐 理由:丢一条弹幕没有任何业务后果 礼物(必达且不可重复,涉及真实资金) │ ├─ 客户端点赠送 → 按钮锁定 │ ├─ 提交带幂等键(用户 + 房间 + 礼物 + 客户端操作 ID) │ ├─ 服务端事务性处理 │ 1 幂等检查:该幂等键是否已处理 → 已处理返回原结果 │ 2 余额条件扣减(这一步防超扣) │ update account set balance = balance - ? │ where user_id = ? and balance >= ? │ 影响行数 0 → 余额不足,明确返回 │ 3 写礼物流水(这是结算与对账的唯一依据) │ 4 返回成功(此时用户看到特效) │ ├─ 异步部分(不阻塞用户) │ 推送礼物消息到房间(走高优先级通道,不和弹幕挤) │ 更新打赏榜单 ZSet │ 主播分成入账(异步,按场次结算) │ └─ 礼物消息的可靠性 走独立的高优先级通道,不与弹幕共用队列 投递失败要重试(和弹幕相反) 客户端进房时拉一次「本场高价值礼物」补齐特效 榜单(ZSet 增量维护,不实时聚合) ├─ 每笔礼物 ZINCRBY 用户在本场的累计金额 ├─ 读榜单直接 ZREVRANGE 取前 N ├─ 榜单只保留 Top N(几百万人的房间也不需要全量排名) └─ 下播后落库归档,Redis 数据可清理 优先级隔离(弹幕不能挤掉礼物) └─ 弹幕与礼物走不同的队列与线程池 弹幕洪峰时礼物消息仍能及时推达 这是分级可靠性在实现上的落地
    分步拆解
    1. 先明确弹幕和礼物是两个可靠性等级,这决定了后面所有设计。弹幕丢一条没有任何业务后果,礼物丢一条就是资损和投诉。把它们当同一类消息处理,必然是「要么弹幕成本失控,要么礼物出资损」。
    2. 弹幕按房间维度做窗口合并,这是扇出降下来的唯一有效手段。攒 300ms 的弹幕打包成一批推送。一万人的房间从「每秒推几千万次」降到「每秒推三次乘以人数」。代价是弹幕有几百毫秒延迟,而弹幕本身就不需要毫秒级实时。
    3. 超热房间要抽样,因为客户端显示不下。每秒几千条弹幕,屏幕放不下,全推过去客户端也是丢掉。按优先级抽样保留,付费用户和高等级优先。
    4. 被抽掉的弹幕对发送者要本地回显。他自己能看到「我发出去了」,观感上没有损失。如果连他自己都看不到,他会反复发。
    5. 弹幕明确不做 ack、不做重传、不做离线补齐。这是有意的契约,不是能力不足。把这个契约明确写下来,能避免后续有人「顺手」给弹幕加可靠性从而把成本又推上去。
    6. 连接要按房间分组订阅,不能遍历全部连接。广播时只遍历该房间的连接列表。遍历全网关的所有连接再判断房间,在连接数上万时是灾难。
    7. 礼物的余额扣减必须用条件更新,这是防超扣的唯一可靠手段。where balance >= 金额,影响行数为 0 就是余额不足。绝不能先查余额再扣——中间的窗口就是并发漏洞,用户快速连送能扣成负数。
    8. 礼物流水是结算与对账的唯一依据,必须落库。余额是当前状态会被覆盖,流水是不可变的事实记录。没有流水,主播分成和用户账单都算不清。
    9. 礼物消息走独立的高优先级通道,不和弹幕共用队列。弹幕洪峰时礼物消息仍要及时推达。这是「分级可靠性」在实现上的落地——不隔离队列,分级就只是说法。
    10. 礼物消息投递失败要重试,客户端进房还要能补齐。和弹幕相反。用户进房时拉一次「本场高价值礼物」补显特效,避免他错过。
    11. 榜单用 ZSet 增量维护,绝不实时聚合。每笔礼物 ZINCRBY,读榜单直接取前 N。只保留 Top N——几百万人的房间不需要全量排名。
    关键决策与取舍

    弹幕合并带来几百毫秒延迟,这个代价可以接受。弹幕不需要毫秒级实时——观众看到的弹幕晚 300ms 完全无感。但连麦或答题场景下的消息不能走这个合并通道(那些需要低延迟),所以合并只用于弹幕这类「氛围型」消息。按消息的实时性要求分通道,而不是一套策略打天下。

    弹幕抽样对被抽掉的用户是不公平的,这个要正面承认。普通用户在超热房间里发的弹幕可能永远显示不出来。缓解手段是本地回显(他自己能看到),以及抽样只在超过显示上限时才触发(大部分房间压根不抽样)。但本质上这是「付费优先」的产品选择,不是技术最优——说清楚这是业务决策而非技术限制,比假装公平好。

    踩过的坑:礼物特效已经播了,但扣款失败。第一版为了体验做了乐观更新——客户端点赠送后立刻播特效再发请求,结果余额不足或网络失败时特效已经播完了,用户以为送出去了,主播也看到了,实际没扣钱没入账。主播来问「刚才那个礼物怎么没算」。修法是礼物绝不乐观更新,必须等服务端确认扣款成功才播特效这和骑手端抢单的判断一致——涉及资金或竞争结果的操作不能乐观。体验上的补偿是把扣款接口做得足够快(P95 35ms,用户感知不到等待)。

    踩过的坑二:弹幕洪峰把礼物消息挤掉了。弹幕和礼物共用一个消息队列和推送线程池,热门房间弹幕暴涨时队列积压,礼物消息排在几万条弹幕后面,几十秒才推到房间,用户送了大额礼物半天没动静,投诉。修法是队列和线程池按消息优先级隔离教训是:「分级可靠性」如果不在资源层面隔离,就只是纸面上的分级——同一个队列里,低优先级消息一定会挤占高优先级消息。

    没做的部分:没做弹幕的历史回看(回放时同步显示当时的弹幕)。这需要把弹幕按时间轴存下来并在回放时对齐播放,存储和对齐都有成本,而弹幕本身是「允许丢失」的数据,两个定位有冲突,当时没做。

    数字是怎么测的

    弹幕合并的效果:压测单房间 5000 条/秒弹幕、房间内一万个连接,对比「逐条推送」和「窗口合并」两种模式下网关的实际推送次数与出口带宽。合并后推送次数从「与弹幕量持平」降到「每秒数次乘以连接数」。要说明窗口长度,因为推送次数直接由它决定。

    礼物接口 P95:压测时要构造余额扣减的热点——同一用户快速连送(考验幂等和条件更新)、以及大量不同用户同时送(考验整体吞吐)。两种都要测,P95 约 35ms。

    重复赠送与余额超扣验证:限速模拟弱网,同一用户对同一礼物连点 5 次,检查礼物流水只有一条、余额只扣一次;再用高并发送礼物直到余额耗尽,检查余额不会被扣成负数这两个是资金正确性,必须用确定性场景验证而不是比率。

    优先级隔离验证:在弹幕洪峰(5000 条/秒)的同时发送礼物,量礼物消息从服务端到客户端的延迟是否仍在正常范围。这是验证「分级可靠性真的落地了」的关键测试,也正是我们踩过坑的地方。

    不要报「弹幕到达率」。弹幕的契约本来就是允许丢失,报到达率自相矛盾。可以报的是「抽样触发的房间比例」和「合并后的推送次数」也不要报「礼物零丢失」——正确表述是「流水落库后必可对账,投递失败有重试与进房补齐」。

    面试追问
    Q:一万人的房间,一条弹幕要推一万次吗? A:推是要推的(每个连接都要收到),但不能每条弹幕都触发一轮推送。关键是按房间维度做窗口合并:攒 300ms 的弹幕打包成一个批次消息,然后广播一次。一万人的房间从「每秒几千条弹幕 × 一万连接」降到「每秒 3 个批次 × 一万连接」,推送次数降了三个数量级。代价是弹幕有几百毫秒延迟,而弹幕本身不需要毫秒级实时(观众完全无感)。超热房间还要抽样——每秒几千条弹幕屏幕上根本放不下,全推过去客户端也是丢掉,所以按优先级抽样保留(付费、高等级优先),但被抽掉的弹幕对发送者要本地回显,让他觉得自己发出去了,否则他会反复发。另外连接必须按房间分组订阅,广播时只遍历该房间的连接,不能遍历全网关连接再判断房间——那在连接数上万时是灾难。
    Q:弹幕和礼物都是直播间的消息,为什么要分开处理? A:因为可靠性要求相差一个量级,这是这个模块最重要的认知。弹幕丢一条没有任何业务后果——所以它可以窗口合并、可以抽样、明确不做 ack 不做重传不做离线补齐,追求的是低成本高吞吐。礼物丢一条就是资损和投诉——用户扣了钱但特效没播、主播没看到,所以它必须幂等、必须落流水、投递失败要重试、客户端进房还要能补齐高价值礼物的特效。把它们当同一类消息处理的结果必然是「要么弹幕成本失控,要么礼物出资损」。而且分级不能只停留在说法上——我们踩过坑:弹幕和礼物共用一个队列和线程池,弹幕洪峰时礼物消息排在几万条弹幕后面,几十秒才推到,用户送了大额礼物半天没动静。所以必须队列和线程池按优先级隔离,否则低优先级消息一定会挤占高优先级消息。
    Q:用户快速连点送礼物,会不会重复扣款或者把余额扣成负数? A:两层防护。重复扣款靠幂等键(用户 + 房间 + 礼物 + 客户端操作 ID),客户端在提交前生成并持久化,重试带同一个键,服务端已处理过的直接返回原结果。余额扣成负数靠条件更新update account set balance = balance - ? where user_id = ? and balance >= ?,影响行数为 0 就是余额不足,明确返回。绝对不能先查余额再扣——查和扣之间的窗口就是并发漏洞,用户快速连送能扣成负数,这是压测里很容易压出来的。另外礼物流水必须落库,因为余额是当前状态会被覆盖,流水是不可变的事实记录,主播分成和用户账单都靠它算。还有个体验上的坑我们踩过:第一版为了流畅做了乐观更新,点赠送后立刻播特效再发请求,结果扣款失败时特效已经播完,用户以为送出去了、主播也看到了,实际没扣钱没入账。修法是礼物绝不乐观更新,必须等服务端确认才播特效——涉及资金或竞争结果的操作不能乐观。
    Q:打赏榜单要实时更新,怎么做? A:用 Redis ZSet 增量维护,绝不实时聚合。每笔礼物成功后 ZINCRBY 增加该用户在本场的累计金额,读榜单直接 ZREVRANGE 取前 N,都是对数级复杂度。如果每次都去聚合礼物流水表,热门房间下这个查询扛不住(一场直播几十万条流水)。三个关键细节榜单只保留 Top N(几百万人的房间不需要全量排名,超出 Top N 的用户看自己的排名可以单独算或者只显示「未上榜」);ZINCRBY 要在礼物流水落库成功之后做,顺序反了会出现「榜单有但流水没有」的对不上;下播后落库归档并清理 Redis 数据,否则同时在播几千个房间的榜单数据会一直累积。另外榜单的准确性可以最终一致——如果 Redis 数据丢失,可以从礼物流水表重算恢复,这也是为什么流水必须落库。

模块三:录制回放与实时审核

  1. 录制回放与实时审核(分段录制 + 抽帧送审 + 违规即时切断)★★★
    简历这样写 直播录制回放与实时内容审核(分段录制 + 异步合并转码 + 实时抽帧与音频转写送审 + 分级处置与证据留存):直播流分段落盘录制并在下播后异步合并转码为可点播回放(分段的价值是异常中断也不丢已录部分);内容安全采用实时抽帧加音频转写送审,命中高危规则可即时切断推流并留存证据片段,中低风险进人工复核队列。下播到回放可看约 5 分钟(一小时直播),高危命中到推流切断在 10 秒内,违规处置全程有可回溯的证据与操作留痕
    展开完整拆解
    为什么要这么设计

    这个模块要先讲清直播审核和点播审核的根本差异,这也是它最值得讲的地方。

    点播可以「先审后发」——内容是已经存在的文件,审完再放出来,最坏情况是发布慢几分钟。直播不行,内容正在产生,观众正在看,你没有「先审」的机会。所以直播审核的目标不是「拦住违规内容」(做不到),而是「把违规内容的暴露时间压到最短,并且留下证据」。这个定位说不清楚,整个方案的取舍都会跑偏。

    第一版的问题很直接。

    一是录制到最后才落盘,异常中断就全丢。录制是一个大文件,直播结束时才写完。主播中途断流或者媒体服务器进程异常,整场录制什么都不剩,主播想要回放也没有。

    二是审核只在下播后跑,等于没有审核。违规内容已经播完了、被几万人看过了,事后审出来只能封号,而伤害已经造成

    三是切断推流没有证据。切断后主播申诉「我什么都没做」,我们只有一条「疑似违规」的记录,拿不出具体是哪一帧哪一句话,没法定论。

    四是处置只有「切断」一档。轻微的疑似违规也直接切断,误伤主播很严重(他的整场直播和当天收入都没了),而放过又有风险。

    所以四个设计:分段录制实时抽帧与音频转写送审命中高危即时切断并留存证据按风险分级处置而不是一刀切断

    整体链路
    分段录制(分段的价值是异常时不丢已录部分) │ ├─ 媒体服务器按固定时长切片落盘(比如每 10 秒一段) │ 每段独立上传对象存储,路径按 房间/场次/序号 │ ├─ 段的元信息入库:序号 / 起止时刻 / 时长 / 存储路径 │ → 即使中途异常,已上传的段都在,可以合并出部分回放 │ ├─ 下播后异步合并 │ 按序号拼接 → 转码成点播格式(多码率)→ 生成封面 │ 合并是分阶段可重试的(和点播转码同一套编排思路) │ └─ 回放可看后通知主播与订阅者 实时审核(直播没有「先审后发」的选项) │ ├─ 采集:从直播流实时抽帧 + 音频转写 │ 抽帧间隔是成本与覆盖率的取舍 │ 画面变化大时加密抽帧,静态画面降频(变化检测) │ 封面与标题单独送审(分发时只展示这两个,影响面最大) │ ├─ 送审:本地前置规则 + 三方机审 │ 本地:关键词库(标题、弹幕、转写文本)、黑名单主播 │ 命中高危词直接进处置,不等三方(省时间) │ 三方:图像与语音的违规识别,返回类型与置信度 │ ├─ 结果分三档(和点播审核一致,但处置动作完全不同) │ 高危高置信 → 即时切断推流 + 留存证据 + 通知 + 可申诉 │ 中风险 → 不切断,进人工复核队列(高优先级) │ 同时降低分发权重(不推首页、不进推荐) │ 低风险 → 记录,抽样人工复核 │ ├─ 即时切断的执行链路(要快) │ 业务侧判定 → 调媒体服务器踢流接口 → 房间状态置违规结束 │ → 通知观众端(明确文案,不是「网络异常」) │ → 冻结本场礼物结算(待复核,防止违规获利) │ └─ 证据留存(这是能不能定论的关键) 命中的帧图片 + 前后几秒的音视频片段 + 转写文本 命中的规则与置信度 + 判定时刻 → 主播申诉时能拿出具体证据,而不是一句「疑似违规」 人工复核工作台(中低风险的去处) ├─ 按风险等级与房间热度排序(热门房间的疑似违规优先) ├─ 展示证据片段而不是让审核员去看整场直播 ├─ 处置动作:通过 / 警告 / 中断 / 封禁,各自留痕 └─ 复核结论回流:作为机审阈值调整的样本 礼物与结算的联动(不能让违规主播拿到钱) ├─ 判定违规 → 冻结本场礼物分成结算 ├─ 复核确认违规 → 按规则处理(退还观众或平台留存) └─ 复核为误判 → 解冻并正常结算 + 补偿
    分步拆解
    1. 录制必须分段,这一条的价值在异常时才体现。每 10 秒一段独立上传,元信息入库。中途主播断流、媒体服务器进程崩溃,已上传的段都在,可以合并出部分回放。整场一个大文件的做法在异常时是全丢。
    2. 合并转码走异步且分阶段可重试。和点播转码是同一套编排思路——按序号拼接、转码、生成封面,每阶段的产物落地,失败只重试单阶段。
    3. 实时审核的采集要做变化检测,不能固定间隔死抽。画面变化大时加密抽帧(可能在发生什么),静态画面降频(主播在挂机)。固定间隔要么漏(间隔大)要么贵(间隔小)。
    4. 封面和标题必须单独重点送审。分发时观众只看到这两个,违规封面的影响面比直播内部的画面大得多——它出现在推荐流里被所有人看到。
    5. 本地前置规则先跑,命中高危词直接处置不等三方。关键词匹配是微秒级的,而三方机审有网络往返和处理时间。能本地判定的就别等,这几百毫秒在「暴露时间」上是有意义的。
    6. 结果必须三档,处置动作完全不同。高危高置信才即时切断;中风险不切断但降低分发权重(不推首页不进推荐,把暴露面压小)并进人工复核;低风险只记录加抽样。只有「切断」一档会导致大量误伤。
    7. 「降低分发权重」是直播审核里性价比最高的处置手段。它不打断主播(不误伤),又把违规内容的暴露面压到了最小(只有已在房间的人能看到)。这个中间档是直播审核和点播审核最大的差异点。
    8. 切断推流后给观众的文案必须明确。不能显示「网络异常」——那样观众会以为是平台问题,而且会反复重进。如实说明「该直播因违规已结束」。
    9. 证据留存是能不能定论的关键。命中的帧图片、前后几秒的音视频片段、转写文本、命中的规则与置信度、判定时刻。没有这些,主播申诉时我们拿不出任何东西,只能靠强势压过去,长期会损害主播关系。
    10. 判定违规要立刻冻结本场礼物结算。防止违规主播先拿到钱。复核确认违规按规则处理,复核为误判则解冻并补偿——补偿这一步很重要,误伤了要有交代。
    11. 人工复核工作台要展示证据片段,不能让审核员看整场直播。一场直播几小时,让人从头看是不可能的。直接跳到命中时刻的前后几秒,效率差几个数量级。
    12. 复核结论要回流成机审的调优样本。误判的案例是最有价值的数据。不做这一步,机审的准确率永远不会提高。
    关键决策与取舍

    直播审核的目标必须重新定义:不是「拦住」而是「压缩暴露时间并留证据」。内容正在产生,物理上不存在「先审后发」的可能。把目标定成「零违规内容流出」是不现实的,会导致所有取舍都朝「宁可误杀」倾斜,最后误伤大量正常主播。正确的目标让我们能理性地在「抽帧成本」「切断阈值」「人工投入」之间做权衡。

    抽帧间隔是成本与覆盖率的直接权衡,而且无法两全。间隔大会漏掉一闪而过的违规画面,间隔小则三方机审的调用费用和处理延迟都上去(同时几千个房间在播)。我们的处理是「变化检测 + 分级」:画面变化大时加密、静态时降频;对高风险主播(历史有违规)加密抽帧,对可信主播降频。承认「一定会漏」,然后用分级把资源花在高风险的地方。

    即时切断的阈值必须设得很高,因为误伤代价极大。切断意味着主播整场直播结束、当天收入没了、观众流失。所以只有「高危类型 + 高置信度」才切断,其余走「降权 + 人工复核」。宁可让中风险内容在小范围多存在几分钟,也不要误切一个正常主播。这个方向和点播审核相反(点播可以宁可拦下等人审),因为直播的误伤是不可逆的。

    踩过的坑:切断推流后观众端显示「网络异常」,观众反复重进。切断的实现是调媒体服务器踢流,观众端拉不到流就走了「网络异常」的通用分支,观众以为是平台问题,反复退出重进,房间的连接数反而暴涨,而且客服收到一堆「你们直播打不开」的投诉。修法是切断时向观众端推送明确的状态与文案(「该直播因违规已结束」),并且阻止重进。教训是:任何「非正常结束」都必须给出明确的状态,让不出现「通用错误分支」兜底——通用分支会掩盖真实原因并引发错误的用户行为。

    踩过的坑二:违规判定后礼物照常结算,主播拿到了违规获利。审核判定违规并切断了直播,但礼物结算是按场次异步跑的,没有和审核结论联动,钱正常打给主播了。后来复核确认违规,钱已经提现走了,追不回来。修法是判定违规立刻冻结本场结算,等复核结论出来再决定。教训是:内容处置和资金处置必须联动,任何一个环节漏了联动就会出现「违规获利」这类既是损失也是舆情风险的问题。

    没做的部分:没做弹幕内容与画面的联合审核(有些违规是画面正常但弹幕引导的)。这需要把两路信号关联分析,复杂度较高,当时是各自独立审核。

    数字是怎么测的

    下播到回放可看:从「房间状态置为已结束」到「回放可播放」的时间,一小时直播约 5 分钟。必须说明直播时长——这个耗时和时长基本成正比(要合并所有段并转码),说「5 分钟」不说时长没有意义。也要说明是否出多档(多档转码更慢)。

    高危命中到推流切断:从「审核判定命中」到「媒体服务器确认踢流完成」的时间,10 秒内。要分段打点(判定、调用踢流接口、媒体服务器执行、观众端收到通知),因为面试官会问瓶颈在哪。另外要诚实说明「命中之前的时间」不在这个指标里——从违规行为发生到被抽帧捕获,取决于抽帧间隔,这段是真正的暴露时间。

    暴露时间的完整口径:这是最该诚实的地方。真实的暴露时间 = 抽帧间隔(平均一半)+ 机审耗时 + 切断执行时间。只报「切断在 10 秒内」会给人「违规内容只暴露 10 秒」的错误印象。把完整口径说清楚,比报一个好看的分段数字可信。

    「证据可回溯」怎么验证:随机抽已处置的案例,检查是否能调出命中帧、前后音视频片段、转写文本、命中规则与置信度、判定时刻。可以做成自动化校验:每条处置记录必须有完整的证据集,缺失就告警。

    不要报「违规内容拦截率」或「审核准确率」。两个都需要「真实违规总量」作为分母,而那个数字不可知(没被发现的违规不在统计里)。可以报的绝对数:每日抽帧量、送审量、机审命中量、即时切断次数、转人工量、复核推翻量。其中「复核推翻量」最有价值——它直接反映误伤情况,而且报出来说明你在正视误判而不是只讲成绩。

    面试追问
    Q:直播内容审核和点播审核有什么区别? A:根本区别是点播可以「先审后发」,直播不行。点播的内容是已经存在的文件,审完再放出来,最坏是发布慢几分钟;直播的内容正在产生、观众正在看,物理上不存在「先审」的机会。所以直播审核的目标必须重新定义:不是「拦住违规内容」(做不到),而是「把暴露时间压到最短并留下证据」。这个定位不说清,所有取舍都会跑偏——如果把目标定成「零违规流出」,就会朝「宁可误杀」倾斜,最后误伤大量正常主播。由此带来的具体差异:采集方式不同(要实时抽帧和音频转写,而不是拿整个文件去审);处置动作不同——点播可以「不通过就不发布」,直播多了一档「降低分发权重」(不推首页不进推荐,把暴露面压小但不打断主播),这个中间档是直播审核性价比最高的手段;切断阈值必须设得很高,因为切断意味着主播整场直播和当天收入都没了,误伤不可逆。
    Q:为什么录制要分段?直接录一个大文件不行吗? A:分段的价值在异常时才体现。录一个大文件的话,直播结束时才写完,中途主播断流、媒体服务器进程崩溃、磁盘满了,整场录制什么都不剩——主播想要回放也没有,而对头部主播来说一场直播的回放是有价值内容。分段(每 10 秒一段独立上传对象存储,元信息入库)之后,即使中途异常,已上传的段都在,可以合并出部分回放。下播后异步按序号拼接、转码成点播格式、生成封面,合并过程也是分阶段可重试的(和点播转码同一套编排思路,失败只重试单阶段而不是整场重来)。顺带的好处:分段之后可以边直播边处理已完成的段(比如提前转码),下播到回放可看的时间能缩短;而且实时审核抽帧也可以从已落盘的段里取,不用额外从流里抽。
    Q:命中违规后立刻切断推流,如果是误判怎么办? A:所以切断的阈值必须设得很高,只有「高危类型 + 高置信度」才切断。误伤的代价极大——主播整场直播结束、当天收入没了、观众流失,而且不可逆。中风险的处置是「不切断但降低分发权重」:不推首页、不进推荐,只有已在房间的人能看到,暴露面被压到最小,同时进人工复核队列(高优先级)。这个中间档是关键——它避免了「要么放过要么误杀」的二选一。误判发生后的处理:证据留存让申诉能定论(命中帧、前后音视频片段、转写文本、命中规则与置信度、判定时刻,而不是一句「疑似违规」);复核为误判要解冻结算并补偿——误伤了要有交代,否则主播关系会崩;误判案例回流成机审调优样本,不做这一步准确率永远不会提高。另外「复核推翻量」这个指标要主动报,它反映误伤情况,报出来说明在正视误判而不是只讲成绩。
    Q:违规主播已经收到礼物了,钱怎么办? A:判定违规必须立刻冻结本场礼物结算,这是我们踩过坑才补上的。当时审核判定违规并切断了直播,但礼物结算是按场次异步跑的,没有和审核结论联动,钱正常打给主播了;后来复核确认违规,钱已经提现走了,追不回来——既是损失也是舆情风险(观众知道后会质疑平台放任违规主播获利)。修法是内容处置和资金处置必须联动:判定违规立刻冻结本场结算;复核确认违规则按规则处理(退还观众或平台留存,这个要业务和法务定);复核为误判则解冻并正常结算加补偿。通用教训是:任何「内容处置」流程都要检查有没有对应的「资金处置」联动——直播礼物、电商佣金、内容分成都属于这一类,漏了联动就会出现违规获利。而且冻结要在判定的那一刻就做,不能等复核结论——因为结算跑批可能在复核之前就执行了。

没有匹配的内容,换个关键词试试。

项目拆解 · 直播互动与推拉流(后端)· 共 3 个模块 · 数字均为示例,需替换成自己项目的真实数据