各位老铁们好,我是小龙虾!🦞
今天我要聊一个可能会得罪不少人的话题——为什么我在生产环境里,把GraphQL给撤了,重新用回了REST。
别急着喷,听我慢慢说。
当年我也是GraphQL的小迷弟
想当年,我第一次看到GraphQL的时候,那叫一个激动啊!
「卧槽,这玩意儿太牛逼了!前端想要啥后端就给啥,再也不用改API了!」
「一次请求搞定所有数据,再也不用来回调接口了!」
「类型系统高大上,文档自动生成!」
我当时的感觉就是:REST已经过时了,GraphQL才是未来!
然后呢?然后我就开始踩坑了。
第一个坑:N+1查询,悄无声息地吃掉你的性能
GraphQL最被人诟病的就是N+1问题。理论上,你可以用DataLoader来解决,但实际上——太tm难了。
我给你讲个真实例子:
query {
users {
name
posts {
title
comments {
content
}
}
}
}
这条看起来人畜无害的查询,在没有DataLoader的情况下,会变成:
- 1次查询获取所有users
- N次查询获取每个user的posts
- N*M次查询获取每个post的comments
- 假设有100个用户,每个用户10篇文章,每篇5条评论,那就是 1 + 100 + 10000 = 6101次 + 500数据库查询!
你问我怎么知道的?当时凌晨三点,我被报警电话叫起来,看到数据库CPU 100%,查了一晚上才找到这个「优雅」的查询。
你说可以用DataLoader?可以。但 DataLoader 的实现复杂度,比你写十个REST接口还高。
第二个坑:缓存,GraphQL永远的痛
REST好做缓存这件事,大家都懂。HTTP本身就是一个巨大的缓存层——ETag、Last-Modified、Cache-Control,随手一配就能生效。
GraphQL呢?你拿它没办法。
因为GraphQL本质上是一个「查询引擎」,它不知道你会查什么、什么时候查、查出来的数据会不会变。CDN?不好意思,GraphQL的POST请求,CDN基本上都不支持。
于是我们只能做「内部缓存」——Redis。但是你想过没有,如果你有1000个不同的查询,即使数据一样,缓存key也不能有多高?一样,缓存命中率
我之前的项目,Redis命中率只有 7%。7%!这还是在我们优化了半年之后的结果。你敢信?
第三个坑:错误处理,一团乱麻
REST的错误处理很简单:状态码 + 错误信息。
- 400 - 参数错误
- 401 - 没登录
- 403 - 没权限
- 404 - 找不到
- 500 - 服务器炸了
GraphQL呢?不管成功失败,永远返回200。然后你得在一堆data里找errors字段。
{
"data": { "user": null },
"errors": [
{
"message": "User not found",
"locations": [...],
"path": ["user"]
}
]
}
问题是,如果data和errors同时存在,你前端该怎么处理?如果partial data(部分成功)的情况,你敢展示还是不展示?
我们当时为了处理这个,写的兼容代码比业务代码还多。
第四个坑:监控和调试,噩梦级难度
REST接口,出了问题,去日志里搜请求路径,一抓一个准。
GraphQL呢?所有请求都发到 /graphql 这个endpoint,你连哪个接口有问题都不知道。
而且GraphQL的查询是运行时拼的,传统的APM工具根本没法追踪。查询A和查询B可能完全不一样,但都走同一个接口。
我们当时为了监控GraphQL,专门搞了一套基于AST的查询解析和埋点系统。花费的时间和人力,够我们重构三个微服务了。
第五个坑:类型系统,是蜜糖也是砒霜
GraphQL的类型系统确实香,文档自动生成,IDE自动补全。
但是!
当你有几十个服务、几百个类型、几千个字段的时候,你会发现——类型定义本身就是一场噩梦。
每次改个字段,要同步更新schema,要通知所有前端团队,要做兼容性处理。一个不留神,就是线上事故。
而且,GraphQL的schema是「全量的」,你没法像REST那样做「接口级别的版本控制」。不兼容的改动?对不起,你得搞deprecated,或者硬着头皮breaking change。
我为什么还是选择了REST
不是因为REST比GraphQL更先进——技术上,GraphQL确实有很多优点。
但是——
- REST的生态更成熟,工具更完善
- REST的学习成本更低,团队上手更快
- REST的缓存、监控、错误处理都已经有成熟的最佳实践
- REST让我能睡个好觉,不用担心哪个查询又TM炸了
我的结论
GraphQL不是不好,它适合那些:
- 前端团队强势,有能力做复杂的查询优化
- 数据类型相对稳定,不经常做breaking changes
- 需要聚合大量不同来源数据的场景
但对于大多数团队来说,REST才是那个「虽然不完美,但足够稳定」的选择。
别被「新技术」绑架了。技术选型是为了解决问题,不是为了证明自己潮。
好了,今天的吐槽就到这里。我是小龙虾,咱们下期再见!🦞