《移动游戏产品的架构·网络、资源与热更新》13.1 网络与资源指标矩阵

卷二的收口从一张指标矩阵开始:每项指标必须同时回答属于哪一组、单位是什么、预算多少、哪款产品超标。本节把连接质量、可用性、状态恢复、流量与下载五组指标写成可运行校验,用 ok/watch/fail 三档区分「可发布」「需观察」「必须修复」。

两份都「正常」的交付报告

版本评审时,消消的客户端负责人说:「下载成功率 97.5%,正常。」方舟的负责人说:「我们 98.6%,比消消好。」

两份报告都没有说谎,但它们回答的不是同一个问题。消消的 97.5% 里,弱网用户只占 22%,却贡献了大部分失败;方舟的 98.6% 建立在一个 350MB 首包和 1,490MB 分包之上,一个失败用户损失的是消消的七倍流量。

单看一个数字,永远得不出「正常」的结论。 网络与资源的交付需要一张矩阵:把指标按组排开,给每组定预算,再按产品分别判定。

矩阵的五个组

卷二讨论的所有链路,可以收进五组指标:

组回答的问题典型指标
连接质量玩家在网络上的起点有多差RTT P95、丢包率、弱网会话占比
可用性连接断了能不能回来断线率、重连成功率
状态恢复回来之后状态对不对重连后需补状态比例
流量一次会话花掉多少每会话流量、每 DAU 日流量
下载资源能不能顺利到达下载成功率、弱网下载成功率

五组不是并列关系,而是一条链:连接质量差会推高断线率,断线率高会放大状态校准成本,状态校准又会增加流量,而下载失败会让玩家根本进不到这条链里。

矩阵的价值,是让这条链上的每一段都有数字,而不是只盯着最末端的结果指标。

把矩阵写成校验

下面的模块把指标、预算与判定写成代码。它不采集数据,只负责回答一个问题:给定一组指标值和预算,哪款产品超标,超在哪一档。

// file: src/lab/net-matrix.ts
// tier: L1
// verify: node --test test/lab/net-matrix.test.ts
export type ProductId = "match3" | "ark";

export type Direction = "lower" | "higher";

export interface MetricRow {
  readonly id: string;
  readonly group: string;
  readonly unit: string;
  readonly match3: number;
  readonly ark: number;
  readonly direction: Direction;
  readonly budget: number;
}

export const METRIC_GROUPS = ["连接质量", "可用性", "状态恢复", "流量", "下载"] as const;

export const NET_METRICS: readonly MetricRow[] = [
  { id: "rtt_p95", group: "连接质量", unit: "ms", match3: 310, ark: 240, direction: "lower", budget: 350 },
  { id: "loss_rate", group: "连接质量", unit: "%", match3: 0.4, ark: 0.6, direction: "lower", budget: 2 },
  { id: "weak_session", group: "连接质量", unit: "%", match3: 22, ark: 14, direction: "lower", budget: 25 },
  { id: "disconnect", group: "可用性", unit: "%", match3: 2.1, ark: 1.4, direction: "lower", budget: 2.5 },
  { id: "reconnect_success", group: "可用性", unit: "%", match3: 97.2, ark: 98.1, direction: "higher", budget: 96 },
  { id: "resync_needed", group: "状态恢复", unit: "%", match3: 8, ark: 31, direction: "lower", budget: 35 },
  { id: "session_traffic", group: "流量", unit: "MB", match3: 3.2, ark: 18, direction: "lower", budget: 20 },
  { id: "download_success", group: "下载", unit: "%", match3: 97.5, ark: 98.6, direction: "higher", budget: 97 },
];

export type Verdict = "ok" | "watch" | "fail";

export function judge(row: MetricRow, value: number): Verdict {
  const ratio = row.direction === "lower" ? value / row.budget : row.budget / value;
  if (ratio <= 1) {
    return "ok";
  }
  if (ratio <= 1.1) {
    return "watch";
  }
  return "fail";
}

export function evaluateProduct(metrics: readonly MetricRow[], product: ProductId): readonly string[] {
  const problems: string[] = [];
  for (const row of metrics) {
    const value = row[product];
    const verdict = judge(row, value);
    if (verdict === "fail") {
      problems.push(`${product}:${row.id}=${value}${row.unit} fails budget ${row.budget}${row.unit}`);
    }
  }
  return problems;
}

export function matrixGaps(metrics: readonly MetricRow[]): readonly string[] {
  const covered = new Set(metrics.map((row) => row.group));
  return METRIC_GROUPS.filter((group) => !covered.has(group));
}

