将OkCupid从REST迁移到GraphQL

2020-11-16 16:47:35

关于从REST API迁移到GraphQL API 1的好处,已经写了很多文章,但我们假设您已经确信了这一点。如果你想转换一个拥有数百万用户的网站,确保它的性能不会受到影响,而且真的不想搞砸:你是怎么做到的?

我们去年踏上了这段旅程,并活着讲述了这个故事!我们的GraphQL API现在是OkCupid的官方API,所有客户端都采用它:我们的iOS和Android应用程序,以及我们的桌面和移动网页单页反应应用程序。

所以,这就是我们如何解决这个庞大项目的。我将谈一谈我们构建的内容、我们用来测试我们发布的新代码的策略,以及在技术方面本可以做得更好的一些事情。免责声明:这篇文章更多的是关于过程,而不是代码本身;请稍后查看另一篇文章,内容是我们必须克服的性能问题,才能达到与以前的API相同的水平。

我们的GraphQL API已经投入生产一年半了,而我们在一年多前就停止向REST API添加新功能了。该图每分钟处理多达170k个请求,它由227个实体组成。

我们还没有完全弃用我们的rest API,但是如果您看一下请求量(我们已经添加了支持最受欢迎页面的实体),那么转换我们的客户端的过程已经过半,按实体数计算可能还不到一半。

由于这对我们来说是一个全新的技术堆栈和存储库(Node、Apollo Server、Docker 2),我们需要制定一个计划,在不中断生产的情况下验证其有效性。我们的流程是:

添加影子请求以调用新API,同时仍通过REST API获取数据。

我们于2019年1月初启动项目,1月28日发布影子查询,3月13日开始A/B测试,4月30日全面发布。因此,只需4个“简单”步骤,您也可以在“仅”4个月内就有一个生产中的图表!

我们决定把OkCupid对话页作为我们的试验台。在这个页面上,用户可以看到他们正在进行的对话列表,以及“相互匹配”列表(可以与之开始新对话的人):

选择一个允许您对站点的一些核心部分进行建模的页面非常重要;这将帮助您确定约定,充实数据模型的重要部分,为将来的工作创建更好的基础,并更好地验证概念。页面越“真实”,就越能帮助您了解新的API是否有效。

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

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

对于许多第一次进行模式设计的团队来说,这可能是一个具有挑战性的步骤-对我来说就是如此!小贴士:

做些调查。从GraphQL文档中的基本示例,到GitHub和Yelp的公共API,再到Relay的文档,关于模式的文章很多。在这里向阿波罗团队大喊一声;我们在这个阶段从他们那里得到了很大的帮助。

不用担心rest API如何格式化数据。最好将您的模式设计得更具表现力和习惯用法,而不是受到以前API返回的内容的约束。

保持一致。我们之前的API主要是Snake_case,但是有一些难看的组合词(例如,userid和displayName)。这是让你的域名更标准、更易读的机会,所以抓住它吧!

具体点。图表中的字段命名越准确,如果需要进行突破性更改,则迁移到新字段就越容易。例如,User.essaysWithDefaults比User.essays更好。

拿出你的研究成果,做一些对你的团队有用的东西。例如,在研究分页标准时,我很想使用Relay的规范,但发现它对边和节点等术语的依赖比我们希望在图中向客户公开的更具临床意义(相反,我们决定返回一个数据列表3)。

在让GraphQL向实际用户提供数据之前,我们使用影子请求在生产环境中测试了我们的系统:在目标页面上,用户从rest API请求数据,然后在显示REST数据(丢弃复制的数据)后从GraphQL请求数据。这让我们可以比较这两个API的性能,并在用户发现问题之前修复它们。

我们当然不是第一个想到这一点的人,但这对我们来说是非常重要的一步。我们这个API的第一个草案花费了几乎两倍于rest API的时间,这显然并不酷。发布影子请求使我们能够在不影响真实用户在站点上的体验的情况下对这些性能问题进行分类。

马上回来看一篇关于哪里出了问题的技术方面的帖子,以及我们是如何让GraphQL达到同等速度的。

