用GraphQL介绍有关性能相关的问题
你好,今天是Openlogi的Advent Calendar活动的第18天。
上次和上上次我写了关于GraphQL的实现。接下来继续讲解GraphQL。
在GraphQL中最常讨论的是关于无法通过REST API实现的灵活查询,但在实际应用中,需要克服一些障碍才能使用它。
如果要提出个人意见,那么认证、授权和绩效可能是其中的几个方面。在本文中,我打算介绍与绩效相关的方法和措施。
在REST的情况下,并没有特别困难来估计特定终端点的数据量和所需时间。但是,在GraphQL中,由于需要在客户端定义需要获取的数据,可能会执行非常高负荷的查询。
例如,在上一篇文章中,我举了以下的模式作为例子。
在这里,表示了存在用户和用户能够浏览的订阅内容。
type Query {
viewer: User
}
type User {
id: String
name: String
feeds: [Feed]
}
type Feed {
id: String
title: String
body: String
author: User
}
我写了这样的查询,考虑显示首页的时间线。
query {
viewer {
id
name
feeds {
title
body
author {
id
name
}
}
}
}
然而,对于这个架构,也可以编写这样的查询。(不考虑是否应该查看他人的动态列表。)
query {
viewer {
id
name
feeds {
title
body
author {
id
name
feeds {
title
body
author {
id
name
feeds {
// 以下無限ループ
}
}
}
}
}
}
}
这样一来,GraphQL可能存在着让数千、数万次数据库访问轻而易举地发生的危险。因此,我将介绍一些关于GraphQL性能的方法。
对查询进行验证
请求大小验证
首先,在底层的方法上,可以通过限制传递给请求的查询的大小来进行限制。简单地通过查询的大小来进行控制。
对于查询结构的验证。
如果需要更复杂的操作,还可以考虑查看查询的嵌套层次的方法。由于GraphQL查询可以转换为AST表示,因此限制其最大深度或者禁止特定结构的查询也是可行的方法之一。
执行超时操作
在每个Resolver的处理中,设置超时也是有用的。有关GraphQL查询超时和复杂性管理的详细信息,请点击 这里 。这是一种在超过一定时间后停止处理并阻止后续Resolver触发的方法。
持续查询
問題是可能收到未预料的查询。因此,我们采取的方法是只允许预先确定的查询。这可以通过使用类似persistgraphql的库来实现,简单概括一下,
- 在构建时,获取用于客户端的查询列表。对每个查询进行哈希处理,并创建实际查询与哈希值之间的映射。客户端发送哈希值,服务器端解析并执行查询。
我們將進行類似的活動。
网络性能
由于GraphQL在客户端中包含数据结构来获取数据,这可能会导致请求的查询大小变得庞大。尽管可以通过使用上述持久化查询中的哈希值来解决此问题,但也可以使用后面提到的Apollo Engine的“自动持久化查询”来解决。
与上述的持久化查询不同,这只是为了减小请求大小。通过在首次执行时在服务器端缓存查询和哈希值,可以通过仅向服务器发送哈希值来执行查询,从而使得在第二次及以后的请求中可以减少数据传输量。
n + 1问题
尽管主题有些不同,但在GraphQL中基本上无法使用类似Eager loading的机制,因此简单地实现总是会引发n+1问题。Facebook的dataloader可以简洁地解决这个问题。除了js之外,还有类似的库可供查找。
关于测量
无论采取哪种方法,都需要进行性能改进的测量。GraphQL的测量很容易通过使用名为Apollo Engine的服务来进行,我将介绍一下。
可以做什么呢?
在阿波罗引擎上,您可以看到以下信息:
执行日期和处理时间的查询信息
各查询的处理时间和数量的统计数据
各节点的处理时间统计
每个节点的处理时间针对特定请求。
另外,您还可以查看错误发生率等信息。顺便说一下,免费的请求限额是1M/月。即使只是开发环境,也可以轻松发现有问题的查询,所以安装是没有损失的。
如果使用Node.js,可以像这样嵌入。
var { Engine } = require('apollo-engine');
const engine = new Engine({
engineConfig: {
apiKey: process.env.APOLLO_ENGINE_API_KEY,
logging: {
level: 'DEBUG'
}
},
graphqlPort: PORT,
endpoint: '/graphql',
dumpTraffic: true
});
engine.start()
app.use(engine.koaMiddleware());
最后
在AWS的App Sync中,宣布了利用Apollo进行离线支持和实时更新的功能。希望这能推动GraphQL生态系统进一步发展。