十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

聚合多种数据源:Apollo Server对接REST API与N+1问题消除实战

聚合多种数据源:Apollo Server对接REST API与N+1问题消除实战 聚合多种数据源Apollo Server对接REST API与N1问题消除实战【免费下载链接】apollo-server Spec-compliant and production ready JavaScript GraphQL server that lets you develop in a schema-first way. Built for Express, Connect, Hapi, Koa, and more.项目地址: https://gitcode.com/gh_mirrors/ap/apollo-serverApollo Server 是一款符合规范、面向生产的 JavaScript GraphQL 服务器支持 schema-first 开发方式可运行在 Express、Koa、Hapi 等框架上。本文将聚焦两个高频实战问题如何用 Apollo Server 聚合多种数据源对接 REST API以及如何消除拖慢接口的 N1 查询问题帮助新手用最短路径构建稳定的聚合层。为什么选择 Apollo Server 聚合多种数据源在真实业务中数据往往散落在不同地方用户信息在 SQL 库、电影目录在第三方 REST API、个性化推荐又在另一个微服务里。Apollo Server 的核心价值就是用一个 GraphQL 接口把这些数据源缝合起来——客户端只发一次请求服务端由各个 resolver 分头去取数。官方推荐的组织方式是为每一种数据源写一个独立的类把取数逻辑封装在类里resolver 只负责调用类的方法。这样代码干净、好测试也方便统一加缓存和错误处理详见 fetching-data.mdx。第一步用 RESTDataSource 子类封装 REST API 请求对接 REST API 时直接使用官方维护的apollo/datasource-rest包中的RESTDataSource基类。你只需要声明baseURL然后为每个端点写一个取数方法import { RESTDataSource } from apollo/datasource-rest; class MoviesAPI extends RESTDataSource { override baseURL https://movies-api.example.com/; async getMovie(id: string) { return this.getMovie(movies/${encodeURIComponent(id)}); } }要点提示内置的get/post/put/patch/delete方法会自动解析 JSON、附带查询参数用encodeURIComponent编码 URL 路径防止注入可覆写willSendRequest为所有请求统一加鉴权头或 API key。第二步在 context 函数中按请求注入数据源RESTDataSource内部带有请求级缓存因此每个请求必须创建新实例数据库连接池这类长生命周期资源则相反可以复用。在context函数里完成注入并顺手把服务端的共享cache传进去const { url } await startStandaloneServer(server, { context: async () { const { cache } server; return { dataSources: { moviesAPI: new MoviesAPI({ cache }), }, }; }, });resolver 里即可通过contextValue取用const resolvers { Query: { movie: (_, { id }, { dataSources }) dataSources.moviesAPI.getMovie(id), }, };⚠️ 常见踩坑如果全局复用同一个数据源实例响应会被错误地缓存到其他请求里——这正是要求按请求新建实例的原因。更多细节见 fetching-rest.mdx 与 context.mdx。N1 问题为什么 10 篇文章要发 11 次请求看这条查询取 10 篇文章每篇都要作者名。query GetPosts { posts { body author { name } } }朴素实现下GraphQL 会为每个post触发一次作者查询1 次取文章列表 10 次取作者 11 次 HTTP 请求且彼此串行等待。这就是著名的 N1 问题是 GraphQL 层 REST 时接口变慢的头号元凶。双层缓存实战如何消除 N1 冗余请求RESTDataSource天生自带两层缓存多数 N1 场景不用手写批处理就能解决第一层缓存并发 GET 请求自动去重同一请求内多个 resolver 打到相同 URL的GET和HEAD请求会被自动合并第一次请求发出后后续相同请求直接等待并复用结果不再真正发第二次 HTTP 请求。举例10 篇文章里有 3 篇是同一作者原本要发 10 次作者查询去重后只需 3 次每个作者 1 次N1 立即退化成 NK。第二层缓存按 HTTP 缓存头与 TTL 存储响应如果 REST 端点返回了cache-control等标准缓存头RESTDataSource会按 TTL 规则把响应体存入缓存你也可以通过cacheOptionsFor方法自定义 TTL。把多个数据源指向同一个cache实例如服务端的默认缓存或生产环境的多实例 Redis还能让缓存跨数据源、甚至跨服务实例共享。 效果重复请求秒回缓存数据库与下游 REST API 的压力显著下降。外部缓存后端的配置方法见 cache-backends.mdx。进阶优化用 DataLoader 为 REST API 批量取数去重解决重复但不同 key的 N110 个不同作者仍需逐次请求。此时可以在数据源内部引入DataLoader把同一事件循环 tick 内的多次load合并成一次批量请求例如?ids1,2,3。官方建议批处理只用于无法被缓存的数据能缓存的优先走缓存路线——因为批量响应往往无法按单个资源命中缓存详见 fetching-data.mdx 的 Batching and caching 一节。启动后在 Apollo Sandbox 验证数据源startStandaloneServer启动后浏览器打开返回的地址即可进入内置的 Apollo Sandbox左侧浏览 schema 文档右侧编写查询并实时查看响应。这是验证聚合结果是否正确、观察接口耗时的最快方式。生产环境下也可以直接对服务端点发送 POST 请求做冒烟测试延伸阅读核心文档与源码路径主题路径REST 数据源完整指南缓存策略、拦截请求docs/source/data/fetching-rest.mdx自定义数据源类与 DataLoader 批量docs/source/data/fetching-data.mdxcontext 函数与 contextValuedocs/source/data/context.mdxresolver 编写规范docs/source/data/resolvers.mdxMERN 栈集成示例docs/source/integrations/mern.mdx响应缓存与缓存后端配置docs/source/performance/cache-backends.mdx总结用RESTDataSource子类封装每个 REST API、在context中按请求注入实例、利用内建的去重与 TTL 缓存消除 N1——掌握这套组合拳你的 Apollo Server 就能稳定聚合任意多种数据源。【免费下载链接】apollo-server Spec-compliant and production ready JavaScript GraphQL server that lets you develop in a schema-first way. Built for Express, Connect, Hapi, Koa, and more.项目地址: https://gitcode.com/gh_mirrors/ap/apollo-server创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表