关于ngx.say(),网上有看到说,如果在access_by_lua_*内有ngx.say输出则content阶段不会被执行到。
我自己在测试中发现确实如此,如果在access_lua_by_lua_*中调用了ngx.say,那content阶段的响应是不会输出的。
我个人的理解是:调用ngx.say()后就生成了响应头和响应体发送给客户端了,也就说Nginx提前终止了该次请求的处理,所以不会再走入content阶段了。
不知道这个理解是否正确?
如果这个理解正确的话,那是不是说明:虽然content才是通常意义上Nginx生成响应内容的阶段,但在其他阶段Nginx也可以生成响应并直接返回给客户端(所以也就跳过了后续的执行阶段)
是的,在 rewrite_by_lua* 或者 access_by_lua* 里面调用了 output APIs,会导致后续阶段没有机会执行。
PS: 源码里有一段注释
/* response header was already generated in access_by_lua*,
* so it is no longer safe to proceed to later phases
* which may generate responses again */
另外,在官网文档ngx.print()部分的介绍中有写到:
If response headers have not been sent, this function will send headers out first and then output body data.
This is an asynchronous call and will return immediately without waiting for all the data to be written into the system send buffer.
因为ngx.print()和ngx.say()的区别只在于换行符,那为什么在access_by_lua_*中可以多次调用ngx.say()而且多次调用的输出是在同一个HTTP响应里的?因为第一次调用后,就已经生成响应头和body了,后续调用生成的响应,是跟第一次调用生成的响应合并成一个再发送给客户端么?
第一次调用 ngx.say 或者 ngx.print,会尝试发送响应头,如果不是 HTTP/1.0 并且开启 lua_http10_buffering,那么响应头里会包含 Transfer-Encoding,body 都会以 chunked 的形式发送出去,后面再次调用 ngx.say 或者 ngx.print,内容也是组织成 chunked 形式发送出去的。如果此时是 HTTP/1.0 且开了 lua_http10_buffering,那么内容会被缓存,直到显式调用 ngx.eof、 ngx.exit 或者当前的 entry thread 结束,内容会被刷出去(此时响应头里包含的是 Content-Length 头部)。