过载保护
为高流量场景实现过载保护
一个GraphQL服务器可以实现过载保护以帮助其在高负载下保持可用。带有过载保护的服务器会监视其资源使用情况,并在资源使用接近性能降低的限制(如耗尽内存)时开始削减传入流量。
随着您向supergraph添加功能和用户,可能会引入新的使用模式,造成不预期的过高负载。过载保护有助于在优化supergraph以完全消除这些峰值的同时减少其影响。
示例场景
系统中常见的过载原因是 雷暴雨问题,大量进程或客户端尝试访问有限的计算机资源。
- Kubernetes中的Pod故障导致较少的Pod处理相同数量的流量。
- 营销活动或病毒式社交媒体帖文在短时间内使应用流量激增。
- 新部署的功能在 图中引入了预期之外的更多负载。
更基于图的复杂问题是在模式中添加一种 实体关系,这导致了流量的显著增加。例如在 星球大战模式中,想象如果没有今天添加的从 Person
到 Film
(尽管是 PersonFilmsConnection
)的链接,并且今天第一次添加。直到模式中该新连接的使用量平缓下来,任何引起流量的部署或事件都可能直接导致 Film
实体的所有者(account holder)承担大量新的负载。
Express 实现方式
大多数流行的编程语言和服务器框架都提供了过载保护包。例如,我们将使用 overload-protection 包结合 @apollo/server
包。此即时(instant)包使您的服务器可以根据以下任何条件返回一个 503 状态码:
- 当前事件循环延迟
- 堆(heap)使用的字节数
- 常驻集大小(RSS)使用的字节数
要使用 overload-protection
,将包包含在 Express 启动中,如下所示:
import express from 'express';import protect from 'overload-protection';const app = express();protect('express');app.use(protect);
ⓘ 注意
如果您当前正在使用 startStandaloneServer
函数,您需要在使用过载保护之前将其切换到 expressMiddleware。
如果您正在使用 @apollo/server
的 Express集成(即expressMiddleware),您可以通过将突出显示的行添加到服务器创建过程中来通过 Express中间件添加 overload-protection
。
import {ApolloServer} from '@apollo/server';import {expressMiddleware} from '@apollo/server/express4';import {ApolloServerPluginDrainHttpServer} from '@apollo/server/plugin/drainHttpServer';import express from 'express';import http from 'http';import cors from 'cors';import {typeDefs, resolvers} from './schema';import protect from 'overload-protection';protect('express');const app = express();app.use(protect);const httpServer = http.createServer(app);const server = new ApolloServer({typeDefs,resolvers,plugins: [ApolloServerPluginDrainHttpServer({httpServer})]});// Note the top-level `await` calls below!await server.start();app.use('/graphql', cors(), express.json(), expressMiddleware(server));await new Promise(resolve => httpServer.listen({port: 4000}, resolve));console.log(`🚀 Server ready at http://localhost:4000/graphql`);
这种方法也适用于您正在使用 @apollo/subgraph
库以类似的方式创建您的 子图。
过载保护并非专用于 GraphQL,因此最好在 Apollo 软件之外处理它。
保护超级图
当添加超级图的过载保护时,一个合理的问题就是,"我是应该给我的网关/路由器还是给我的各个 子图 添加保护?" 简短的回答是 "两者都要":
- 保护 路由器 保护了整个 超级图 的可用性。
- 保护一个 子图 可以下降请求该子图的数据查询的错误率。
网关/路由器
网关的主要问题是由于请求积累而导致的部分或级联失败。如果网关无法卸载过载,其性能将开始下降。
通常单个网关请求会转换为对 子图 的多个请求,这可能会超过复杂查询的预期负载。在这种情况下,网关中的过载保护可以防止其完全崩溃。这看起来像是可用性的一次临时下降,而不是完全中断。
子图
子图的失败可能会导致网关的备份。如果这种备份是由于负载引起的,过载保护有助于短路错误的返回。这通过允许网关更快地返回错误来释放网关和子图的压力。