写点什么

我们怎样把一个数百万用户的网站从 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:441995
用户头像

发布了 113 篇内容, 共 23.4 次阅读, 收获喜欢 277 次。

关注

评论

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

有道技术沙龙 | AI 语音交互技术在语言学习场景的实践

有道技术团队

人工智能

LeetCode题解:647. 回文子串,动态规划,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

微信小程序开发:绑定手机号获取验证码功能

三掌柜

小程序 3月日更

带你走进与千万数据通信者共成长的“家园”

华为云开发者社区

华为 开发者 网络 华为数据通信 社区

C语言中“野指针”、“悬空指针”是什么?

不脱发的程序猿

c 指针 编程之路 bug 3月日更

JVM笔记 -- 来,教你类加载子系统

秦怀杂货店

Java JVM 类加载 虚拟机

数据驱动业务:一张大屏掌控城市运行,效率提高95%

一只数据鲸鱼

物联网 数据可视化 智慧城市 智慧园区 智慧交通

TcaplusDB君 · 行业新闻汇编(3月17日)

TcaplusDB

数据库 nosql 后端 TcaplusDB Tcaplus

电商千万级交易的金手指:分布式事务管理

华为云开发者社区

微服务 事务 华为云 分布式事务管理 DTM

图解堆排序

Silently9527

Java 排序算法 堆排序

一招让Kafka达到最佳吞吐量

Kevin Wan

go kafka go-zero

架构师训练营第十一周作业 - 命题作业

阿德儿

寻找被遗忘的勇气(十七)

Changing Lin

3月日更

SDK 是如何存储事件数据的?

神策技术社区

ios 大数据 存储 数据采集 神策数据

阿里P8大牛亲自讲解!2021年Android网络编程总结篇,醍醐灌顶!

欢喜学安卓

android 程序员 面试 移动开发

怎么找属于自己最优的2B增长模型?

boshi

销售管理 SaaS 七日更

电影AI修复,让重温经典有了新的可能

华为云开发者社区

电影 华为云视频 AI修复 视频增强 经典

区块链溯源追溯系统开发方案,区块链公共服务平台建设

WX13823153201

区块链溯源追溯系统开发

朋友,你听说过跨域吗

河磨

spring CORS 跨域

上万字详解Spark Core(建议收藏)

五分钟学大数据

大数据 spark 28天写作 3月日更

【LeetCode】不同的子序列Java题解

HQ数字卡

算法 LeetCode 28天写作 3月日更

跟公司新招的这个“同事”搭档,工作搬砖太“自动化”了

华为云开发者社区

华为 AI RPA 自动化 员工

你遇到过哪些质量很高的 Java 面试?

张小方

Java 面试 阿里 薪资

沙龙报名 | 云计算进入多元架构,云原生时代的挑战与机遇

京东科技开发者

云计算 云原生

Oracle Sql性能优化

大数据技术指南

oracle 大数据 28天写作 3月日更

智慧公安二维码定位报警系统开发,微警务平台解决方案

源中瑞-龙先生

二维码定位报警系统开发 智慧公安 智慧公安扫码

拍乐云创始人&CEO赵加雨:深耕18载,打造全景式音视频服务

拍乐云Pano

音视频 WebRTC 在线教育 RTC 实时通信

“时间”都去哪儿了?性能调优分析方法与案例详解

京东科技开发者

数据库 客户端

第8周大作业

八达鸟

JDK8新特性 Fork/Join 的优化

Java小咖秀

Java java8 jdk8 forkjoin fork

EGG公链生态项目——EFTalk上的巴莱特定律

币圈那点事

区块链

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