gRPC 流式 vs REST 轮询:何时真正重要
了解在何种情况下 gRPC 流式传输 比 REST 轮询 更合适,包含实时仪表盘和进度更新的清晰示例,以及移动端与防火墙注意事项。

问题:请求更新 vs 接收更新
轮询意味着客户端不断向服务器询问更新,通常按定时器(每 1 秒、5 秒、30 秒)发起请求。
流式传输意味着客户端建立一条连接,服务器在有更新时持续发送,而不需要等待下一个请求。
这一点差别解释了为什么在小型演示里流式和轮询看起来相似,但在真实产品中行为会大不相同。轮询要求你事先权衡:更新越快,请求越多。流式则是保持一条通道,只有在实际发生变化时才发送数据。
在实践中,有几件事情会因此改变:
轮询的数据新鲜度取决于你选的间隔,而流式可以感知接近瞬时的变化。轮询也产生大量“无变化”的响应,这会增加双方成本(请求、头部、认证检查、解析)。在移动端,频繁轮询会更常唤醒无线电,耗电和消耗数据。由于轮询是采样状态,它可能错过间隔之间的短暂变化,而设计良好的流可以按顺序传递事件。
一个简单例子是显示新订单及其状态的实时运维仪表盘。每天业务不忙时每 10 秒轮询可能没问题。但当团队希望在 1 秒内看到更新时,轮询要么显得滞后,要么开始猛砍服务器。
并非所有应用都需要实时。如果用户偶尔查看页面(例如每月报告),每分钟轮询或按需刷新通常是最简单且最合适的选择。
何时轮询开始带来痛点
轮询看起来直观:客户端每隔 N 秒问一次“有新东西吗?”。当更新稀少、用户数少或迟几秒无关紧要时,它能很好地工作。
痛点在于你需要频繁的新鲜度、很多用户,或两者兼备时。
实时仪表盘是经典案例。想象一个运维屏显示未处理工单、支付失败和告警。如果这些数字每几秒就变动,轮询要么滞后(用户错过峰值),要么猛砍你的 API(服务器不断回答“未变”)。
进度更新是另一个常见陷阱。文件上传、报告生成和视频处理往往耗时数分钟。每秒轮询虽然让界面看起来“实时”,但会产生大量额外请求且仍感觉跳跃,因为客户端只能看到快照。
不可预测的到达也让轮询浪费。聊天、支持队列和新订单可能会静默 10 分钟,然后在 30 秒内爆发。轮询在安静期照样付费,而在爆发期仍有延迟风险。
物联网类的信号把问题放大。当你跟踪设备在线/离线状态、最后在线时间和小量指标时,成千上万的微小变化会累积。轮询会把这些乘成稳定的请求流。
当你看到类似的模式时,轮询通常已经开始成为问题:团队把间隔降到 1–2 秒以显得响应;大多数响应没有更新却仍耗费头部与认证;服务器负载随打开的标签页增加而非真实数据变化;移动用户抱怨电量和流量;用户打开仪表盘时出现流量激增,而非业务事件发生时。
为什么流式在实践中能胜出
流式的主要好处很简单:你不再反复问服务器同一个问题,而答案通常是“未变”。有轮询时,应用按定时器发送请求,只为发现没更新,这造成了浪费的流量、额外解析和更多超时风险。
流式中,服务器保持一条连接,仅在有变化时推送新数据。如果订单状态更新、指标越过阈值或后台任务从 40% 到 41%,更新可以立刻展示,而不必等下一个轮询窗口。
更低的延迟不仅是速度问题,它还改变了界面的感觉。轮询常带来明显的“跳跃”:加载器出现、数据成批刷新、数字突变。流式往往产生更小、更频繁的更新,显得更平滑、更可信。
流式还可以让服务器端的工作更容易理解。轮询通常每次返回完整响应,即使 99% 与上次相同。流式可以只发送变更,这意味着更少字节、更少重复的数据库读取和序列化。
实践上的对比是:轮询产生许多短请求,经常返回“无新内容”;流式使用一条长连接,仅在需要时发送消息。轮询的延迟受你选择的间隔限制(2 秒、10 秒等);流式延迟与事件本身相关(事件发生即用户看到)。轮询响应常是完整快照,而流可以发送小的增量。
回到实时工单仪表盘:5 秒轮询要么在安静期浪费调用,要么接受仪表盘总是滞后几秒。流式让安静期真的保持安静,一旦工单到达,UI 能立刻更新。
人们实际使用的流式模式
当人们想象流式时,常会设想一个“大”且“实时”的连接可以解决一切。实际上,团队使用一些简单模式,每种模式对应不同类型的更新场景。
1) 服务器到客户端的流(下行)
这是最常见的模式:客户端发起一次调用,服务器持续发送新消息。适合任何需要观察变化的屏幕。
实时运维仪表盘就是例子。浏览器不再每 2 秒问“有新订单吗?”,而是服务器在新订单到达时立即推送更新。许多团队也会发送心跳消息,以便 UI 显示“已连接”并更快检测断连。
同样思路适用于进度更新。如果某个报告需要 3 分钟,服务器可以流式发送里程碑(排队、10%、40%、生成 PDF、完成),这样用户看到进展但不会对服务器造成轰炸。
2) 客户端到服务器的流(上行)
这里客户端在一次调用里高效发送大量小事件,服务器在结束时或之后只回一次综述。适合突发数据场景。
比如移动应用采集传感器数据,或 POS 应用缓存离线操作。有网络时,它可以以流的方式批量发送事件,开销比成百上千个单独 REST 请求小。
3) 双向流(双向)
用于双方都可能随时发言的持续对话。调度工具可以向现场应用发送命令,而现场应用同时流式返回状态。多人协作(多人编辑同一记录)也适合这种模式。
当结果是单次答案、更新稀少,或你需要最简单的缓存、网关与监控路径时,请求-响应仍是最佳选择。
如何逐步决定与设计
先写下哪些内容必须立即在屏幕上变化,哪些可以等几秒。大多数产品只有一小块“热点”:实时计数器、进度条、状态徽章。
把更新分成两类:实时(real-time)和“稍后也可以”(good enough later)。例如,支持仪表盘可能需要新工单立即出现,但每周总计每分钟刷新一次也没人会注意。
然后给事件类型命名并保持每次更新尽量小。若只有一个字段变了,就不要每次发送整个对象。一个实用方法是定义 TicketCreated、TicketStatusChanged、JobProgressUpdated 这样的事件,每个只包含 UI 需要的字段。
一个有用的设计流程:
- 标注每个 UI 元素的最大延迟(100 ms、1 s、10 s)。
- 定义事件类型及每种事件的最小载荷。
- 决定客户端断连后如何恢复(完整快照,或从游标继续)。
- 为慢客户端设定规则(批处理、合并、丢弃旧更新或降低频率)。
- 选择当流不可用时的回退方案。
重连行为是很多团队卡住的地方。一个稳妥的默认策略是:连接时先发送快照(当前状态),然后发送增量事件。如果支持断点续传,包含一个像“last event id” 的游标,让客户端能请求“把 18452 之后的都发给我”。这让重连更可预测。
背压(backpressure)就是“如果客户端跟不上怎么办”的问题。对于实时仪表盘,通常可以合并更新。如果进度从 41%、42%、43% 快速变化,手机忙时你可以只发送 43%。
也要规划回退以保持产品可用。常见选择是临时切到每 5–15 秒轮询,或给不太关键的屏幕一个手动刷新按钮。
如果你在 AppMaster 中构建,通常会有两条路径:一条事件驱动的流程用于“热”更新,另一条标准 API 读取用于回退快照。
真实示例:实时仪表盘与任务进度更新
想象一个显示 200 个 SKU 库存水平的仓库仪表盘。用 REST 轮询,浏览器可能每 5 秒调用 /inventory,收到完整 JSON 列表并重绘表格。大多数时候没有变化,但你仍付出代价:重复请求、重复完整响应和重复解析。
用流式,流程翻转。客户端打开一条长连接。它首先收到初始快照(让 UI 立即渲染),然后只有在某项发生变化时才收到小更新。
典型的仪表盘视图变为:
- 初始状态:SKU 的完整列表、数量和每行的“最后更新”时间戳。
- 增量更新:仅包含变动的行(例如 SKU-184 从 12 变为 11)。
- 新鲜度信号:一个全局的“数据截至时间”,让用户信任所见内容。
再加一个屏幕:长时间运行任务,如导入 CSV 或生成月度发票。轮询常产生尴尬的跳跃:0%、0%、0%、80%、完成。流式让它看起来更真实、更平稳。
进度流通常发送小而频繁的快照:
- 完成百分比(0 到 100)
- 当前步骤(“Validating”、“Matching”、“Writing”)
- 估算完成时间(尽力而为且可变)
- 最终结果(成功、警告或错误信息)
对库存来说,增量(deltas)很棒,因为它们很小。对任务进度来说,快照通常更安全,因为每条消息本身就小,且如果客户端重连错过消息,快照能减少混乱。
在类似 AppMaster 的平台中,这通常对应一个读取模型(初始状态)加事件式的更新(增量),让 UI 在不砍 API 的情况下保持响应。
移动客户端有哪些变化
在手机上,“持续连接”与桌面不同。网络会在 Wi‑Fi 与蜂窝间切换,隧道会重置,用户会走进电梯。大的变化是你不再把注意力放在单次请求,而是把它们看作随时可能消失的会话。
要预期断连并为安全重放做设计。一个好的流包含类似“last event id”的游标,让应用在重连时能说:“从这里继续”。否则用户会看到重复更新(同一步骤两次)或缺失更新(从 40% 跳到 90%)。
如果消息小且有意义,流式往往更省电,因为应用避免了持续唤醒去轮询。但前提是不要每秒发送整个对象——那是快速耗电与耗流量的做法。优先发送紧凑事件,例如“order 183 status changed to Shipped”,而不是每次重发整个订单。
当应用在后台时,流式通常会被操作系统暂停或杀掉。要有明确回退:展示最后已知状态,前台时再刷新。对紧急事件使用平台推送通知,并在用户点开通知时让应用打开并重新同步。
移动仪表盘与进度更新的实用做法:
- 用退避重连(每次失败后等待更久),避免在信号差时耗电。
- 包含事件 id 或时间戳,并让更新具备幂等性,避免重复破坏 UI。
- 在合适时发送增量,并对低优先级更新做批处理。
- 连接时发送快照以保证 UI 正确,然后应用实时事件。
- 做简单的版本控制(消息类型及可选字段),以便旧版客户端仍能工作。
在 AppMaster 中构建移动应用时,把流当作“可用则优秀”,而不是“唯一事实来源”。UI 应该能在短期断连期间继续可用。
防火墙、代理和 HTTP/2 的陷阱
在纸面上流式看起来是显著优势,直到真实网络介入。关键差别在于连接:流式通常意味着一条长期存在的 HTTP/2 连接,这可能会触发公司代理、中间盒和严格的安全设置。
公司网络有时会做 TLS 检查(代理解密并重新加密流量)。这可能破坏 HTTP/2 协商、阻止长连接或在不易察觉的情况下降级行为。症状包括随机断连、流无法启动,或更新不是平滑到达而是集中成批到达。
经典 gRPC 要求 HTTP/2 支持。如果某个代理只支持 HTTP/1.1,调用可能会失败,即便普通 REST 能工作。这就是为什么浏览器环境常需要 gRPC-Web,以便通过更常见的 HTTP 基础设施。
负载均衡器、空闲超时与 keepalive
即便网络允许 HTTP/2,基础设施也常有空闲超时。长时间安静的流可能被负载均衡器或代理关闭。
常见修复:
- 设置合理的客户端与服务端 keepalive(不要太频繁)。
- 增加负载均衡器和反向代理的空闲超时限制。
- 在长静默期发送小心跳消息。
- 健壮地处理重连(可续传状态,避免重复事件)。
- 在客户端和服务端记录断连原因。
何时优先选 gRPC-Web 或回退方案
如果用户处于受限的公司网络,应该把流视为 best‑effort,并提供回退通道。常见做法是本地应用保留 gRPC 流式,而在像浏览器代理那样的网络环境下允许 gRPC‑Web(或短轮询的 REST)。
务必在用户实际使用的网络环境中测试:
- 有代理策略的公司内网
- 公共 Wi‑Fi
- VPN 连接
- 移动运营商网络
如果你把 AppMaster 部署到 AppMaster Cloud 或主流云提供商,务必做端到端验证,而不仅在本地开发环境验证。
常见错误与陷阱
最大陷阱是把流式当成默认。实时感觉很好,但它可能悄然增加服务器负载、移动端电池消耗和工单量。先严格限定哪些页面确实需要秒级更新,哪些可以每 30–60 秒刷新。
另一个常见错误是每次事件都发送完整对象。推送 200 KB JSON 每秒的实时仪表盘在流畅时很爽,但第一个繁忙小时就会被掏空。优先小增量:"order 4832 status changed to shipped",而不是“所有订单再发一次”。
安全问题比人们承认的更常被忽略。长连接也需要严格的认证与授权检查,并且要考虑令牌在流中期过期的情况。如果用户失去项目访问权限,服务端应立即停止发送更新。
重连行为是很多应用在真实世界中崩溃的地方,尤其是移动端。手机在 Wi‑Fi 与 LTE 之间切换、睡眠和被后台化。以下习惯能防止最坏情况:假设会断连;从最后见到的事件 id(或时间戳)恢复;让更新幂等以便重试不重复;为慢网络设定明确的超时与 keepalive;当流失败时提供降级模式(降低刷新频率)。
最后,团队上线流式却没有可视化监控。这会失去对问题的发现能力。追踪断连率、重连循环、消息延迟和丢失更新。如果服务器端显示任务已 100% 完成但客户端卡在 70% 长达 20 秒,你需要指标来显示延迟源头(服务端、网络还是客户端)。
在选择流式前的快速检查清单
定义“实时”对你用户的含义。
先看延迟。如果仪表盘需要感觉实时,低于 1 秒的更新可以证明流式的价值。如果用户只需要每 10 到 60 秒刷新一次,简单的轮询通常在成本和复杂度上更优。
然后看扇出(fan‑out)。被很多人同时观看的单一数据源(墙屏上的运维仪表盘加上 50 个浏览器)会把轮询变成不间断的背景负载。流式能减少重复请求,但你仍需处理大量打开的连接。
一个快速决策清单:
- 变化必须多快展示:小于 1 秒、约 10 秒还是约 1 分钟?
- 会有多少客户端同时观看同一数据、并持续多长时间?
- 客户端离线 30 秒应如何处理:显示陈旧数据、缓冲更新还是重载状态?
- 你的网络路径能否端到端支持 HTTP/2,包括代理和负载均衡器?
- 如果流在生产中被阻断,你有安全的回退(如临时轮询)吗?
还要考虑失败与恢复。流式一旦可用很棒,但难点在于重连、丢失事件与保持 UI 一致。实用的设计是把流式作为快速路径,但定义一个重同步动作(一次 REST 调用)来在重连后重建当前状态。
如果你只是快速原型一个仪表盘(例如在 AppMaster 的无代码 UI 中),可以尽早应用此清单,这样在真正理解更新需求之前不会过度构建后端。
下一步:先试点小范围流,再安全扩展
把流式当作需要赢取的能力,而不是开关式的切换。选一个能明显体现新鲜度价值的场景,其他保持原状直到你拿到数据。
从单条高价值流开始,例如长任务的进度更新(文件导入、报告生成)或仪表盘上的一个卡片(今日订单、活跃工单、当前队列长度)。把范围保持小也更容易用真实数据与轮询进行对比。
一个简单的试点计划:
- 定义成功标准:目标更新延迟、可接受的断开率,以及移动端的“足够好”标准。
- 发布最小流:一种消息类型、一个屏幕、一个后端端点。
- 衡量基础指标:服务器 CPU 和内存、打开连接数、消息延迟、重连频率和客户端电池影响。
- 添加回退:如果流失败或网络阻断,自动降级为较慢的轮询模式。
- 小心扩展:在你能解释指标之后再增加字段或屏幕。
把回退作为刻意的设计。某些公司网络、老旧代理或严格防火墙会干扰 HTTP/2,移动网络在应用后台时不稳定。优雅降级可以避免空白屏与支持工单。
如果你想在不写大量自定义代码的情况下交付,AppMaster (appmaster.io) 可以帮助你快速构建后端逻辑、API 和 UI,然后随着需求变化迭代。先小范围试点,证明价值,再在确实优于轮询的地方增加流式。