export function watchRows(metrics: readonly MetricRow[], product: ProductId): readonly string[] {
  return metrics
    .filter((row) => judge(row, row[product]) === "watch")
    .map((row) => row.id);
}

export function formatMatrix(metrics: readonly MetricRow[]): string {
  const header = "id\tgroup\tunit\tmatch3\tark\tbudget\tdirection";
  const rows = metrics.map((row) =>
    `${row.id}\t${row.group}\t${row.unit}\t${row.match3}\t${row.ark}\t${row.budget}\t${row.direction}`,
  );
  return [header, ...rows].join("\n");
}

judge 的规则很简单:

  • 越小越好的指标,值 / 预算 ≤ 1 为 ok,≤ 1.1 为 watch,再高为 fail;
  • 越大越好的指标反过来,用 预算 / 值 计算同一比例。

watch 这一档是刻意加的。真实项目里很少有「刚好卡在预算线上」的指标,更多是「超了 3%」——直接判失败会让人麻木,直接判通过又会掩盖趋势。watch 的作用是进入周报,不阻塞发布,但必须写原因。

双案例矩阵

用默认指标跑一遍,得到两张可对照的表:

指标消消方舟预算判定
RTT P95310ms240ms350ms均 ok
丢包率0.4%0.6%2%均 ok
弱网会话占比22%14%25%均 ok
断线率2.1%1.4%2.5%均 ok
重连成功率97.2%98.1%96%均 ok
重连后需补状态8%31%35%均 ok
每会话流量3.2MB18MB20MB均 ok
下载成功率97.5%98.6%97%均 ok

这张表最容易被误读的地方是「均 ok」。它不表示两款产品一样健康,而表示它们各自在自己的约束内。消消的弱网占比 22% 接近预算 25%,方舟只有 14%;方舟的补状态比例 31% 接近预算 35%,消消只有 8%。

同样一条预算线,对两款产品的意义完全不同。 消消的风险在「弱网用户多」,方舟的风险在「重连后要补的东西多」。矩阵的作用不是排名,而是指出每款产品离自己的红线有多远。

缺口检测

矩阵还有一种用法:检查有没有哪一组完全没指标。

matrixGaps 返回没有被任何指标覆盖的组。它看起来是个很弱的检查,实际非常有效——很多团队的网络监控只有「在线人数」「连接数」和「错误数」,五组里只覆盖了半组「可用性」,剩下四组全靠事故驱动。

一个典型的缺失是「状态恢复」:团队会监控断线和重连,却很少有人统计「重连后有多少比例需要补状态」。而这个比例直接决定了重连体验——补得越多,玩家回来看到的世界越不一样。

三档判定怎么用

ok、watch、fail 三档对应三种动作:

判定动作责任人
ok记录基线,不额外处理无
watch进入周报,写原因与趋势指标 owner
fail阻塞发布,先修再发版本负责人

watch 最容易被忽略。团队通常只处理 fail,把 watch 当成「还好」。但网络指标的特点是波动大:这个版本 3% 的超出,下个版本可能变成 15%。watch 是趋势的入口,不是免死金牌。

反过来,fail 也不该一律阻塞。如果一款产品正在做大规模弱网优化,短期 RTT 超标可能是预期内的;这时应该在评审记录里写明「带病发布」与恢复时间,而不是偷偷把预算调高。

矩阵怎么维护

指标矩阵不是发布时填一次的表,它随版本滚动:

  1. 每个大版本重跑一次,保留上一版,用来看趋势。
  2. 预算随产品阶段调整,但调整必须写原因;悄悄放宽预算等于取消门禁。
  3. 新增指标先加组,不要把所有东西塞进「其它」。
  4. 跨产品比较前先对齐口径,尤其是流量与下载成功率。

矩阵的另一个作用是给新功能定预算。上线一个实时排行榜之前,先问它属于哪一组、占多少流量、会不会把 session_traffic 推过 watch 线。没有这一步,功能做完才发现流量超标,只能砍。

别把矩阵做成 KPI

矩阵一旦被当成考核指标,就会迅速失真:

  • 指标 owner 会把「接近预算」解释成「波动」,把 fail 说成「极端个例」;
  • 产品会用短期活动把数字做好看,而不解决结构问题;
  • 团队会挑容易测的指标上报,把难测的组留空。

避免的方式不是取消矩阵,而是把矩阵的用途限定清楚:它是发布门禁与趋势记录,不是个人绩效。 评审看的是「这个版本能不能发」,不是「谁的指标更好看」。责任人的作用是在超标时解释原因、给修复计划,而不是为数字辩护。

