接入指南

什么时候该把轮询换成推送:体育数据接入对照

一份按时间线排的接入对照:今天先跑通 REST 最小闭环,联调周再评估实时推送,上线前用三项监控和一份清单确认切换时机,不堆术语,直接给可复制的代码片段。

北辰 发布于 更新于 约 4 分钟读完
体育数据接口接入对照示意图
注册即用

注册开发者账号即获试用额度,多数团队当天完成联调。

标准鉴权

app_key / app_secret 服务端签名,规范统一、接入无歧义。

实时更新

比分、事件与统计实时更新,数据链路持续稳定。

今天就能做完的最小闭环

先把话说清楚:这篇文章不劝你立刻上实时推送。轮询和推送不是新旧关系,是两种成本结构。判断标准只有一条——你的页面在「没人操作」的时候,是否仍然需要秒级知道比分变了。

如果答案是「不需要」,那轮询更省事:请求-响应模型简单,失败重试逻辑天然存在,浏览器和网关都不挑。如果答案是「需要」,比如比分 App 的进行中列表、转播后台的事件条、官方产品的进球提醒,那轮询就会开始难受:要么间隔拉长导致延迟明显,要么间隔缩短导致请求数和连接数一起涨。

今天就能做完的最小闭环,是先不碰推送,只用 REST 把链路跑通一次:

# 1. 注册并拿到 key(示意)
# https://dev.apiballs.com/#/user_register

# 2. 拉一场进行中比赛的快照
curl -s "https://api.apiballs.com/v1/matches?status=live&limit=5" \
  -H "Authorization: Bearer $APIBALLS_KEY"

# 3. 拉该场比赛的事件流
curl -s "https://api.apiballs.com/v1/matches/{match_id}/events?since=0" \
  -H "Authorization: Bearer $APIBALLS_KEY"

这三步的意义不是「接入完成」,而是确认三件事:key 能用、网络通、返回结构你读得懂。很多团队卡在联调第一天的原因,不是协议选错,而是连字段含义都没对齐——比如 status 的取值集合、事件 type 的枚举、时间戳是秒还是毫秒。把这些记在一张表里,后面省掉大量来回。

顺手记一个数字:你打算多久轮询一次。这个数字会在第三节直接决定你要不要换推送。

密钥与环境隔离(dev/test/prod 分开)

联调阶段最常见的自伤,是三个环境共用一把 key。表现是:本地调试把配额跑满,测试环境开始报错,你以为是服务端抖动,查半天发现是自己人打自己人。

正确做法是环境隔离从第一天就做,不是上线前补:

环境 key 用途 允许的调用 数据源
dev 本地开发 少量快照、单场事件 演示或低频数据
test 联调、压测 订阅白名单内的比赛 与生产同结构
prod 线上 全量 生产数据

配置上别把 key 写进仓库。用环境变量或密钥管理服务,本地 .env 加进 .gitignore:

# .env.local(不要提交)
APIBALLS_KEY=dev_xxxxxxxx
APIBALLS_ENV=dev
APIBALLS_PUSH_URL=wss://push.apiballs.com/v1/stream

服务端注入时做一层校验,防止 dev 的 key 被打到 prod:

const env = process.env.APIBALLS_ENV;
if (env === 'prod' && process.env.APIBALLS_KEY.startsWith('dev_')) {
  throw new Error('prod 环境检测到 dev key,拒绝启动');
}

这一行 throw 会在未来某次误操作时救你一次。我们见过太多「上线后才发现用的是测试 key」的现场,排查方向全错。

另外,key 的权限范围也建议分开:联调用只读权限,生产再按需开。推送和轮询可以用不同的凭证,这样某一侧出问题时不至于把另一侧一起拖下去。

实时订阅配额怎么用在联调(不要一上来订全部进行中比赛)

到了这一步,你大概已经决定要试推送了。先别急着把「所有进行中比赛」一次性订阅上。

实时推送的配额通常按「并发订阅数」或「连接数」计。联调阶段全量订阅有两个坏处:一是配额瞬间打满,后面真正要测的场景没额度;二是数据噪声太大,你分不清是推送有问题还是业务逻辑有问题。

