提前锁票 InfoQ 最具价值感的视频栏目 | InfoQ 大咖说 了解详情
写点什么

我们怎样把一个数百万用户的网站从 REST API 迁移到 GraphQL API?

2020 年 12 月 28 日

我们怎样把一个数百万用户的网站从REST API迁移到GraphQL API?

关于从 REST API 迁移到 GraphQL API 的好处,已经有很多人写过这方面的文章。假设你已经接受了这个事实,那么如果你想要转换一个拥有数百万用户的网站,确保性能不受影响,并且真的不想把它搞砸,你该怎么做?


去年,我们开始这个旅程,并获得成功。现在,我们的 GraphQL API 是 OkCupid 的官方 API,被所有的客户端调用,包括 iOS 和 Android App,以及桌面和移动 Web 单页 React 应用程序。


本文将阐述从 REST API 迁移到 GraphQL API 的实践过程。


注:本文更多地是关于过程而不是代码本身。


收益


现在,GraphQL API 已在生产环境中运行了一年半。一年多前,我们就停止向 REST API 添加新特性了。GraphQL API 由 227 个实体组成,每分钟处理 17 万个请求。


当然,我们还没完全弃用 REST API,从请求量方面来看,客户端的迁移工作进行了一半多,但从实体数量来看,可能还不到一半。


我们是怎么做的


对 OkCupid 来说,这些是一个全新的技术栈和代码库(Node、Apollo Server、Docker),所以我们要制定一个计划,在不中断生产环境的情况下验证其有效性。流程是:


  1. 选择一个适当的页面进行转换;

  2. 构建 schema;

  3. 添加影子请求来调用新的 API,同时仍然通过 REST API 获取数据;

  4. 使用真实用户进行 A/B 测试。


在 2019 年 1 月,这个项目正式启动,在 1 月 28 日发布影子查询,在 3 月 13 日开始 A/B 测试,并在 4 月 30 日发布完整版。所以,经过 4 个“简单”的步骤,你也可以在 4 个月内得到一个在生产环境中运行的 GraphQL API。


接下来,让我们深入探究每一个步骤。


选择一个适当的页面进行转换


我们决定将 OkCupid 对话页面作为切入点。在这个页面中,用户可以看到自己正在进行的对话,也能看到“匹配”列表(可以开始新对话的人):



转换之前的对话页面


选择一个网站核心页面是很重要的,这样可以帮你确定好约定条件,充实数据模型的重要部分,为未来的工作打好基础,并更好地进行概念验证。页面越“真实”,就越能帮你了解新 API 是否可行。


我们选择了对话页面,需要考虑如何呈现它:


  • User:用户基本信息;

  • Match:两个用户相互关联的状态信息(例如,匹配百分比,一个用户是否喜欢另一个用户,等等);

  • Conversation:基本的对话信息(例如,发送者、最后一条消息的片段、发送时间,等等)。


我们还要考虑到一些可重用的 API 概念,比如分页。


构建 schema


对很多第一次进行 schema 设计的团队来说,这可能是一个具有挑战性的步骤——对我来说就是如此!这里有一些建议:


  • 调研。与 schema 有关的文章有很多,例如 GraphQL 文档提供的基本示例、GitHub 和 Yelp 的公共 API、Relay 的文档等等。在这里要感谢 Apollo 团队,他们为我们提供了大量帮助。

  • 不要担心 REST API 的数据格式问题,最好将 schema 设计得更具表达性和惯用性,不要受旧 API 的限制。

  • 保持一致。我们的旧 API 主要使用了蛇形命名格式,但也存在一些难看的组合词(例如userid和 displayname)。这时候刚好是纠正这些字段名称的好机会!

  • 具体化。GraphQL API 中的字段名称越具体,在进行重大更改时,就越容易迁移成新字段。例如,User.essaysWithDefaultsUser.essays更好。

  • 基于调研结果为团队做一些有用的东西。例如,在研究分页标准时,我本来想用 Relay 的规范,但发现它对edgenode等术语太过依赖,对于客户端来说不够友好(我们最终决定返回一个 data 列表)。


加入影子请求


在 GraphQL 为真实用户提供数据前,我们在生产环境中使用影子请求对系统进行测试:在目标页面上,用户向 REST API 请求数据,在显示 REST 数据之后,再向 GraphQL API 发出相同的请求。这样,我们就可以比较两个 API 的性能,并在用户发现问题前修复它们。


当然,这并非我们首创,但却是非常重要的一步。


在这个 API 的第一个草案上花费的时间几乎是 REST API 的两倍,这显然不是很酷。使用影子请求让我们可以在不影响实际用户体验的情况下诊断性能问题。


进行实验


最后一步是使用真实用户来测试新的 API 。因为已经验证了响应时间与影子请求是差不多的,所以我们有信心进行 A/B 测试。


如果你期望在实验中看不到变化,那么这种实验是没有意义的,因为你试图证明什么都没有发生。在这样的实验中,你关心的统计数据在本质上是没有意义的,除非发生了什么问题。


