Ghost 安装完后默认设置的响应速度已经是很不错,在相同配置和网络线路的机器安装服务且不优化的情况下,Ghost 相较于 WordPress 是肉眼可见的快很多。
一般来说想要用户体验不差,页面 TTFB 响应得在 500ms 以内,超过了 500ms 那页面的加载就会有很明显的迟缓。想要用户体验达到优质,起码要保持响应能够在 200ms 以内。连通性还可以的机器上,Ghost 安装完默认状态其实能在 200ms 左右,比 Wordpress 动辄上 500ms 已经好很多,但是他们都还有很多可优化的空间,在众多优化方法中,缓存自然而然排到第一位。
因为 Ghost 动态博客系统,无论它多么轻量化和高效率,总避免不了用户的每一次请求都要经过内核执行计算工作来返回数据给用户。接收到用户请求后,它先要去读取数据库,然后利用读取回来的数据渲染页面,最后再把页面分发到客户端。这一点对于更新不频繁的博客来说,有很多的额外资源浪费,也导致了 TTFB 被拉长,访客的体验下降。
缓存则可以很好的解决这个问题,因为缓存可以避免重复内容的请求再次经过 ghost 内核计算,而是直接在代理服务器层面直接返回数据给用户。处理链路简化了,同时也避免了计算资源的占用。
不过缓存要怎么做呢?Wordpress 这个「生态成熟的巨无霸」倒是有现成的插件,安装完直接就可以直接使用,那 Ghost 该怎么操作来使用缓存呢?其实很简单,直接定制一下反向代理工具的参数就可以。比如大部份人在用的 Nginx,它本身就提供了缓存的功能,需要做的只是简单的配置一下。
至于缓存这一套东西是怎么运行的呢,正常来说,Nginx 处理响应的顺序是:
- 用户发送请求;
- Nginx 接收请求;
- Nginx 转发请求给 Ghost;
- Ghost 读取数据库,生成页面,返回页面给 Nginx;
- Nginx 将 Ghost 返回的页面传送给用户。
大致就是这样 5 个步骤,乍一看其实没什么,但是实际上,用户的所有页面请求,服务器都需要完整的执行一次这个流程,即便刚刚才访问过一个页面,之后再访问一次,服务器还是要重复再执行一次这个流程。
那么如果使用了缓存功能,Nginx 又会怎么处理数据请求呢?其实第一次访问还是相同的,Nginx 还是需要像 Ghost 去请求数据的,只不过多了一些缓存的操作:
- 用户发送请求;
- Nginx 接收请求,检查是否有对应资源的缓存;
- 第一次请求,检测不到缓存,把请求发送给 Ghost;
- Ghost 读取数据库,生成页面,返回页面给 Nginx;
- Nginx 将 Ghost 返回的页面传送给用户,同时创建对应资源的缓存;
在初次访问过后,Nginx 留存了对应资源的缓存,那么之后的访问流程就变成:
- 用户发送请求;
- Nginx 接受请求,检查是否有对应资源的缓存;
- 检测到缓存,Nginx 直接将缓存数据传回给用户。
很明显流程减少了,而且减少的还是相对比较耗时的那一部份流程,这样下来,即便使用 Ghost,访问体验也会和那些静态博客没什么差别拉。
给 Ghost 配置 Nginx 缓存
那么要怎么使用 Nginx 缓存呢,其实很简单的,只需在 Nginx 的配置文件里面添加一些内容就好了。首先,打开站点的 Nginx 配件,然后在顶部设置一个缓存区:
proxy_cache_path /tmp/blog_pub_cache levels=1:2 keys_zone=blog_pub_cache:16m max_size=256m;
配置缓存区域:存储路径、名称、限制等等
其中 /tmp/blog_pub_cache
是缓存数据存放位置,这个可以根据自己想法改,keys_zone
后面的 blog_pub_cache
是缓存区的名称,这个也可以改,更详细的参数可以去看看官方的文档。
缓存区有了,接下来要在 location
块里添加配置来使用缓存功能了:
# 检查缓存命中状态
add_header X-Cache-Status $upstream_cache_status;
## 缓存配置
# 忽略源的缓存控制,不影响 add_header Cache-Control 的传递
proxy_ignore_headers Cache-Control;
# 调用的缓存区域的名称
proxy_cache blog_pub_cache;
proxy_cache_key $uri$is_args$args;
proxy_cache_lock on;
# 设置不同响应的缓存的有效期
proxy_cache_valid 200 1d;
proxy_cache_valid 404 1m;
# 验证缓存是否过期和是否被修改
proxy_cache_revalidate on;
# 后台更新缓存
proxy_cache_background_update on;
# 在服务故障时(返回错误响应)使用缓存来响应请求(避免服务彻底失效)
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
都是一些缓存相关的设置,更多可选参数和细节可以去看看官网的手册
添加完成之后,保存刚刚编辑的配置文件,用 nginx -t
检查一下配置文件是否有问题,如果没有问题的话,systemctl restart nginx
重启一下 Nginx。
重启完 Nginx 之后,新建一个隐私模式的浏览器窗口并访问站点,之后打开浏览器的调试工具,找到「网络」选项卡,关掉缓存,然后再刷新一次页面。正常情况下已经能很明显的感觉到响应速度变快了。
此时点击「网络」选项卡下的请求,看一下响应头里面的数据,正常情况下会有一个 X-Cache-Status
的条目,如果后面的数据是 HIT,那正面缓存响应成功了。
缓存设置好了之后还有两点需要注意的,一个是可能存在部份页面需要设置「跳过缓存」来保证实时性,另一个则是使用了「会员阅读权限」功能的用户。
跳过部份页面缓存
Ghost 是不适合全站缓存的,比如后台登陆页面,账户信息页面等等,这一些需要实时信息的页面都是不适合缓存的,所以如果是在 location / {...}
下配置的缓存,一定要注意添加一些内容来让不适合缓存的页面跳过的缓存。
操作的话很简单,直接在 location /
的里面嵌套一个新的 location 块:
location ~ /(ghost/account|members|signin|signup) {
proxy_no_cache 1;
proxy_cache_bypass 1;
}
排除 ‘ghost’ ‘account' 'members' 'signin' signup' 页面(可以根据自己需求增减)
不同级别用户如何分开缓存
Ghost 自带了阅读权限配置的功能,可以给内容设置「所有人可见」「订阅用户可见」「付费用户可见」等权限,但使用缓存功能后,可能存在权限交叉的情况。
这时候的处理办法,就是要对不同等级的用户来单独设置缓存区了。
首先得在配置文件的顶部再添加一个缓存区,直接加在之前设置的缓冲区下面就可以。我这边的两个缓存区,一个名字是是 blog_pub_cache
,意思是常规的游客缓存,下面新添加的: blog_mem_cache
,用来存放订阅用户的缓存。
proxy_cache_path /tmp/blog_pub_cache levels=1:2 keys_zone=blog_pub_cache:16m max_size=256m;
proxy_cache_path /tmp/blog_mem_cache levels=1:2 keys_zone=blog_mem_cache:16m max_size=256m;
同时配置两个缓存区域,用来分别存放不同用户的缓存
之后需要判断用户是否是订阅用户,我这边因为没有 Stripe,目前暂时只用了免费订阅的功能,所以目前来说只需要区分游客和普通订阅用户就可以,那么区分方法只用看看是否有 ghost-members-ssr.sig
cookie 就好了:
# 默认使用公共缓存
set $ghost_cache_type blog_pub_cache;
# 如果包含会员信息 cookie 则改用会员缓存
if ($http_cookie ~* "ghost-members-ssr.sig") {
set $ghost_cache_type blog_mem_cache;
}
根据 cookie 来分别调用不同的缓存空间
第一段配置是设置 $ghost_cache_type
这个变量为普通的「游客缓存」,第二段就是判断服务是否包含「ghost-members-ssr.sig」的 cookie,如果有的话就将变量改为 blog_mem_cache
。
最后,把之前在 location
配置的 proxy_cache
字段,把后面的缓存区名称改为变量 $ghost_cache_type
。这样,无论是普通用户还是订阅用户,在他们访问的时候,Nginx 都会调用对应的缓存区来响应用户的缓存,避免了权限交叉。
就这样,其实很简单。不过因为没有系统地学过 Nginx,都是拼拼凑凑弄的,所以刚开始也折腾了挺久。不清楚有没有可能有错误(应该是没有的吧😂)。