Hi
如果和客户端的连接中断(异常,服务器收到 RST 包),该套接字接收缓冲区里的数据是会被丢掉的,此时 ngx_unix_recv 将会返回 NGX_ERROR 或者 0。
不论是 0 还是 NGX_ERROR, ngx_http_lua_socket_tcp_read 得到返回值,都会设置好错误码(ngx_http_lua_socket_handle_read_error),然后返回到 ngx_http_lua_socket_tcp_receive,之后进入 ngx_http_lua_socket_tcp_receive_retval_handler,往 Lua 返回 nil, errmsg 以及 partial.
那么你再次调用 sock:receive,直接就会得到 nil, errmsg。
这种情况下不会有你说的定时器重复添加的问题。
在 2017年7月21日星期五 UTC+8下午1:26:57,tokers写道:
Hi!
有复现的例子吗
On Thursday, July 20, 2017 at 8:51:12 PM UTC+8, 高岩 wrote:
ngx_http_lua_socket_tcp_read函数
#if 1
if (rev->active && !rev->ready) {
rc = NGX_AGAIN;
break;
}
#endif
这一段可以去掉了,如果sock:receive不判断第一个返回值是否是nil,而继续读的情况下(误使用)
会导致无法判断连接是否失效,然后触发
if (rev->active) {
ngx_add_timer(rev, u->read_timeout);
} else if (rev->timer_set) {
ngx_del_timer(rev);
}
当nginx worker处于shutting down状态的情况下,会导致定时器不停的添加,导致进程无法退出
local len = string.len
local sock, err = ngx.req.socket()
if not sock then
return
end
ngx.req.init_body(128 * 1024)
sock:settimeout(0)
local content_length = nil
content_length=tonumber(ngx.req.get_headers()['content-length'])
local chunk_size = 4096
if content_length < chunk_size then
chunk_size = content_length
end
local size = 0
while size < content_length do
local data, err, partial = sock:receive(chunk_size)
data = "" or partial
if not data then
return
end
size = size + len(data)
local less = content_length - size
if less < chunk_size then
chunk_size = less
end
end
ngx.req.finish_body()
这个代码的问题是
local data, err, partial = sock:receive(chunk_size)
data = "" or partial
if not data then
return
end
如果接收的时候,没有全部接收完毕,连接异常断开的情况,此时partial是一个字符串,或者空字符串
n = ngx_http_lua_socket_read_error_retval_handler(r, u, L);
lua_pushliteral(L, "");
return n + 1;
所以这里data="">
In case of success, it returns the data received; in case of error, it returns nil
with a string describing the error and the partial data received so far.
所以这里是不是因为连接错误而退出(错误使用),也不会退出循环,因为size < content_length无法达成,连接已经异常断开
此时再次调用sock:receive,也就是
ngx_http_lua_socket_tcp_receive
ngx_http_lua_socket_tcp_read
而之前在c->recv时,连接异常断开,ngx_unix_recv调用过程中,会导致rev->ready = 0;
if (rev->active && !rev->ready) {
rc = NGX_AGAIN;
break;
}
而rev->active只有在ngx_epoll_del_connection或者ngx_epoll_del_event的时候,才会置为0(我们用的epoll)
对于这个请求,已经进入了死循环,但是因为sock:receive是异步的,nginx worker并不会hang死
if (rev->active && !rev->ready) {
rc = NGX_AGAIN;
break;
}
所以这个地方在这种错误使用的case下,并不能判断连接是否异常关闭了
就会导致NGX_AGAIN的情况下,再次添加定时器
ngx_http_do_read_client_request_body
ngx_http_upstream_process_non_buffered_request
ngx_event_pipe_read_upstream
这几个类似接收client/upstream数据的地方都没有这个判断,rev->ready这个一般用于决定是否添加定时器,连接状态还是得recv一下