想象你走进一家分工精细的餐厅:前台接待、传菜部、后厨热炒、面点房各司其职。这家餐厅接待两拨客人——大堂的散客看着印刷菜单,用自然语言报菜名,服务员把需求逐字抄在单子上传给后厨;而 VIP 包间的熟客则打一条专属热线,双方拿着同一张“订购表单”模板,报几个数字编号,后厨瞬间就知道要做什么。这里的“菜单点菜”就是 REST 风格:接口文档公开、报文是人类可读的 JSON 文本、每一单都完整自包含;而“表单热线”就是 gRPC:契约文件预先约定字段、报文用 Protocol Buffers 压缩成二进制、一条长连接上多路请求同时飞驰。微服务架构正是这样一家餐厅——订单、支付、库存各自是独立档口,档口之间如何高效传菜,直接决定了整桌菜的出品速度。本文就把这两条传菜通道的原理、差异与选型心法一次讲透。
对于 AI 从业者而言,理解 gRPC 与 REST 的选型逻辑,是构建高性能 AI 服务架构的基本功——无论是大模型推理服务还是特征平台,正确的通信协议往往比盲目堆算力更能立竿见影地降低延迟。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
通信协议概述
餐厅的后厨从不直接面对客人,所有菜品都靠传菜通道送达。微服务架构也是如此——单体应用被拆成订单、支付、库存等多个独立“档口”后,档口之间必须通过网络互相调用:下单要通知库存扣减,支付要回调订单更新状态。这条看不见的“传菜通道”就是通信协议,它决定了消息传得多快、多稳、多省。
选对传菜方式,对于规模化的系统意义重大。如果每个档口之间都各自拉一条私人通道,服务数量一多,通道总数会爆炸式增长;更重要的是,通道的“报文格式”直接决定了每道菜在路上的耗时和成本。REST 和 gRPC 正是当前最主流的两种传菜方式:前者像大堂点菜,依赖 HTTP 方法与 JSON 文本;后者像 VIP 专线,依赖 HTTP/2 与二进制契约。
从数学上看,n 个服务两两直连时,潜在调用关系的数量增长得很快,这让“如何高效复用通道”成为必须正视的问题。
回看餐厅的比喻:档口越多,传菜路线越复杂,聪明的老板不会让每个档口都重新发明传菜方式,而是统一设计两类通道——公开的大堂点菜通道和高效的 VIP 专线。接下来我们分别拆解这两种通道的内部构造。
- n 个服务参与协作的微服务独立进程数量,n 是任意正整数,代表系统规模。
- 组合数从 n 个服务里任选 2 个建立调用关系,组合计数给出全部可能连接的数量。
- 化简结果组合数展开后除以 2,得到全连接场景下的连接数公式,直观暴露了规模瓶颈。
REST技术特性
大堂点菜为什么流行?因为它“门槛极低”。菜单是公开印刷的,客人用自然语言就能交流,服务员把需求写成一张张独立的单子,后厨做完一单再处理下一单。REST 正是这套逻辑:它把业务数据抽象成“资源”,每个资源有一个 URL 地址,用 HTTP 方法表达操作意图——GET 是查看菜品、POST 是下单新菜、PUT 是整单替换、DELETE 是退菜。
REST 的报文载体通常是 JSON 文本。就像手写单子上的每一个字都清晰可辨,JSON 的字段名、层级结构对人和调试工具都完全透明,curl 一下就能看懂接口在传什么。与此同时,REST 强调无状态:服务员不记住你上一单点了什么,每一张单子都自包含全部信息。这对餐厅的好处是任何服务员都能随时接手,对应到系统就是服务器不保存会话,任意节点都能处理请求,横向扩容极其轻松。
REST 最大的“甜蜜负担”在于报文体积。每一次调用都要携带完整的 HTTP 头部和 JSON 文本,字段名反复出现,就像单子上每道菜都要把“宫保鸡丁,微辣,不要花生”写全。
大堂点菜胜在公开透明、人人能上手,但每张单子都“字斟句酌”地写全,传菜效率自然受限。当餐厅规模变大、点单频率变高时,老板开始思考:能不能给熟客开一条更高效的专线?
- 单次报文总量REST 请求每次传输的总字节数,是头部与正文两部分之和。
- HTTP 头部方法、路径、状态、Cookie 等元信息组成的文本头,每个请求都会完整携带。
- JSON 正文业务数据的文本载荷,字段名和结构冗余重复,数据量越大冗余越明显。
gRPC技术特性
VIP 专线和堂食点单的本质区别,在于“先约定,后传输”。老板给熟客发一张印刷好的订购表单,第一栏填菜品编号、第二栏填数量,双方都清楚“编号 17 等于宫保鸡丁”。gRPC 就是这条专线:它要求客户端和服务端先共同维护一份接口契约文件(IDL,即接口定义语言,通常以 .proto 结尾),里面定义好每个服务的接口、参数和字段编号;随后用工具自动生成各语言的代码,就像把表单印刷并分发到每个档口。
gRPC 的底层是 HTTP/2,这条“热线”支持多路复用——一条长连接上可以同时跑很多个请求,互不阻塞;还支持头部压缩,把重复的元信息压缩到极小。报文本体用 Protocol Buffers(简称 Protobuf)编码为二进制:字段不再写名字,而是用数字编号代替,数据被紧凑地拼成字节流。同样的数据,Protobuf 的体积通常只有 JSON 的四分之一到十分之一。
专线还有一个堂食没有的王牌——双向流式通信。就像电话两端可以同时说话,gRPC 支持客户端和服务端在一条连接上持续互相推送消息,特别适合 AI 推理场景中逐 token 流式输出、或数据同步中的增量推送。表单专线把“每次都说全”变成了“一次约定、反复高效使用”。
- 体积比值将同一数据分别编码,JSON 体积除以 Protobuf 体积,衡量压缩增益。
- 经验区间典型业务字段下,Protobuf 约为 JSON 的 1/4 到 1/10,故比值落在 4 到 10 之间。
对比分析维度
大堂点菜和 VIP 专线各有拥趸,真正的工程判断要落到四个维度的对比上。第一是性能与体积:专线的 Protobuf 二进制报文小、解析快,HTTP/2 多路复用又让大量请求共享一条连接,吞吐与延迟全面占优;大堂的 JSON 虽体积大,但胜在直观。第二是可读性:REST 的报文人类可直接阅读,浏览器里就能调试,接口文档天然自解释;gRPC 的二进制报文肉眼不可读,必须借助工具解析 proto 契约。
第三是语言互操作:两边都支持主流语言,但 gRPC 靠契约文件自动生成各语言 SDK,就像把表单模板翻译成多国语言,一致性强;REST 则依赖各团队手写客户端,风格容易漂移。第四是浏览器支持:REST 被浏览器天然兼容,而 gRPC 依赖 HTTP/2 和自定义帧,浏览器无法直接发起,需要 gRPC-Web 或网关转换。
从并发模型看,两者的总耗时差异在请求密集时会被放大。REST 在 HTTP/1.1 下通常串行请求,总耗时是各请求之和;gRPC 在 HTTP/2 下并发复用连接,总耗时不超过最慢的那一路。
对比下来没有完美的点菜方式:专线快但“看不见”,大堂慢但“摸得着”。聪明的工程师不会二选一,而是让两种通道在各自的位置上发挥作用。
- 单路耗时单个请求从发出到收到响应的完整时长,包含网络往返与服务端处理。
- 并发上限多路请求同时发送时,整体耗时逼近其中最慢一路,而不是所有路累加。
- 串行累加HTTP/1.1 下请求逐个排队,总耗时等于 n 个请求时长之和,差距由此而来。
选型决策指南
选型本质上是在回答:你的“客人”是哪类?如果是内部档口之间的高频协作——比如订单服务反复查询库存、推荐引擎频繁拉取特征——那就像 VIP 熟客,用 gRPC 专线能省下大量带宽和延迟;如果是面向外部第三方开发者,比如开放平台、公开 API,那就像陌生散客,必须给他们一本看得懂的公开菜单,REST 的通用性和可读性无可替代。
性能敏感场景是 gRPC 的主场:高吞吐、低延迟、弱网环境,二进制小报文和连接复用都能明显改善体验。而兼容性要求高的场景,REST 的宽松约束反而是优势——JSON 天然允许字段可选,客户端与服务端可以各自缓慢演进;gRPC 的契约虽严格,但一旦变更就需要谨慎管理版本。团队技术栈同样影响决策:REST 工具链遍地都是,学习成本几乎为零;gRPC 需要团队理解 proto 契约、掌握代码生成和调试工具,初期投入更高。
把决策要素量化,可以写成加权评分的形式,分数越高越倾向选择 gRPC。
选型就像给餐厅定菜单:大堂菜单和 VIP 表单都印在墙上,关键是让对的客人走对的门。多数成熟团队的做法是“内外有别”——内部服务用 gRPC 提效,对外开放用 REST 保通用。
- 选型评分综合得分 Score 越高,越倾向选择 gRPC;得分越低则回归 REST。
- 正项权重性能要求高、调用发生在内部、需要强类型契约,都是 gRPC 的加分项。
- 负项权重团队学习成本与既有生态改造代价是扣分项,权重 w4 通常取较小值。
工程实践技巧
表单专线虽高效,但一旦表单印错,后厨就会做错菜。gRPC 的工程实践同样围绕“把契约管好”展开。首先,proto 文件要统一管理:就像餐厅把所有表单模板放在一个柜子里,团队应把 .proto 文件放入独立仓库,统一版本化发布,各服务通过依赖引入,避免各处复制造成契约漂移。
字段编号的兼容性是 gRPC 实践中最容易踩坑的地方。Protobuf 编码时,字段靠编号识别而非名字,因此编号一旦定下就像表单单号:只增不减、绝不复用。新增字段必须使用比当前最大编号更大的编号;删掉的字段编号要永久保留并标记为 reserved,否则未来某天复用旧编号,新旧客户端会互相解析错乱。
错误处理与观测同样关键。gRPC 定义了标准状态码(如 NOT_FOUND、UNAVAILABLE),服务端返回状态码而非随意字符串,调用方据此做精确重试;配合超时传播和链路追踪,一次跨服务调用从哪一环变慢都能被快速定位。表单维护得越规范,餐厅出菜的稳定性就越高。
- 编号递增新增字段必须分配比当前所有字段号更大的编号,避免与旧字段冲突。
- 禁止复用已删除的字段编号不能再次分配,否则旧服务会把新字段误读成旧字段。
生态与未来
餐饮业不会停在纸质菜单和电话热线,通信协议同样在进化。云原生时代,gRPC 已成为服务网格(如 Istio、Envoy)中的一等公民:Kubernetes 集群内部的服务间通信大量采用 gRPC,网格侧车自动完成负载均衡、熔断与可观测性,专线变成了“智能总机”。
浏览器这道坎也正在被填平。gRPC-Web 规范让浏览器通过 HTTP/1.1 或 HTTP/2 与 gRPC 服务通信,配合 Envoy 等网关做协议转换,前端也能享受契约带来的类型安全。与此同时,HTTP/3 基于 QUIC 协议,在弱网环境下连接建立更快、队头阻塞更少,gRPC 未来有望平滑迁移到 QUIC 之上,专线在移动端和弱网场景会跑得更稳。
传菜通道的演化从未停止,但 REST 也不会退场。公开、可缓存、人类可读的价值在开放生态中依然稀缺,两种协议将长期并存、各司其职。对工程师而言,理解它们各自的底层逻辑,比追逐“谁取代谁”的争论重要得多。
AI 应用场景
大模型推理服务加速
阶段:模型部署与推理
解决的问题:LLM 推理是典型的流式高吞吐场景,传统 REST 轮询需要反复建立连接、逐次拉取完整 JSON 结果,token 级流式输出几乎无法实现。gRPC 的双向流式通信让客户端与服务端维持一条长连接,推理引擎逐 token 推送结果,首 token 延迟和带宽占用大幅下降。
具体案例:NVIDIA Triton Inference Server 等主流推理框架原生提供 gRPC 接口,AI 平台团队部署千亿参数模型时,客户端通过 gRPC 流式接口接收生成结果,配合 HTTP/2 多路复用支撑多路并发请求,将单路推理吞吐提升 2-3 倍。
内部特征服务平台
阶段:在线特征计算与获取
解决的问题:推荐系统每次请求需要同时拉取用户画像、物品特征、上下文特征等几十个特征,若用 REST 逐个请求则延迟累加严重。内部 RPC 框架基于 gRPC 契约先行、Protobuf 小报文的特点,一次并发调用即可聚合全部特征。
具体案例:某电商推荐团队将特征服务从 REST 迁移到 gRPC,proto 契约统一定义特征字段,特征量从平均 12KB 降到 1.5KB,P99 延迟从 180ms 降到 45ms,服务端连接数减少了 70%。
对外开放平台 API
阶段:AI 能力对外开放与集成
解决的问题:面向第三方开发者输出 AI 能力(如 OCR、语音识别、内容审核)时,外部调用方技术栈多样、期望低门槛接入,REST 的 JSON 可读性、浏览器兼容性和 HTTP 缓存能力是最大优势。
具体案例:某云厂商的 AI 开放平台对外提供 REST 风格接口,开发者通过 Swagger 文档即可调试,配合 API 网关完成鉴权、限流与计费;而平台内部的服务间调用则统一走 gRPC,形成“外部 REST、内部 gRPC”的经典分层。
Python 演示
# -*- coding: utf-8 -*-"""REST(JSON) 与 gRPC(Protobuf 风格) 编码体积及传输耗时对比演示""" import jsonimport time def encode_varint(value): """把无符号整数编码为 varint 字节序列""" out = bytearray() v = value while v > 0x7F: out.append((v & 0x7F) | 0x80) v >>= 7 out.append(v) return bytes(out) def make_field(field_no, wire_type, payload): """生成 Protobuf 风格的字段:tag(字段号<<3|类型) + payload""" tag = (field_no << 3) | wire_type return encode_varint(tag) + payload def proto_encode(user): """将用户字典编码为极简 Protobuf 风格二进制消息""" b = bytearray() # 字段1: 用户ID(int64, wire_type=0) b += make_field(1, 0, encode_varint(user['id'])) # 字段2: 姓名(string, wire_type=2, 长度前缀) name = user['name'].encode('utf-8') b += make_field(2, 2, encode_varint(len(name)) + name) # 字段3: 邮箱(string) email = user['email'].encode('utf-8') b += make_field(3, 2, encode_varint(len(email)) + email) # 字段4: VIP 标记(bool, wire_type=0) b += make_field(4, 0, encode_varint(1 if user['vip'] else 0)) return bytes(b) def json_encode(user): """将字典编码为紧凑 JSON 文本""" return json.dumps(user, ensure_ascii=False, separators=(',', ':')).encode('utf-8') def simulate_invoke(n, protocol): """模拟 n 次调用耗时:REST 串行,gRPC 单连接并发""" if protocol == 'rest': return n * 0.05 # 每次完整请求一个 RTT return 0.05 + n * 0.002 # 一次握手 + 并发传输每请求耗时 def main(): user = {'id': 10086, 'name': '张三', 'email': 'zhangsan@example.com', 'vip': True} p = proto_encode(user) j = json_encode(user) print('JSON 体积: %d 字节' % len(j)) print('Protobuf 体积: %d 字节' % len(p)) print('体积比: %.1f 倍' % (len(j) / len(p))) n = 200 t_rest = simulate_invoke(n, 'rest') t_grpc = simulate_invoke(n, 'grpc') print('REST 串行 %d 次耗时: %.2fs' % (n, t_rest)) print('gRPC 并发 %d 次耗时: %.2fs' % (n, t_grpc)) print('耗时比: %.1f 倍' % (t_rest / t_grpc)) if __name__ == '__main__': main()
def encode_varint(value):实现了 Protobuf 的 varint 变长编码,数值越小占用字节越少,这是二进制报文紧凑的核心。tag = (field_no << 3) | wire_type把字段编号和线类型合并进一个字节,让接收方能根据编号反查契约。b += make_field(2, 2, encode_varint(len(name)) + name)字符串字段采用“长度前缀 + 内容”的编码方式,解码时先读长度再读内容。json.dumps(user, ensure_ascii=False, separators=(',', ':'))生成去掉多余空格的紧凑 JSON,模拟生产环境的最小化输出。if protocol == 'rest': return n * 0.05模拟 REST 每个请求独占一次网络往返,n 个请求耗时线性累加。return 0.05 + n * 0.002模拟 gRPC 只在开始时握手一次,后续请求在同一条连接上并发传输。
常见误区
误区我听说 gRPC 性能碾压 REST,所以任何场景都应该优先用 gRPC,选 REST 就是落后。
正解gRPC 的优势体现在请求密集、报文较大、流式交互的场景。如果只是低频的小请求,比如每分钟调用几次且每次都只传一个 ID,REST 的简单直接反而更省心。就像只点一杯白开水,犯不上动用 VIP 专线——直接喊服务员反而最快。性能对比必须在具体负载模型下谈,脱离场景谈“碾压”没有意义。
为何常见:性能对比文章往往展示 gRPC 在压测中的巨大优势,读者容易忽略压测条件是高并发大报文,误把结论推广到所有场景。
误区Protobuf 和 JSON 差不多,只不过压缩了一下,换了个更紧凑的格式而已。
正解两者有本质区别。JSON 是自描述的文本格式,键名就在报文里,任何人拿到都能读懂;Protobuf 是依赖 schema 的二进制格式,报文里只有字段编号和值,没有契约文件(.proto)根本无法解析。就像一页填好数字的表单,不看表头模板,你根本不知道"17"是菜名还是数量。
为何常见:使用者看到 Protobuf“体积小、速度快”的表面特征,却没有意识到它“契约先行、编号定字段”的底层设计,导致在调试和版本演进时踩坑。
误区只有 gRPC 支持流式,REST 只能一请求一响应,做不了实时推送。
正解REST 生态也有流式方案,比如 SSE(Server-Sent Events)可以让服务端持续向客户端推送数据,HTTP 的 chunked 传输也能逐块返回内容。但它们是单向的、机制相对原始,无法实现客户端与服务端同时互发;gRPC 的四类流式模式(一元、服务端流、客户端流、双向流)是协议级的一等公民,实现复杂双向交互更优雅。
为何常见:很多人把“REST 默认不支持”误解成“REST 完全不能”,忽略了 HTTP 本身具备分块传输的能力,只是工程上用得少。
误区gRPC 也是基于 HTTP 的,浏览器里用 fetch 不就能直接调用了?
正解gRPC 依赖 HTTP/2 的二进制帧、Trailers 以及自定义的 gRPC 状态传递,浏览器原生的 fetch/XHR 无法构造这些帧,会被直接拒绝。要让浏览器调 gRPC 服务,必须借助 gRPC-Web 协议加 Envoy 等网关做转换,或者走 REST 兼容层。就像电话热线需要专用话机,浏览器这只“公共话筒”接不进专线。
为何常见:看到"gRPC over HTTP/2"就误以为它与普通 HTTP 请求等价,忽略了 HTTP/2 之上还有一层 gRPC 自定义的应用层协议。
误区反正 REST 报文人类可读,体积大点无所谓,带宽足够就万事大吉。
正解内部高频调用下,嵌套 JSON 很容易膨胀到 KB 级,字段名反复传输会显著消耗带宽和 CPU 解析时间。实测中,一个多层嵌套的用户对象 JSON 可以比 Protobuf 大 5-10 倍,当每秒调用上万次时,这直接转化为可观的机器成本和延迟。大堂点菜的单子写得太啰嗦,传菜阿姨也会累的。
为何常见:小规模系统里带宽冗余被低估,团队往往在流量上涨后才被迫优化,此时改造协议的成本已大幅提高。
面试题
Q1. gRPC 默认使用的数据序列化格式是?
- A.JSON 文本
- B.YAML 文本
- C.Protobuf 二进制← 正确答案
- D.XML 文本
答案:C。gRPC 默认使用 Protocol Buffers(Protobuf)作为序列化格式,它是一种依赖 schema 的紧凑二进制格式。JSON、YAML、XML 都是文本格式,体积更大、解析更慢。就像 VIP 专线用数字编号点菜而不是写全菜名。
Q2. gRPC 通信传输层基于的协议是?
- A.HTTP/1.1 短连接
- B.HTTP/2 长连接← 正确答案
- C.WebSocket 全双工
- D.裸 TCP 自定义帧
答案:B。gRPC 建立在 HTTP/2 之上,利用其多路复用、头部压缩和双向流能力。HTTP/1.1 不支持多路复用,WebSocket 与裸 TCP 都不是 gRPC 的传输底座。一条 HTTP/2 连接就像一条可多路并行的专线热线。
Q3. REST 中获取资源最常用的 HTTP 方法是?
- A.GET 查询资源← 正确答案
- B.POST 创建资源
- C.PUT 全量更新
- D.DELETE 删除资源
答案:A。GET 语义是安全且幂等地获取资源,是 REST 中最常见的查询方法;POST 用于创建、PUT 用于更新、DELETE 用于删除。就像大堂点菜时“查看菜单”是最基本的动作,对应 GET 查询。
Q4. Protobuf 中字段编号的核心作用是?
- A.决定 JSON 键名
- B.编码时标识字段位置← 正确答案
- C.控制服务访问权限
- D.记录字段修改日志
答案:B。Protobuf 编码时不传输字段名字,而是用字段编号在二进制流中标识“这是第几个字段”,接收端靠 .proto 契约把编号映射回字段名。字段编号一旦定下就只增不改,如同表单上的行号,行号一变整张表单就乱了。
Q5. gRPC 双向流式通信的核心特征是?
- A.仅客户端连续发送
- B.仅服务端持续推送
- C.双方可同时收发数据← 正确答案
- D.必须依赖 WebSocket
答案:C。双向流式通信允许客户端和服务端在同一条 HTTP/2 连接上同时、独立地发送多条消息,适合 AI 推理的流式输入输出。仅客户端发送是客户端流,仅服务端推送是服务端流,且 gRPC 不依赖 WebSocket。
Q6. 下列哪种场景更适合选用 gRPC?
- A.对外公开的第三方开放接口
- B.内部高吞吐的微服务调用← 正确答案
- C.面向浏览器的静态页面站点
- D.需要强缓存的多媒体接口
答案:B。内部高吞吐微服务调用是 gRPC 的典型主场——二进制小报文、连接复用、契约生成 SDK 都带来明显收益。对外接口需要通用性和可读性,浏览器场景需要 REST 的天然兼容,强缓存场景也依赖 REST 的 HTTP 缓存机制。
Q7. gRPC-Web 技术解决的核心问题是?
- A.替代 HTTP/2 多路复用
- B.让浏览器可调用 gRPC 服务← 正确答案
- C.压缩 REST 请求报文体积
- D.在浏览器端解析 JSON 更快
答案:B。gRPC 依赖 HTTP/2 二进制帧和 Trailers,浏览器原生无法构造,gRPC-Web 定义了浏览器可用的协议变体,配合 Envoy 等网关转换,让前端也能调用 gRPC 服务。它不替代 HTTP/2,也不负责压缩 REST 报文。
Q8. 下列属于 REST 风格核心特点的有?
- A.报文内容人类可读← 正确答案
- B.支持 HTTP 层缓存← 正确答案
- C.强类型 IDL 契约
- D.浏览器天然兼容← 正确答案
答案:ABD。REST 基于 JSON 文本,人类可读;利用 HTTP 缓存机制可减少重复传输;浏览器原生支持 REST 请求。强类型 IDL 契约是 gRPC 的典型特征,REST 恰恰缺少强制契约。这正是“大堂菜单”公开、通用、可缓存的特点。
Q9. gRPC 基于 HTTP/2 获得的核心能力包含?
- A.单连接多路复用← 正确答案
- B.HPACK 头部压缩← 正确答案
- C.报文明文可读
- D.双向流式传输← 正确答案
答案:ABD。HTTP/2 为 gRPC 带来多路复用(一条连接并发多个请求)、HPACK 头部压缩(减小元信息开销)和双向流式传输。报文明文可读是 REST/JSON 的特点,gRPC 的 Protobuf 报文是二进制不可直接阅读的。
Q10. 与 JSON 相比,Protocol Buffers 的特点有?
- A.编码后体积更小← 正确答案
- B.依赖 schema 契约← 正确答案
- C.人类可直接阅读
- D.序列化解析更快← 正确答案
答案:ABD。Protobuf 用字段编号替代字段名,省去大量文本冗余,体积通常只有 JSON 的 1/4 到 1/10,且二进制解析无需处理字符串结构,速度更快;但它依赖 .proto 契约,没有契约就无法解析,也不可读。人类可读是 JSON 的独特优势,就像印刷菜单人人能看,而填了数字的表单只有拿着模板的人才懂。