Apollo Server - 关于缓存/数据源选项的困惑

Posted

技术标签:

【中文标题】Apollo Server - 关于缓存/数据源选项的困惑【英文标题】:Apollo Server - Confusion about cache/datasource options 【发布时间】:2019-04-20 20:34:27 【问题描述】:

文档 (https://www.apollographql.com/docs/apollo-server/features/data-sources.html#Using-Memcached-Redis-as-a-cache-storage-backend) 显示如下代码:

const  RedisCache  = require('apollo-server-cache-redis');

const server = new ApolloServer(
  typeDefs,
  resolvers,
  cache: new RedisCache(
    host: 'redis-server',
    // Options are passed through to the Redis client
  ),
  dataSources: () => (
    moviesAPI: new MoviesAPI(),
  ),
);

我想知道cache 键是如何使用的,考虑到缓存似乎实际上是在MoviesAPI() 之类的东西中自定义实现的,然后通过context.dataSources.moviesAPI.someFunc() 使用。例如,假设我想为 SQL 数据库实现自己的缓存。它看起来像

  cache: new RedisCache(
    host: 'redis-server',
  ),
  dataSources: () => (
    SQL: new SQLCache(),
  ),
);

SQLCache 有我自己的函数连接到RedisCache,例如:

  getCached(id, query, ttl) 
    const cacheKey = `sqlcache:$id`;

    return redisCache.get(cacheKey).then(entry => 
      if (entry) 
        console.log('CACHE HIT!');
        return Promise.resolve(JSON.parse(entry));
      
      console.log('CACHE MISS!');
      return query.then(rows => 
        if (rows) redisCache.set(cacheKey, JSON.stringify(rows), ttl);
        return Promise.resolve(rows);
      );
    );
  

这意味着我在ApolloServer cache 键和dataSource 实现中都有RedisCache。显然,RedisCache 用于 dataSource 实现中,但是 ApolloServer cache 键到底是做什么的?

同样在客户端,示例大多显示使用 InMemoryCache 而不是 Redis 缓存。客户端 Apollo 缓存应该是与服务器缓存不同的缓存,还是应该在两个地方都使用像 RedisCache 这样的相同缓存?

【问题讨论】:

【参考方案1】:

据我所知,传递给ApolloServercache 严格用于RESTDataSource 的上下文中。从 REST 端点获取资源时,服务器将检查响应中的 Cache-Control 标头,如果存在,将适当地缓存资源。这意味着如果标头为max-age=86400,则响应将以 24 小时的 TTL 缓存,并且在缓存条目过期之前,将使用它而不是调用相同的 REST url。

这与您实现的缓存机制不同,因为您的代码会缓存来自数据库的响应。他们的意图是相同的,但他们使用不同的资源。您的代码有效复制 ApolloServer 的 cache 已经做的唯一方法是,如果您为 REST 端点编写了类似的 DataSource

虽然这两种缓存都减少了处理 GraphQL 响应所需的时间(从缓存中获取明显快于从数据库中获取),但客户端缓存减少了必须向服务器发出的请求数量。最值得注意的是,InMemoryCache 允许您在站点的不同位置(如 React 中的不同组件)重复使用一个查询,同时只获取一次查询。

因为客户端缓存是规范化的,这也意味着如果通过一个查询获取资源时已经缓存了资源,您可以避免在通过另一个查询请求它时重新获取它。例如,如果您使用一个查询获取用户列表,然后使用另一个查询获取用户,则您的客户端可以配置为在缓存中查找用户,而不是进行第二次查询。

请务必注意,虽然服务器端缓存的资源通常具有 TTL,但 InMemoryCache 却没有。相反,它使用“获取策略”来确定单个查询的行为。例如,这使您可以拥有一个始终从服务器获取的查询,而不管缓存中有什么。

希望这有助于说明服务器端和客户端缓存都是有用的,但方式却截然不同。

【讨论】:

感谢您清晰的解释!仔细看看MoviesAPI,它实际上并没有像我的SQLCache那样实现自定义缓存,但似乎使用了扩展RESTDataSource的辅助方法,所以像你说的那样有道理,它知道使用@987654334 @ 传递到 ApolloServer。在那种情况下,我也觉得cache 真的应该被称为restCache 以避免混淆哈哈,但是哦,好吧。确实,我已经看到了即时客户端缓存的好处,但是由于您的帮助,我现在更好地理解了服务器端缓存的差异和目的!干杯! 我同意,这有点令人困惑并且没有很好的记录。我相信apollo-datasourceapollo-link-state 一样仍在积极开发中。因此,FWIW,这些 API 在未来可能会发生变化。 嘿,好久不见!因此,功能越多,混乱就越多。这似乎超过了RESTDataSource 缓存的级别,并且更接近我最初想要的:apollographql.com/docs/apollo-server/features/caching/…,但仍然与“缓存来自数据库的响应”不太一样?你能评论一下新功能吗? :P 我认为它可能会起作用,因为我的解析器只是在它们前面使用缓存进行数据库调用,所以这似乎允许我将缓存放在整个服务器级别而不是每个解析器上? 如果您需要覆盖上游 TTL 值,您可以通过 RESTDataSource 中的以下方法来实现:willSendRequest(request: RequestOptions) request.cacheOptions = ttl: 3600 // 1h

以上是关于Apollo Server - 关于缓存/数据源选项的困惑的主要内容,如果未能解决你的问题,请参考以下文章

Apollo 查询从缓存中获取了错误的数据

缓存对象上的 Apollo 客户端查询重复数据删除

Apollo 从缓存中查询对象值

Apollo Server Lambda - 关于无服务器的任何实际示例

我如何通过 nodejs 通过 apollo-server 从服务器获取所有数据

没有参数的字段的 Apollo 缓存重定向