首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >车间数据大屏:五分钟一刷的数据管道

车间数据大屏:五分钟一刷的数据管道

原创
作者头像
用户4770330
发布于 2026-10-20 10:46:07
发布于 2026-10-20 10:46:07
850
举报

车间墙上挂了三块大屏,看的是当班产量、设备开动率和在制工单进度。一开始的实现很朴素:页面里一句 setInterval,五秒拉一次全量数据。二十来块屏加起来,光刷新就把接口调用量顶到了每天几百万次,数据库下午三点准时开始卡。后来重做了一遍,把刷新频率降到五分钟,把"拉全量"改成"带版本号取快照",问题才彻底消失。结论放在前面:大屏刷新的关键不在前端那一句定时器,而在服务端有没有一个稳定的快照版本,以及客户端能不能把刷新时刻对齐到统一的墙钟。

一、五秒一刷的代价

1.1 二十块屏拖垮一个接口

原来每块屏独立请求,五秒一次,一次拉回四个指标的全量明细。表面看只是屏幕上数字跳一下,实际每次请求要扫当天几十万行报工记录,做四次聚合。二十块屏在同一秒发起,聚合计算叠加,接口平均耗时从三百毫秒涨到四秒,超时之后前端重试,调用量再翻一倍。

1.2 三个被忽略的成本

这类实现通常只算了一笔账——带宽。实际有三笔。请求成本:每次刷新一次数据库聚合,屏幕越多越亏。渲染成本:全量数据重绘,图表实例每五秒重建一次,内存缓慢上涨,挂一天浏览器就卡。数据成本:五秒的粒度对车间管理没有意义,看的人关心的是"这一班进度怎么样",不是"这一秒比上一秒多做了几个"。

1.3 五分钟这个数字怎么来的

刷新频率是跟使用场景对出来的,不是拍出来的。产量数据由报工产生,工人一批活干完才扫一次码,间隔普遍在十分钟以上;设备开动率由采集网关上报,一分钟一条。取两者中较慢的一个再放宽一档,五分钟刚好。更密只会让人看到一堆不变的数字,反而失去信任感——屏幕老在跳,人就不看了。

二、管道的三层设计

2.1 服务端只出快照

服务端不再实时算,改成每分钟把四个指标算好写进快照表,配上自增版本号和生成时间。大屏请求时只带一个参数:我手上是哪个版本。版本一致就直接返回空变更,不一致才返回数据。这样即使十块屏同时刷新,服务端做的也只是读一张几百行的表,成本几乎为零。快照表按天分区,保留三十天,历史查询另走归档,不影响大屏读路径。

2.2 客户端按墙钟对齐

前端不用 setInterval,理由是它会漂移:页面开一天,定时器和对齐的时间能差出半分钟,多块屏之间的数字就对不上,站在两块屏中间看的人头一个反应是系统出错了。改成递归 setTimeout,每次执行完计算距离下一个整五分钟还有多久,按差值重排:

代码语言:javascript
复制
const REFRESH_MS = 5 * 60 * 1000;

function createPoller(api, render) {
  let timer = null;
  let offset = 0;        // 客户端与服务器的时钟差
  let seq = 0;           // 请求序号,丢弃过期响应
  let stopped = false;

  async function tick() {
    if (stopped) return;
    const mySeq = ++seq;
    try {
      const res = await api.snapshot();   // { version, serverTime, rows }
      if (mySeq !== seq) return;
      offset = res.serverTime - Date.now();
      render(res);
    } catch (err) {
      renderStale(err);                   // 保留上一帧,只把角标置灰
    } finally {
      scheduleNext();
    }
  }

  function scheduleNext() {
    const now = Date.now() + offset;
    const next = Math.ceil((now + 1) / REFRESH_MS) * REFRESH_MS;
    const delay = Math.max(1500, next - now);
    clearTimeout(timer);
    timer = setTimeout(tick, delay);
  }

  document.addEventListener('visibilitychange', () => {
    if (!document.hidden) scheduleNext();
  });

  return { start: tick, stop() { stopped = true; clearTimeout(timer); } };
}

两个细节值得说明。时钟差用服务端返回的时间戳反推,避免客户端系统时间被改过之后整块屏的刷新时刻全乱;异常时不清空画面,只把右上角的数据时间标灰,因为车间大屏忌讳白屏,人看到白屏会以为系统停了。

2.3 版本号省掉九成流量

带上版本号之后,绝大多数刷新只返回一个"无变化"的响应,几百字节。真正返回数据的只有每分钟快照刚生成的那一次。这块的能力后来是在搭贝上搭的看板页里直接对接的,页面本身不存数据,只认版本。四个指标的数据量加起来不到两百行,即便全量返回也很轻,但省下来的请求解析和重绘开销是实打实的。

2.4 断线之后要能自己回来

车间网络不稳定,交换机重启、网线被叉车压断都发生过。前端加了指数退避:连续失败时把重试间隔从五秒逐步拉到一分钟,成功一次立刻回到正常节奏。同时在页面顶部显示一个很小的连接状态点,绿色正常、黄色重试中、灰色已停止。没有这个点,屏幕上数字停住,没人知道是产量没变还是网络断了。

