Free Arch:给 GraphQL 加上服务器端响应缓存
题外话(声明本文和图数据库无关)
最近写了几篇关于 GraphQL 的文章:
《Free Arch:给 GraphQL 增加 CDN 缓存》
得到一些私信以及评论,问我关于图数据库的问题。我声明一下,目前我还没有任何使用图数据库的经验!
当然如果先打赏再来问我,我可能不会这么偷懒地说,我会悄悄地学习一下,再回答,哈哈哈哈哈。
我在另一个回答里,也简单声明了,GraphQL 和图数据库完全是两码事,这里再澄清一下:
图数据库是一种将数据存储在图中的数据存储技术,如果数据和数据间存在相互关系的话(当前的大多数数据集正好符合这个特征),据说它的性能是非常非常好的。但是我本人目前并没有任何使用图数据库的经验。
GraphQL 是查询数据的一种方式。它定义了灵活的 API 协议,允许你查询指定的刚好需要的数据,并且按需返回。至于数据存储技术采用什么,GraphQL 完全不管。这个我有一些经验。
GraphQL 总结
GraphQL 和图数据库是两回事情,GraphQL 是一种灵活的查询语言,其优势并不依赖图数据库,所以放心地使用吧!
前情提要
在 《Free Arch:给 GraphQL 增加 CDN 缓存》一文里,详细叙述了如何薅各种云厂商的羊毛,从而给 GraphQL 服务加速,以一个非常慢的的查询为例子,解决了其三慢合一中的两慢。
服务器端慢查询
AWS lambda 冷启动,通过 CDN 缓存直接响应,多数请求不再打到 AWS lambda。
Heroku 冷启动,通过 AWS API Gateway 的自定义域名,不再依赖 Heroku 上的代理服务。
其中服务器端慢查询并未解决,从而需要 CDN 预热。即正如前文所说的,请求量分布越广泛,请求量越大,该服务的整体性能表现越好。但是对于小站点来说,用户是零星分布在各个不同的地域。就是说尽管每个地域只要有第一个人访问了该服务,触发了该地域的 CDN 缓存建立,从而使得后续该地域的用户感受到极快的服务响应,但是这个小站点在该地域就那么一个用户,他的体验不好,就直接流失了,没有更多的用户来感受到后续的极快响应。所以反而是小站点更需要解决第一个慢的问题,因为它的用户发出的请求,更容易直接打到服务器。而大站点,多数用户的请求只到达 CDN 就直接折返了。
今天就来解决这第一个慢的问题。
思路
尽管慢查询的根本原因在后端的代码,但是免费架构的思路确实从前到后去做优化。就是尽量在烂代码的前提下,提升系统的整体表现。在烂代码的前提下,改善了系统整体性能表现后,再去优化后端代码(如果追求极致的话);多数情况下可能直接省略了烂代码的优化过程,毕竟,又不是不能跑,而且已经跑得很好了!
CDN 缓存是最靠前的优化,今天往后一步,在请求从服务器端发出时做个拦截,将请求发出去时,同时将响应体写在内存里。这样,再有请求打到服务器端,就直接从内存读取上次查询得到的结果并发出去,从而避免执行慢查询。
启用响应缓存
使用 Apollo 的话,有官网可以参考。所以从编码层面不做详述,具体的实际案例的细节,可以参考这个提交:
https://github.com/Jeff-Tian/serverless-space/commit/45cce5614866b340c8fb08b8b911dc9294c2017e
这仍然是一篇免费架构的系列文章,因此重点会放在如何薅羊毛上,以及分享其他的小妙招。
效果展示
端到端体验(在小程序里打开本文)
响应速度
通过 Apollo Studio,直接访问 AWS lambda API Gateway 的原始默认链接(绕过 CDN):
https://jqp5j170i6.execute-api.us-east-1.amazonaws.com/dev/nest/graphql,请求慢查询,可以看到延时在 1 秒以内!
(要知道,因为使用的是 AWS 免费账户,所以是 AWS 海外的 lambda 服务)
请求头
缓存控制被正确设置
Redis 缓存的内容展示
后面会详述如何使用免费的 Redis 服务,以及为什么需要使用公开服务展示 Redis 的缓存内容。
访问:https://uniheart.herokuapp.com/redis 可以展示 Redis 的缓存内容,可以看到慢查询的结果被正确缓存了,并且以 Apollo 默认的 fqc:xxxx 作为键值:
这里为了方便在线展示,复用了几年前的免费服务。如果被滥用,以上路由可能会被加上锁。
免费使用 Redis To Go 的中转方案
正如 Apollo 官网所说,启用响应缓存,可以使用 Redis 或者其他内存数据库。这里发现一个好的在线 Redis 服务,Redis To Go:
正如上图首页展示的,官网没有免费选项,但是免费架构教你如何免费使用 Redis To Go 的服务。很简单,去 Heroku 里创建一个应用,然后在这个应用里添加一个 Redis add-on,选择 Redis To Go。
然后你就可以从环境变量里读取链接字符串,形如:redis://redistogo:password@hostname.redistogo.com:9295
Redis Insight 小妙招分享
Redis Insight 是一个 Redis Web 控制台,使用它可以方便观测 Redis 服务器。要启动很简单,可以参考如下 docker-compose 文件:
version: '3'
services:
redis:
image: redis
ports:
"6379:6379"
redisinsight:
image: redislabs/redisinsight:latest
ports:
'8001:8001'
然后执行:
docker compose up -d
open http://localhost:8001
接下来是一个小妙招系列。打开 Redis Insight 后,添加 Redis 服务器时,会看到对话框中分开给予了配置项,但是我们拿到的是一个连接字符串。我曾经准备给 Redis Insight 提改进意见,让它直接接收连接字符串,从而不需要手动去拆开连接字符串。但是实际上 Redis Insight 是支持连接字符串的!只需要复制连接字符串,然后在对话框里粘贴就行,它会自动拆解:
总结
通过 Heroku 免费应用的中转,成功薅到 Redis To Go 的 Redis 服务羊毛。启用 GraphQL 响应缓存,使得在没有 CDN 的情况下,也能改善系统的整体响应速度。结合前面的 CDN 缓存,解决三慢合一问题,既优化了第一个用户的体验,又优化了后续其他用户的体验。