在10.1节和10.2节,介绍的都是全局的缓存系统,那么有没有只针对当前请求设置的缓存呢,即某个数据在请求的每个阶段都有缓存,但请求结束缓存就会消失。

举例来说,请求在rewrite阶段生成1个缓存数据,作用是可以让后面的阶段(如content阶段)获取到该缓存数据,但该数据在请求完成后就没有用了,可以在请求结束后清除掉。

这种缓存主要针对如下情况使用,在Ngx_Lua中Lua API的执行是有阶段限制的,那么当某个不能执行Lua API的阶段需要使用Lua API指令生成数据时,就可以利用刚才介绍的缓存方式,在其他阶段处理好数据并缓存,在需要该数据的阶段调用此缓存即可。这里需要用到的缓存指令是ngx.ctx。

10.3.1 ngx.ctx的使用

ngx.ctx
环境:init_worker_by_lua、set_by_lua、rewrite_by_lua、access_by_lua、 content_by_lua、header_filter_by_lua、body_filter_by_lua、log_by_lua、ngx.timer.、balancer_by_lua
含义:ngx.ctx是Lua的table类型,用来缓存基于Lua的环境数据,该缓存在请求结束后会随之清空,类似于Nginx中set指令的效果。
示例:

 rewrite_by_lua_block {
        --设置1个test数据
        ngx.ctx.test = 'nginx'
        ngx.ctx.test_1 = {a=1,b=2}

    }
    access_by_lua_block {
        --修改test
        ngx.ctx.test = ngx.ctx.test .. ' hello'
    }
    content_by_lua_block {
        --输出test 和 test_1中的a元素的值
        ngx.say(ngx.ctx.test)
        ngx.say("a: ",ngx.ctx.test_1["a"])
    }
    header_filter_by_lua_block {
        --作为响应头输出
        ngx.header["test"] = ngx.ctx.test .. ' world!'
    }
}

上述配置执行结果如下:

# curl -i  'http://testnginx.com/'
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Mon, 18 Jun 2018 05:07:15 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
test: nginx hello world!

nginx hello
a: 1

从执行结果可知,ngx.ctx的数据可以被Lua的每个执行阶段读/写,并且支持存储table类型的数据。

10.3.2 子请求和内部重定向的缓存区别

ngx.ctx在子请求和内部重定向中的使用方法有些区别。在子请求中修改ngx.ctx. 的数据不会影响主请求中ngx.ctx.对应的数据,它们维护的是不同的版本,如执行子请求的ngx.location.capture、ngx.location.capture_multi、echo_location等指令时;在内部重定向中修改数据会破坏原始请求中ngx.ctx.*的数据,新请求将会是1个空的ngx.ctx table,例如,当ngx.exec、rewrite配合last/break使用时。

ngx.ctx在内部重定向中使用的示例如下:

location /subq {
    header_filter_by_lua_block {
        --如果ngx.ctx.test不存在,则把not test赋值给a
        local a = ngx.ctx.test or 'not test'
        --作为响应头输出
        ngx.header["test"] =  a .. ' world!'
    }
    content_by_lua_block {
        ngx.say(ngx.ctx.test)
    }
}
location / {
    header_filter_by_lua_block {
        ngx.header["test"] =  ' world!'
    }
    content_by_lua_block {
        ngx.ctx.test = "nginx"
        --执行内部重定向
        ngx.exec("/subq")
    }
}

执行结果如下:

# curl   -i 'http://testnginx.com/'
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Mon, 18 Jun 2018 05:36:38 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
test: not test world!

nil

从执行结果可知,ngx.ctx.test在内部重定向后变为空。
注意:查询ngx.ctx 会产生元方法调用,这会消耗一定的服务器资源,所以如果数据可以通过函数生成,就尽量不要使用ngx.ctx。如果希望更多地了解ngx.ctx对性能的影响,可以通过火焰图对它的性能进行分析(详见第16章)。