最后一步是用真实用户测试新的API和旧的API!因为我们已经验证了响应时间与影子请求相似,所以我们对发布A/B测试很有信心。

你希望看不到变化的实验是棘手的,因为你试图证明什么都没有发生。因此,在这样的实验中,你追踪的数据本质上永远不会有意义,除非出了什么问题。

因此,与其寻找统计数据的显著变化,不如为您的实验设置一个持续时间;一旦达到这个持续时间,仍然没有看到显著变化,您就可以信心十足地启动了。对我们来说,这是一个月的运行时间(每组用户超过10万)。和…。啊,真灵!

没有一个初稿是完美的(至少对我来说,第二稿也是如此)。虽然发布API的过程进行得很顺利,但我们在发布之后学到了一些技术知识。

我们没有任何关于如何从GraphQL突变返回错误的结构,当我们意识到有问题时,我们已经有了向客户显示错误的强大的各种方式。一个看起来非常有趣的解决方案是标准化错误类型,我们可以在给定的突变有效负载中扩展该错误类型。这篇中等水平的帖子对好的错误风格进行了非常深入的总结。

当遇到涉及业务规则的产品特性时,将该逻辑添加到API层很有诱惑力,特别是如果您不是依靠另一个团队来实现它的话。

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

在使用图形一段时间之后,我们已经意识到业务逻辑在集中在后端时工作得最好,并且我们的图形的作用是以对客户端有意义的方式获取、格式化和呈现后端的数据。

总体而言,我们的流程运行得非常好;它使我们能够快速将一些东西投入生产,以验证我们的技术决策,在错误到达用户之前修复它们,并根据以前的API测试我们的更改。

如果您决定踏上类似的旅程,我们希望这份路线图会对您有所帮助。祝好运!。

感谢凯瑟琳·埃里克森(Katherine Erickson)、雷蒙德·孙(Raymond Sohn)和OkCupid网络团队阅读本文的草稿。

1.对我们来说,它归结为:一种让客户端与我们的数据交互更具表现力的方式,一种用更少的网络请求来检索数据的更高性能的方式,让我们的客户端在不更改API的情况下创建新功能的灵活性更高,只要稍微构建一下图形,就可以创建新的功能,以及一种正在迅速被采纳为API社区标准的技术。2.这是一个全新的项目,构建在一个新的存储库中,并与我们的后端和客户端代码库分开部署。它在Node上运行,使用阿波罗服务器和Express。我们的数据是由对初始版本的rest API的调用提供的,但后来我们转向使用GRPC直接调用后端。

该API与Docker一起部署:我们使用CI构建Docker镜像,并使用Docker Sarm协调将这些镜像发布到我们的web服务器。休向我们的运营团队提供了一个巨大的、真正巨大的呐喊,因为他组织了Docker Sarm和一个与之互动的启动脚本,以及大量的Docker经验和支持!还有情感上的支持。

我们在所有平台(桌面/移动网络、iOS和Android)上使用Apollo Client,并与Apollo Studio集成,以使用其Operation Registry实现安全性,并跟踪速度和现场使用统计数据。

3.我们觉得边和节点不太对劲,但是对分页游标的Relay描述非常准确。因此,我们对项目使用一个数据数组,并使用一个受Relay启发的PageInfo实体:

";";";一种描述分页数据页面时使用的常用格式。";";";键入PageInfo{";";";获取上一页结果的密钥(如果有)。";";";之前:字符串";";";获取下一页结果的密钥(如果可用)。";";";之后:字符串";";";一个布尔值,表示提供更多结果。";";";hasmore:布尔!";";";可用的结果总数。";";";Total:int!}";";";界面,用于确保分页结果包含有关当前页面的信息。界面PageResult{pageInfo:PageInfo!}";";";用户对话的分页列表。";";";";type ConversationConnection实现PageResult{data:[对话]!PageInfo:PageInfo!}扩展类型用户{#34;&34;此用户的对话列表。";";";对话(LIMIT:INT=20 BEFORE:STRING AFTER:STRING):ConversationConnection!}