三、几个现场才发现的问题

3.1 屏幕休眠后定时器不准

大屏主机的浏览器在长时间无操作后会降频,定时器被拉长,一晚上过去能差出好几分钟。解法是监听页面可见性变化,从隐藏回到可见时立刻重排一次刷新时刻,并顺手补一次数据请求。这块逻辑很短,但不写就会出现"早上头一眼看到的是昨晚八点的数"。

3.2 图表实例不能每次重建

早期每次刷新都销毁旧图表再新建,跑一天下来内存翻了三倍,浏览器开始掉帧。改成图表实例只建一次,刷新时走数据更新接口,坐标轴范围变化时才重设。改动量不大,效果明显:连续运行七天,内存曲线是平的。

3.3 时间口径要写在屏幕上

出过一次争议:大屏上的当班产量和班组自己记的差了几十件,查了半天是班次切分时间不一致。之后所有大屏都在角落标了三个信息:数据截止时间、班次定义、统计口径版本。字很小,但争议少了一大半,因为看的人知道自己在看什么。

3.4 交接班与临时查看

大屏是给人看的,看的人会换班。我们在大屏旁边放了一张纸,写着三行操作说明:怎么切到工单进度页、怎么临时拉长时间范围、怎么重启。听着好笑,但值班的人换了三拨之后,这张纸省掉的电话比任何文档都多。另外,临时查看的需求不能靠改刷新频率解决,我们单独做了一个只读页,允许选日期和班次,刷新逻辑相同、频率不同,两边互不干扰。

真正值得写下来的一条是"别让大屏自己扛逻辑"。早期我们把车间的换算规则、班次判断都写在前端页面里,改一次口径要重新发布一次页面,还得挨个屏去刷缓存。后来把这些规则全部下沉到服务端的快照任务里,前端只负责取值和画图,页面已经三个月没改过。分工清楚了,维护的人不需要懂车间的班次口径,车间也不需要懂前端。

四、改完之后

改造上线,接口日调用量从几百万降到二十多万,数据库在下午的负载曲线平了。大屏连续运行一个月没重启过,值班的人说终于不用每天早上去拍一下主机了。真正省事的其实是运维:以前每隔两周就有人报"某块屏不动了",现在这类反馈基本消失,因为状态点会先告诉人问题在哪一段。硬件上也有个小改动:原来用的是普通办公主机,夏天机箱里积灰、风扇异响,三个月清一次;换成低功耗的小主机之后,连续运行一年没关过机,电费和维护都省了。

五、总结

大屏的刷新链路,本质是把"实时"这个词重新定义一次:业务需要的新鲜度是分钟级,就不该按秒级去实现。落地时记住三件事——服务端出快照不出明细、客户端按墙钟对齐而不是按页面加载时刻、异常时保留上一帧而不是清空。这三点做好,一块屏和二十块屏的成本差别不大,维护负担也不会随屏幕数量线性上涨。

常见问题

问:轮询和长连接推送,大屏场景该选哪个?

答:看刷新频率和数据变化的可预测性。五分钟一刷的场景,轮询足够,实现简单、断线自愈、不需要维护连接状态;秒级以下、变化频繁的场景才值得上推送。中间的折中是长轮询,服务端挂起请求直到有变更或超时,连接数可控,但要注意网关的空闲超时配置要大于挂起时长。

问:多块大屏怎么保证同一时刻显示同一份数据?

答:两个动作。刷新时刻对齐到统一的墙钟整点,而不是各自从加载时刻起算;服务端返回带版本号的快照,版本一致就复用,不重新计算。只要快照生成是原子的一次写入,同一时刻所有屏读到的就是同一版数据,差异通常出现在刷新前后的几百毫秒内。

问:大屏长时间运行内存一直涨,一般是什么原因?

答:多数是图表实例和定时器没有回收。每次刷新重建实例、每切换一次页面挂一个定时器,都会留下引用,垃圾回收器收不掉。排查方式是打开浏览器任务管理器看内存曲线,刷新几次观察是否阶梯式上涨。修复办法是实例只建一次、定时器统一登记并在页面卸载时清理,图表更新走数据接口而不是重建。

问:网络抖动时,前端重试策略怎么设计比较稳?

答:指数退避加随机抖动。首次失败等几秒,之后每次翻倍,设一个上限比如一分钟,成功一次就重置。加随机是为了避免所有屏幕在同一秒集中重试,把刚恢复的接口再打挂。同时把失败状态显式画在界面上,不要静默重试,否则故障时长没人说得清。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、五秒一刷的代价
    • 1.1 二十块屏拖垮一个接口
    • 1.2 三个被忽略的成本
    • 1.3 五分钟这个数字怎么来的
  • 二、管道的三层设计
    • 2.1 服务端只出快照
    • 2.2 客户端按墙钟对齐
    • 2.3 版本号省掉九成流量
    • 2.4 断线之后要能自己回来
  • 三、几个现场才发现的问题
    • 3.1 屏幕休眠后定时器不准
    • 3.2 图表实例不能每次重建
    • 3.3 时间口径要写在屏幕上
    • 3.4 交接班与临时查看
  • 四、改完之后
  • 五、总结
  • 常见问题
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档