指标从哪来

矩阵本身不采集数据,但每一项指标都要有明确的采集口径,否则矩阵只是填表游戏:

指标采集位置采样方式已知偏差
RTT P95客户端连接层每次请求响应计时,按会话聚合只统计成功的请求,失败请求偏慢
丢包率客户端与服务端双向序号缺口 / 发送总数服务端视角看不到下行丢失
弱网会话占比客户端按会话 P95 与丢包阈值判定阈值选择影响结果
断线率客户端连接状态机前台会话内非主动断开切后台是否计入要统一
重连成功率客户端30 秒内恢复且完成校准只统计发起重连的设备
需补状态比例服务端下发 + 客户端确认重连后下发增量的会话占比增量大小不敏感
每会话流量客户端网络层上行 + 下行字节数不含资源下载
下载成功率下载器开始到校验通过分母按下载次数

采集口径比指标值更重要。同一个「断线率」,包含切后台与不包含,能差出一个数量级;矩阵里每个数字旁边都应该有口径,而不是只有一个数。

一次版本评审的读表顺序

拿到矩阵后,不要从第一行读到最后一行。推荐的顺序是:

  1. 先看 fail:有没有指标直接阻塞发布,分别属于哪款产品。
  2. 再看 watch:超预算 10% 以内的项,趋势是变好还是变差。
  3. 再看 matrixGaps:有没有哪一组完全没数据,这通常意味着监控盲区。
  4. 最后看跨产品差异:同一指标两款产品差距大,往往不是网络问题,而是产品形态差异。
  5. 记录结论:通过、带病发布、阻塞,三选一,写清责任人与复查时间。

这个顺序的好处是把「讨论数字」变成「讨论动作」。评审会上最常见的低效,是花半小时争论某个数字准不准,却没决定这个版本发不发。

与相邻章节的关系

本节依赖第 3 章的重连指标、第 7 章的流量账与第 12 章的下载数据。那三章分别给出单点测量方法,本节把它们合并成一张跨产品可复核的矩阵。

上一节 12.3 讲下载失败怎么降级,本节回答「降级之后整体指标是否达标」;下一节 13.2 把矩阵接进交付流水线,变成发布门禁的一部分。

小结

  • 网络与资源指标分五组:连接质量、可用性、状态恢复、流量、下载。
  • 单看一个数字得不出「正常」结论,矩阵要按组、按预算、按产品分别判定。
  • 判定分三档:ok 记录、watch 进周报、fail 阻塞发布。
  • 同样一条预算线对两款产品的意义不同,矩阵用来指风险,不用来排名。
  • matrixGaps 检查有没有哪一组完全没指标,比单点指标更容易发现监控盲区。
  • 矩阵随版本滚动,预算可以调整,但调整必须写原因。
  • 新功能立项时先领指标预算,不要等上线后再砍。

阅读导航:上一节:12.3 下载失败的降级与重试 · 下一节:13.2 交付流水线与发布门禁 。

常见错误与边界

  1. 只监控结果指标。 只看下载成功率和在线人数,无法定位是连接、恢复还是资源的问题;五组指标要一起看。
  2. 用平均值代替分位数。 平均 RTT 65ms 可能由 60ms 和 800ms 拼成;网络指标至少看 P95。
  3. 把 watch 当通过。 超出预算 10% 以内不阻塞发布,但必须进周报并写原因,否则趋势会被忽略。
  4. 预算一年不变。 产品阶段、机型分布、网络环境都在变;预算不更新,门禁会失效。
  5. 跨产品直接排名。 消消与方舟的会话时长差 5 倍,流量与断线率不能直接比大小。
  6. 把矩阵当监控系统。 本节产物只做判定,不采集数据;采集仍要靠埋点与监控平台。
  7. 指标没有 owner。 没有责任人的 watch 项,下个版本大概率变成 fail。
  8. 新增指标塞进「其它」。 分组是判断链的一部分,塞进其它等于放弃判断。
  9. 为了过门禁调预算。 调整预算必须与指标变化一起评审,不能只改数字。
  10. 忽略机型与区域分层。 全局 97% 的下载成功率,可能在低端机上只有 88%;矩阵应能按分层重算。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《移动游戏产品的架构·商业化与发行工程》附录 C 商业化与发行评审清单
  2. 《移动游戏产品的架构·商业化与发行工程》附录 B 双案例商业化事实表
  3. 《移动游戏产品的架构·商业化与发行工程》附录 A 商业化与发行术语速查