我写了三年前端,第一次被 gRPC 的 proto 文件整懵了

3 阅读7分钟

我写了三年前端,平时和后端打交道的方式特别简单:fetch 一个地址,拿到 JSON,渲染到页面上。我一度以为全世界的接口都是这么工作的。直到上个月,后端的同事跟我说,他们把两个服务之间的内部调用从 REST 换成了 gRPC,顺手把一个 user.proto 文件甩给了我,说前端这边真要直连也得先看看。

我盯着那个文件看了好一会儿,没太看懂,又不好意思问,怕显得自己太菜。

我以为接口就是 fetch 一个 JSON

入行这三年,我脑子里关于接口的画面非常固定。浏览器 Network 面板里点开,看请求参数,看返回的 JSON,哪个字段不对就改哪个。出错了也是肉眼能查的,字符串嘛,谁都看得懂。

所以第一次听说 gRPC 的时候,我本能反应是:这不还是远程调用吗,能有多不一样。后来才发现,不一样的地方刚好是我完全陌生的那部分。它不是给人眼看的文本协议,是二进制;它不靠你手敲 URL,而是先得有一份接口契约,再用工具把代码生成出来。

第一次看到 proto 文件我有点慌

那个 user.proto 开头写着 syntax = "proto3",下面是一堆 message 和 rpc。我看到这么一段:

message UserRequest { int64 id = 1; }

message UserResponse { int64 id = 2; string name = 3; string email = 4; }

我卡在了那个数字上。id 后面为什么要跟个 1,name 后面跟个 3,这些编号是干啥的。同事说那是字段标签,跟书写顺序无关,序列化的时候靠它认字段。我嗯了两声,其实没完全懂,但不敢再问了,怕暴露自己连这种基础概念都没有。

syntax = "proto3" 那行我后来才查明白,是在声明用第几版语法,老项目可能还在用 proto2,两者字段规则不一样。这种边角知识,平时写业务一辈子用不上,真碰到了才发现自己的空白有多大。

rpc 这两个字母我也没反应过来。后来才知道它定义的就是一个远程方法,相当于我平时理解的那个接口。只是写法从直接写个函数,变成了先在一份契约里声明好,再让两端各自生成代码。

我那晚还偷偷去搜了下 proto3 和 protobuf 到底是什么关系,越看越迷。感觉自己写了三年业务,遇到真正的工程协议,基础这块是空的。

我让 AI 帮忙,结果更乱了

说实话,这种看不懂的东西,我第一反应就是丢给 AI。我把 proto 贴进去,问前端怎么调这个服务。它确实给了我一坨 TypeScript,用 @grpc/grpc-web,还让我先装 protoc 或者用 buf 把 proto 编译成 JS。

问题来了。AI 给的步骤里默认我已经知道 buf 是什么、generate 之后文件落在哪、前端工程里怎么接。我跟着跑,第一步就卡在环境上,报了个错我也看不出所以然。我把报错粘回给 AI,它又给了我一段新命令,我照着跑,还是红的。来来回回三四轮,问题没解决,我对着终端越来越慌,最后干脆把窗口关了,假装这事没发生。

这种感觉最近特别频繁。AI 帮我写的代码越来越多,我反而越来越怕。怕哪天它生成的东西出了问题,我连看都看不懂,更别提改。前两周有个需求,AI 一口气给我写了两百行组件逻辑,我 review 的时候发现有个状态管理的地方我没见过,问它,它解释得头头是道,可我点头的同时心里是虚的。那种明明过了、其实没懂的感觉,比写不出代码还难受。

gRPC 只是把这个恐惧变得很具体:一份契约我都没读明白,生成的代码我当然也不敢碰。我想找人问,又怕同事觉得我都写三年了还不懂这个,挺丢人的。

我决定先跑通一个最小例子

后来我换了种方式。不再让 AI 替我把整件事做完,而是自己先把最小的那条路走通。我装了 buf,对着那个 user.proto 跑了一次 buf generate,真的吐出来一堆 TypeScript 文件。我点开看,发现它把 proto 里的 rpc GetUser 变成了一个我能直接调用的函数,传参和返回都按 proto 里的类型定好了。

那一刻我才算摸到门道。原来所谓强类型,就是这份契约同时管住了两端。后端改了字段,前端这边的生成代码马上就不匹配,编译期就能发现,而不是等上线了才在控制台看到 undefined。以前我们团队就出过这种事,接口字段改了没人通知前端,半夜告警才发现的。

我也顺手弄清楚了 gRPC 那四种调用方式。unary 是一问一答,跟我平时调接口差不多;server streaming 是客户端问一次,服务端一直往下吐数据;client streaming 反过来,客户端一直发,服务端最后回一个;bidirectional 是两边都能随时发。我们项目暂时只用到 unary,但知道还有另外三种在,心里踏实不少。

它快在哪,我亲眼看了

后端同事给我看了个对比。同样的业务逻辑,压测的时候 REST 平均延迟大概两百多毫秒就开始飘,gRPC 能压到几十毫秒。原因他讲得通俗:gRPC 跑在 HTTP/2 上,一个连接能同时跑很多请求,不用每次都握手;数据用的是 Protobuf,二进制,比 JSON 小一大截,解析也快。

我是写前端的,对加载慢这事儿格外敏感,所以他一说这个我马上就信了。我之前做列表页,一屏要并发十几个请求,Network 面板里红彤彤一片,那时候要是换成一条连接多路复用,估计能清爽很多。不过话说回来,那是我自己的前端调用,跟后端服务之间的通信不是一回事,别乱套。

我也记下了另一句:浏览器原生不支持 gRPC,前端要直连得走 grpc-web 代理。所以对我而言不是以后都用 gRPC 代替 REST,而是后端跟后端之间拿它提速,我这边该 REST 还是 REST,顶多加一层代理。别被一个快字冲昏头,场景不对硬上就是给自己找罪受。

我现在的心态

回头看,那份 proto 文件已经没那么吓人了。但 AI 带来的那点慌,还在。

我现在的做法是,AI 可以帮我生成,但我得自己先把契约读明白。gRPC 这种东西,如果我连 proto 都看不懂,生成的代码我根本不敢往项目里放,出了事也救不回来。人的价值可能不在写得快,而在出了问题知道去哪看,能不能判断这段代码靠不靠谱。这点能力,AI 暂时替不了,但得我自己去攒。

给同样在摸索的你

如果你也是写前端的,某天突然被甩来一个 .proto,看不懂太正常了。那几个字段编号、四种调用方式、还有代码生成这一步,认真跑一遍官方示例,一个下午够入门。

要是你也被 AI 搞得有点焦虑,别憋着,这事儿一点都不丢人。我到现在也没完全想通,所以特别想找几个同样在 frontend 和 AI 之间来回挣扎的人聊聊。你踩过的 gRPC 的坑,或者你被 AI 整懵的瞬间,都可以来跟我说说,互相壮个胆。