太棒了,我这有个非常接近的场景的...加入周期的ngx.flush(true)解决了大部分的场景...
https://github.com/knyar/nginx-lua-prometheus/issues/25
这个prometheus插件的collect()全都是用ngx.say(...),我这里有个六万多条的返回。使用curl测试即使不输出到文件偶尔也会卡住,然后cpu100%;
在 2017年10月17日星期二 UTC+8上午3:20:11,agentzh写道:
Hello!
ngx.say() 和 ngx.print() 是异步的,所以你用一个热循环不断地调用肯定会堵住。
你应该在热循环里定期地调用 ngx.flush(true) 把数据实际刷出本地的 send buffer。这是我们提供 ngx.flush(true) 接口的目的。
一般地,对于 CPU 过于密集的计算,可以定期通过 ngx.sleep(0) 把控制权暂时交还给 nginx 事件循环。毕竟我们的 I/O 模型是协作式的,而非抢占式的。你写一个死循环必然堵住了。
Regards,
Yichun
2017-10-09 22:20 GMT-07:00 Jerry Gao
<serge...@gmail.com>:
现象
* curl收到部分内容后会卡住,不再收到内容
* nginx worker占满单核心
配置
location /t {
content_by_lua_block {
for i = 1,100000 do
ngx.say("hello")
end
}
}
请求
使用curl请求,并将输出打到磁盘等慢速设备
进程状态
![]()
![]()
![]()
分析了下原因:
在ngx_linux_sendifle_chain中
if ((size_t) (send - prev_send) != sent) {
#if (NGX_THREADS)
if (thread_handled) {
return in;
}
if (thread_complete) {
send = prev_send + sent;
continue;
}
#endif
wev->ready = 0;
return in;
}
考虑如下时序:
- 当接收端较慢,实际发送的字节数要小于预期发送的字节数,wev->ready被置为0
- 由于content_by_lua的for循环不放弃CPU,nginx无法进入事件循环,wev->ready无法被置为1
- wev-ready==0,发送buffer无法发送,越来越大,处理时间变得越来越长
当前模块本身提供的ngx.flush可以解决,ngx.say之后及时调用ngx.flush(true)
但上述例子中的使用方法导致的结果:
- wev->ready无法再置为1,即便系统能够发送数据,也因此无法再发送
- nginx worker 阻塞
其中1不是预期的也是不可接受的
这里是否应该处理?进一步,应该如何修改?
--