Hello!
2012/8/1 Liu Dapeng wrote:
> agentzh 你好!
>
> 我是一名对openresty很感兴趣的技术人员,最近正在考虑用openresty做cdn的动态分流,在使用openresty的时候遇到一个问题
>
> body_filter_by_lua 为什么在有些请求中执行了两次,现象
> 1. 如果请求的是存在的静态内容或者 proxy_pass 时body_filter_by_lua执行一次
> 2. 如果请求的是content_by_lua或echo的url的时候 body_filter_by_lua执行两次
>
Nginx 的 output body filter
本来就会在一次请求中被调用多次。因为分块(或者说分批)输出响应体数据是非常正常的做法。这也是实现基于 HTTP 1.1 chunked
编码的所谓“流式输出”所必需的。
考虑下面这个例子:
location = /read {
echo -n hello world;
echo -n hiya globe;
body_filter_by_lua '
local chunk, eof = ngx.arg[1], ngx.arg[2]
print("chunk: [", chunk, "], eof: ", eof)
';
}
这里每一条 echo 指令都会调用一次 nginx 输出过滤器链,再加上最后标识输出流末尾的“last
buf”,则一次会调用三次。该示例在请求 /read 时会在 nginx 的错误日志中产生三条 notice 级别的输出:
[notice] 2297#0: *1 [lua] [string "body_filter_by_lua"]:3: chunk:
[hello world], eof: false
[notice] 2297#0: *1 [lua] [string "body_filter_by_lua"]:3: chunk:
[hiya globe], eof: false
[notice] 2297#0: *1 [lua] [string "body_filter_by_lua"]:3: chunk:
[], eof: true
对于使用 ngx_proxy 等其他模块的场景,输出过滤器被调用的次数就不一定了。
> openresty版本 nginx version: ngx_openresty/1.2.1.7
>
> 配置
>
> http 段中
>
> body_filter_by_lua '
> local time = math.floor(ngx.time()/100);
> local st_bytes = ngx.shared.st_bytes;
> st_bytes:add(tostring(time), 0);
> st_bytes:incr(tostring(time), 10);
> local time_str = tostring(time);
> local str = st_bytes:get(time_str);
> return;
> ';
>
> server段中
>
> location /testy {
> content_by_lua '
> local time = math.floor(ngx.time()/100);
> local st_bytes = ngx.shared.st_bytes;
> local time_str = tostring(time);
> local str = tostring(st_bytes:get(time_str));
> ngx.say(str);
> ngx.eof();
> ngx.flush();
> ';
> }
> location /proxy/ {
> proxy_pass http://localhost;
> }
> location /echo/ {
> echo "adsf";
> }
> 逻辑
> 每个http请求,为共享数据ngx.shared.DICT中的一个字段加10
>
> 结果
> 1. 访问静态文件(proxy_pass 结果类似)
> nginx restart
> curl "http://localhost:8888/"
> curl "http://localhost:8888/testy/"
>
> 显示 10
>
> nginx restart
> curl "http://localhost:8888/"
> curl "http://localhost:8888/"
> curl "http://localhost:8888/"
> curl "http://localhost:8888/testy/"
>
> 显示 30
>
> 2. 访问echo (content_by_lua结果类似)
>
> nginx restart
> curl "http://localhost:8888/echo/"
> curl "http://localhost:8888/testy/"
> 显示 20
>
> nginx restart
> curl "http://localhost:8888/echo/"
> curl "http://localhost:8888/echo/"
> curl "http://localhost:8888/echo/"
> curl "http://localhost:8888/testy/"
> 显示 60
>
如果你想在共享内存字典中对请求进行统计,可以考虑使用 log_by_lua 指令:
http://wiki.nginx.org/HttpLuaModule#log_by_lua
它总是会在请求结束时被调用,且仅被调用一次。另外,header_filter_by_lua* 在请求生命期中也只会被调用一次,毕竟响应头只有一个 :)
P.S. 我抄送给了 openresty 邮件列表:https://groups.google.com/group/openresty
也欢迎你加入该列表并在那里和我们交流 nginx 以及各模块相关的问题 :)
Best regards,
-agentzh