有关Nginx Proxy模块中proxy_cache_use_stale和proxy_cache_lock的作用

之前在网上看到了有关nginx缓存的讨论, 发现自己还有很多不明白的地方, 其中之一就是有关stale cache. nginx网站上相关的设置为proxy_cache_use_stale. 之后又看到了这个帖子, 看上去像是可以减少回源次数. 于是决定尝试一下;)

准备

个人感觉搭环境是最最耗时间的一件事呢. 不过还好这些服务都是蛮容易搭的, 所有服务跑在一台机子上就可以了.

服务端

先用mojolicious搞个真实的服务.

real_server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/perl

use Time::HiRes qw/gettimeofday/;
use Mojolicious::Lite;

app->log->path('./log');

app->hook(after_dispatch => sub {
    my $c = shift;

    $c->res->headers->cache_control('max-age=2');
});

plugin AccessLog => {
    log     => './access.log',
    format  => 'combined',
};

get '/hello' => sub {
    my $c = shift;

    $c->render_later;

    my $delay = Mojo::IOLoop->delay(sub {
        $c->render(text => "hello Mr.@{[join('.', gettimeofday)]} ;)");
    });

    $delay->steps(
        sub {
            my $delay = shift;
            Mojo::IOLoop->timer(5 => $delay->begin);
        },
    );
};

app->start;

为了有清楚直观的效果, 主要有以下几点:

  1. 利用Mojolicious::Plugin::AccessLog模块进行访问的日记记录
  2. 请求到来时隔5秒再返回请求.
  3. 利用对应的hook, 更新HTTP头缓存信息.

题外话, 感觉Mojo启动时的相关代码真心读不懂, 比较郁闷. 不过看看还是蛮有意思的;)

代理端

准备一个nginx作为代理的简单配置文件:

y.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
proxy_cache_path /path/of/cache/data/yy levels=1 keys_zone=no_yy:5m inactive=1h max_size=10m;

upstream y.cn {
    server 127.0.0.1:3000;
}

server {
    server_name y.cn;
    listen 127.0.0.1;

    log_format wtf  '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" "$upstream_cache_status"';

    access_log logs/y.cn wtf;

    location / {
        proxy_pass http://y.cn ;

        proxy_cache no_yy;
        proxy_cache_valid 200 1s;

        proxy_cache_bypass $http_x_purge_cache;
        #proxy_no_cache     $http_x_purge_cache;

        #proxy_cache_use_stale updating;
        proxy_cache_lock on;
        proxy_cache_lock_timeout 10s;
    }
}

这个配置文件比较简单, 将缓存存在路径yy里, 对应的zone是no_yy. 另外需要注意的inactiveproxy_cache_valid设置的时间. 我们之后仅仅需要调整proxy_cache_lock以及proxy_cache_use_stale. 为了方便观察, 在访问日志中记录了变量$upstream_cache_status的值.

客户端

再来一个模拟多个请求的访问脚本, 主要利用了curl:

multi-request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

function purge ()
{
    local url='http://y.cn/hello'

    rs=`curl -x 127.0.0.1:80 -H "X-PURGE-CACHE: 1" -A "curl purge" "$url" 2>/dev/null`

    echo "[purge] $rs"
}

function request ()
{
    local id=$1
    local url='http://y.cn/hello'

    rs=`curl -x 127.0.0.1:80 -A "yy $id" "$url" 2>/dev/null`

    echo "[$id] $rs"
}

#purge &

for i in {0..9}; do
    request $i &
done

wait

这个脚本差不多可以同时发出10个请求. 其中的purge函数主要是为了查看nginx配置文件相对应的proxy_cache_bypassproxy_no_cache设置对缓存的影响.

尝试

环境和脚本都有了, 进行尝试的过程就异常轻松了. 简单步骤就是:

  1. 服务器端修改http头信息, morbo自动监测
  2. 修改nginx配置文件, nginx -s reload加载配置文件
  3. 跑访问脚本, 查看输出以及mojolicious和对应的nginx访问日志

得出的简单结论如下:

  1. 使用proxy_cache_stale开关可以大大减少回源次数, 因此可以将inactive这个值适当延长
  2. 推荐设置proxy_cache_lock, 同样是减少回源次数. 和上面的差别在于缓存是否存在
  3. proxy_cache_bypassproxy_no_cache可以搭配使用来刷新单一缓存

相关代码

有兴趣的同学不妨可以看看nginx有关cache方面的源码, 这里推荐一篇相关代码分析的文章.

有关cache的代码主要集中在ngx_http_upstream.cngx_http_file_cache.c两个文件中. 流程方面可以从ngx_http_upstream_init_request开始看.

Over

Comments