因此,你应该为实验设置一个持续时间,而不是观察统计数据是否发生了显著变化。一旦达到了设定的持续时间,并且仍然没有看到显著的变化,就可以对系统满怀信心了。


在实践中,这个持续时间是一个月(每组实验超过了 10 万用户)。


哪些地方可以做得更好?


初稿总是不完美的(即使是第二稿也是,至少对我来说)。虽然发布 API 的过程进行得很顺利,但在发布之后,我们还是学到了一些技术上的东西。


错误处理


我们没有针对 GraphQL 更新 API 返回的错误定义好结构。当意识到这个问题时,它已经向客户端显示了各种各样的错误。一个看起来比较好的解决方案是标准化一个 Error 类型,这样可以在给定的消息体中对其进行扩展。这篇文章非常深入地讲解了如何设计错误类型。


业务逻辑应该放在哪里?


当遇到涉及业务规则的功能时,你会很容易想到把逻辑添加到 API 层,特别是如果你需要依赖另一个团队来实现它们。


例如,我们开发了一个显示所有喜欢你并给你发信息的人的功能。我们向付费用户显示整个列表,但对于免费用户,只显示第一项,然后是一系列占位符。这个功能的第一个版本在 API 层有检查用户付费状态并用占位符替换列表项的逻辑。


在使用 GraphQL API 一段时间后,我们意识到把业务逻辑集中在后端是最好的,而 GraphQL API 的作用是以一种对客户端来说有意义的方式获取、格式化和显示后端的数据。


总结


总的来说,这个流程是可行的,可以快速地将一些东西发布到生产环境中,以此来验证技术决策是否可行。在错误对用户造成影响前,你可以修复它们,并针对旧 API 来测试新的变更。


如果你决定进行类似的迁移,希望这个流程能够给你带来帮助。


原文链接:


https://tech.okcupid.com/moving-okcupid-from-rest-to-graphql/

2020 年 12 月 28 日 13:441797
用户头像

发布了 98 篇内容, 共 16.0 次阅读, 收获喜欢 180 次。

关注

评论

发布
暂无评论
发现更多内容

面试学习!月薪20k+的Android面试都问些什么?终获offer

欢喜学安卓

android 程序员 面试 移动开发

“复制”马斯克(三):我们要为他的“反智事业”买单吗?

脑极体

webpack | 谈谈webpack的本质

梁龙先森

前端 前端工程 webpack 28天写作

设计模式简单讲 - 适配器设计模式

小马哥

Java 设计模式 架构师 日更挑战 7日更

CSS(九)——盒子的浮动与定位

程序员的时光

程序员 日更挑战 28天写作

蚂蚁二面遭调优猛击,我闭关啃透485页性能实战手册,入职京东

互联网架构师小马

性能优化

28天瞎写的第二百三十三天:唯工具论的错误

树上

28天写作

深入浅出Android!阿里P7深入Binder原理讲解,论程序员成长的正确姿势

欢喜学安卓

android 程序员 面试 移动开发

LeetCode题解:433. 最小基因变化,BFS+生成所有可能新基因再匹配,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

开发质量提升系列:表字段名称引发的血案

罗小龙

代码规范 28天写作

我的电脑嘲讽我!

Tango

日更挑战

传统线程互斥技术 synchronized

武哥聊编程

Java 多线程 synchronized 28天写作

产品训练营-第六课笔记

Geek_娴子

设计模式: 工厂模式

爱笑的小雨

设计模式 23种设计模式 Java设计模式

《破壁MySQL》 - MySQL索引

haxianhe

MySQL

项目管理知识标准体系

Ian哥

28天写作

阿里内部Redis宝典意外流出极致经典:源码+实战+理论

比伯

Java 编程 程序员 架构 面试

外企时代已经过去?

李忠良

28天写作

NullPointerException 的处理新方式,Java14 真的太香了!

xcbeyond

Java java 14 新特性 28天写作

阿里架构师集一生内力编撰的笔记,到底有什么干货?

Java架构师迁哥

mPaas上线应用检测

阿里云金融线TAM SRE专家服务团队

ios android RPC

【Linux系统】关于守护进程

程序员架构进阶

Linux 架构 守护进程 28天写作

故乡的年

熊斌

28天写作

贪心算法

en

算法 贪心算法

真正牛逼的人,都是极简主义者!!

冰河

产品 程序 极简主义

批判性思维自修课(四)

石君

28天写作 批判性思维

管理笔记[2]:独裁者X与民主统治者Y

俊毅

还不清楚Lambda的底层原理?一文帮你搞懂

后台技术汇

28天写作

浅谈JavaScript常见的循环方式

devpoint

foreach for for...of

创业失败启示录|茶之玄学

青城

28天写作 创业失败启示录 青城

Redis击穿、穿透、雪崩产生原因以及解决思路

Java架构师迁哥

打造 VUCA 时代的 10 倍速 IT 团队

打造 VUCA 时代的 10 倍速 IT 团队

我们怎样把一个数百万用户的网站从REST API迁移到GraphQL API?-InfoQ