连接不是一根干净的管子 — 报进度、报日志、清单会长、线会断
这一章讲三件事: 一次跑很久的调用中间会发生什么、你能看到什么; 线断了怎么接上;以及清单太长或者中途变了怎么办。
它在全书链条里的位置: 第 05、06 章讲的都是「一问一答、马上有结果」。 这一章处理那条连接上除了 一问一答之外的所有东西。 它也是全书里书稿最不完整的一章——两个小节在原书里各只有三四行(见第 7 节)。
1. 四件事,四个答案
这一节先给全景:一件跑得久的活会带出哪几个问题。
回想第 05 章那次调用:发一条、等一下、收一条,一秒之内结束。 换成一次要跑三分钟的调用,四个问题立刻冒出来:
| 问题 | 用户会问什么 | 协议给的办法 | 本章第几节 |
|---|---|---|---|
| 它现在到哪一步了? | 「卡住了吗?」 | 服务器一路报进度 | §3 |
| 它中间出过什么状况? | 「为什么少了一个?」 | 服务器把自己的日志发过来 | §4 |
| 清单太长怎么办? | (用户看不见,但工具会莫名其妙消失) | 一次给一页,附一个接着取的记号 | §6 |
| 线断了怎么办? | 「白等三分钟?」 | 报出最后收到的那条,从那儿往后补 | §5 |
前两个和最后一个共用同一样东西,先讲那样东西。
2. 通知:不需要回话的那一类消息
这一节是本章主走查的第 1 步。
本 章主走查的输入:用户说「把项目里的说明文档全转成 PDF」, 一共 214 个文件,这次调用跑了 3 分钟。 214、3 分钟以及后面出现的所有具体数值,都是我们为演示编的,不是真实数值。 书里这几节没有给场景,只有方法名。
回看第 04 章那三种报文:请求带编号、回复带同一个编号、还有一种不带编号的。 第三种就是通知——发出去就完事、不等回复的那类消息。
书里说得很简练:协议允许服务器(客户端也一样)发送通知消息, 之后怎么处理这些通知,「由接收方自己决定」1。
最后半句很重要:「由接收方自己决定」意味着你可以完全不理它。 不理它不会报错,也不会卡住——你只是什么都看不到。
主走查的第 1 步是:客户端发出那条「转 214 个文件」的调用请求, 并且在请求里附上一个标识,表示「这一件活的进度请报给我」。 然后这条请求会挂在那儿三分钟。接下来的一切,都靠通知送回来。
3. 进度:每一步报一个数
这一节是主走查的第 2 步。
进度不是应用自己估着画的。它是服务器一条一条报回来的,每条里有三样:
| 里面有什么 | 值 | 说明 |
|---|---|---|
一个标识(progressToken) | conv-7 | 客户端在发请求时定的,用来认领这是哪一件活的进度 |
| 已完成量 | 1、2、3…214 | 必须一次比一次大 |
| 总量(可选) | 214 | 有它才画得出百分比;没有就只能显示「已完成 137」 |
| 一句话(可选) | 「正在转 chapter-03.md」 | 给人看的 |
客户端发请求 → { … , "_meta": { "progressToken": "conv-7" } }
↑ 这个标识就是「请报进度」的开关,不写就没有进度
服务器一路报 ← { "method": "notifications/progress",
"params": { "progressToken": "conv-7",
"progress": 137, "total": 214 } }
三分钟后回结果 ← { "id": …, "result": { … } }
图说:214 个文件、3 分钟,平均每秒转一个多一点。
如果没有这条进度线,用户面对的就是三分钟的空白。
这个数到底代表什么,由服务器说了算。 它可以按文件数报,也可以按已经处理了多少数据量来报, 甚至按它自己觉得合适的任何单位报——客户端只能照显。 书里在讲客户端那个发进度的方法时,也把这一点说得很直白: 这些值最终长什么样,取决于你连的是哪台服务器、它对进度更新的要求是什么2。
这里有一处必须点明的方向差异。 书里介绍的那个方法(
send_progress_notification()) 是客户端往服务器报进度;而上面这条走查用的是服务器往客户端报。 两个方向协议里都有,但书里只写了前者,后者是我们照官方规范补的3。 你在写客户端时真正需要的,几乎总是后者。