建议的联调节奏:

  • 第一天:只订一场比赛,确认能收到连接建立、心跳、事件下发三类消息
  • 第二天:订 3 到 5 场,观察断线重连和事件顺序
  • 第三天:模拟一次网络抖动,看客户端能不能补齐断连期间的事件

WebSocket 接入示意:

const ws = new WebSocket(
  `${process.env.APIBALLS_PUSH_URL}?key=${process.env.APIBALLS_KEY}`
);

ws.onopen = () => {
  ws.send(JSON.stringify({
    action: 'subscribe',
    channels: ['match.events'],
    matchIds: ['demo_match_001'] // 先只订一场
  }));
};

ws.onmessage = (msg) => {
  const data = JSON.parse(msg.data);
  // data.type: 'event' | 'heartbeat' | 'subscribed' | 'error'
  if (data.type === 'event') {
    handleEvent(data.payload);
  }
};

订阅消息里带 matchIds 白名单,是联调期最重要的习惯。上线后你可能会按联赛或按状态订阅,但联调阶段一定要窄。

还要确认一件事:推送是不是「增量」的。如果是,断线期间的事件需要你主动用 REST 补拉:

# 重连成功后,按最后收到的事件时间补拉
curl -s "https://api.apiballs.com/v1/matches/{id}/events?since={last_event_ts}" \
  -H "Authorization: Bearer $APIBALLS_KEY"

没有这个补拉逻辑,推送一旦断线就会丢事件,而且你很难发现——因为重连后新事件照常来,看起来一切正常。

监控三项:REST 错误率、连接状态、事件延迟

换推送之后,很多人只盯「有没有收到消息」,这不够。至少三项指标要同时看,缺一项都会让你误判。

REST 错误率。 推送不是替代 REST,而是和它配合。补拉、兜底、冷启动都走 REST,如果 REST 错误率上升,推送看起来正常也没用,因为你的数据是残缺的。按状态码分桶看,401 和 429 要分开报警。

连接状态。 至少记录:当前活跃连接数、重连次数、平均连接存活时长。重连次数突然上涨,通常不是你的代码问题,而是链路中间有设备在掐长连接。

事件延迟。 定义清楚:从事件发生时间到客户端处理完成的时间差。分位数比平均值有用,关注 P95 和 P99。延迟抖动往往先于丢事件出现,是更早的预警信号。

一个最小监控片段:

const metrics = { restErrors: 0, reconnects: 0, eventLatency: [] };

function onEvent(payload) {
  const latency = Date.now() - payload.occurredAtMs;
  metrics.eventLatency.push(latency);
  if (latency > 5000) {
    console.warn('event latency high', { matchId: payload.matchId, latency });
  }
}

阈值自己定,但别定得太理想。跨区域、跨境链路下,延迟波动是常态,报警太灵敏会让你很快开始忽略它。

上线前检查清单

上线前一周,不要再加新功能,把下面这些逐条过一遍。

  • dev / test / prod 三套 key 已隔离,prod 启动时校验 key 前缀
  • 推送断线后有 REST 补拉逻辑,补拉游标能持久化
  • 心跳超时、重连退避、最大重试次数都有明确配置
  • 订阅白名单在生产按联赛或状态收敛,不是「全部进行中」
  • REST 错误率、连接状态、事件延迟三项都有面板和报警
  • 事件字段的枚举、时区、时间戳单位与前端对齐并写入文档
  • 灰度计划:先放一部分流量走推送,REST 保留为兜底
  • 回滚方案:关掉推送后,轮询配置能立刻接管

最后一条经常被跳过。切换推送的那天,你最需要的不是推送本身,而是「随时能退回去」的能力。把轮询配置保留一个版本周期,等推送稳定运行一段时间后再删。

回到标题的问题:什么时候该把轮询换成推送。不是「推送更先进」,而是当你的场景对延迟敏感、且轮询的请求成本已经影响到其他链路时,换。如果两者都不成立,继续轮询没有任何问题。接入同学要做的,是把这个判断写进文档,而不是凭感觉切。

关键词 实时推送 轮询 WebSocket 体育数据API 接入指南 全速数据 连接保活 事件延迟
全球赛事覆盖

200+ 国家/地区,1000+ 赛事

实时稳定可靠

实时更新,7×24 持续服务

专业技术支持

7×24 小时响应,有问必答

多语言多时区

多语言字段,按时区输出