如何通过计算查询复杂度来限制GraphQL APIs的速率

120 阅读6分钟

通过计算查询复杂度来限制GraphQL APIs的速率

速率限制是一个保护API稳定性的系统。GraphQL为速率限制提供了新的可能性。我将向您展示Shopify针对GraphQL Admin API的费率限制系统,以及它如何解决REST API中常用方法的一些限制。我将向你展示我们如何计算查询成本,以适应客户需要的数据,同时为服务器提供更可预测的负载。

什么是速率限制,为什么API需要它?

为了确保开发者有一个可靠和稳定的API,服务器需要执行合理的API使用。最常见的会影响平台性能的情况是:

  • 坏人通过发送过多的请求来滥用API
  • 客户端无意中以无限循环的方式发送请求,或以突发的方式发送大量请求

传统的速率限制API的方式是基于请求的,广泛用于REST APIs。其中一些有固定的速率(也就是客户允许每秒发出若干个请求)。Shopify Admin REST API提供了客户在每次提出请求时花费的点数,而这些点数每秒钟都会被重新填充。这使得客户可以保持一个请求节奏,从不限制API的使用(即每秒两个请求),并在需要时偶尔进行请求爆发(即每秒进行10个请求)。

尽管被广泛使用,基于请求的模式有两个限制:

  • 客户无论如何都会使用相同数量的点数,即使他们不需要API响应中的所有数据。
  • POST,PUT,PATCHDELETE 请求产生的副作用对服务器的负荷要求比GET请求更高,因为GET请求只读取现有数据。尽管在资源使用方面存在差异,但在基于请求的模型中,所有这些请求消耗的点数是一样的。

好消息是,我们利用GraphQL来克服这些限制,并设计了一个速率限制模型,更好地反映每个请求对服务器造成的负载。

GraphQL Admin API速率限制的计算查询成本方法

在计算查询成本的方法中,客户每秒钟获得50分,最高可达到1000分的限制。与基于请求的模式的主要区别是,每个GraphQL请求都有不同的成本。

让我们开始讨论我们对基于请求的模型所面临的挑战的方法。

根据其请求的数据量来定义类型的查询成本

服务器在执行GraphQL查询之前会对其进行静态分析。通过识别查询中使用的每个类型,我们可以计算出其成本。

对象:一个点

对象是我们的基本单位,值一个点。对象通常代表一个单一的服务器端操作,如数据库查询或对一个内部服务的请求。

标量和枚举:零分

你可能想知道,为什么标量和枚举没有成本?标量是返回一个最终值的类型。标量类型的一些例子是字符串、整数、ID和布尔。枚举是一种特殊的标量,它返回一组预定义的值中的一个。这些类型存在于已经计算了其成本的对象中。在一个对象中查询额外的标量和枚举,通常要付出最小的代价。

在这个例子中,shop 是一个对象,成本为1。id,name,timezoneOffsetMinutes, 和customerAccountsreturn 是标量类型,成本为0。总的查询成本为1。

连接:两点加上返回的对象的数量

连接在GraphQL中表示一对多的关系。Shopify使用符合Relay标准的连接,这意味着它们遵循一些惯例,例如通过使用edges,node,cursor, 和pageInfo 来复合它们。

edges 对象包含描述一对多关系的字段:

  • node:由查询返回的对象的列表
  • cursor: 我们在该列表中的当前位置

pageInfo 持有 和 布尔字段,帮助在列表中导航。hasPreviousPage hasNextPage

连接的成本是2加查询期望返回的对象的数量。在这个例子中,一个期望返回五个对象的连接的成本是7分。

cursor 和 ,是免费的,因为它们是连接对象已经完成的繁重工作的结果。pageInfo

这个查询的成本是7分,就像前面的例子一样。

接口和联盟:1分

接口和联合体的行为就像返回不同类型的对象,因此它们和对象一样需要花费1分。

变异:10分

突变是对数据库和索引产生副作用的请求,甚至可以触发Webhooks和电子邮件通知。更高的成本是必要的,以考虑到这种增加的服务器负载,所以它们是10分。

在GraphQL响应中获取查询成本信息

你不需要自己计算查询成本。API响应包括一个扩展对象,其中包括查询成本。你可以尝试在Shopify Admin API GraphiQL explorer上运行一个查询,看看它的计算成本的作用。

该请求响应包括由extension 对象显示的计算成本。

在GraphQL响应中获得详细的查询成本信息

你可以通过在你的请求中添加X-GraphQL-Cost-Include-Fields: true 标头,在extension 对象中获得详细的每个字段查询成本。

了解要求的和实际的查询成本

你是否注意到上面的查询有两种不同类型的成本?

  • 请求的查询成本是在使用静态分析执行查询之前计算的。
  • 实际查询成本是在我们执行查询时计算的。

有时,实际成本比请求成本小。这通常发生在你在一个连接中查询特定数量的记录,但返回的记录较少。好消息是,请求费用和实际费用之间的任何差额都会退还给API客户。

在这个例子中,我们查询了前五个低库存的产品。只有一个产品符合这个查询,所以即使请求的成本是7,你也只被收取了按实际成本计算的4分。

衡量计算查询成本模型的有效性

计算出的查询复杂度和执行时间有线性关系

通过使用查询复杂度计算规则,我们的查询成本与通过查询执行时间衡量的服务器负载成正比。这给Shopify提供了扩展我们的基础设施所需的可预测性,给合作伙伴一个稳定的平台来构建应用程序。我们还可以检测这种关联的异常值,并找到性能优化的机会。

通过计算客户查询或修改的数据量来限制GraphQL API的速率,比REST API常用的基于请求的模型更能适应每个API客户的使用情况。 我们计算的查询成本方法有利于API使用情况良好的客户,因为它鼓励他们只请求他们需要的数据,为服务器提供更可预测的负载。

其他信息