关于lua阻塞nginx进程并导致80端口无法连接的情况

223 views
Skip to first unread message

iammutex

unread,
May 12, 2014, 5:50:17 AM5/12/14
to open...@googlegroups.com
最近线上出现了一次故障,现象是一组使用lua+nginx的服务80端口无法访问。

lua主要做的事是从redis里获取数据后进行逻辑判断再返回给客户端

Redis连接使用的是:lua-redis-parser

由于现在多台机器同时出问题,怀疑是中心化的存储导致的,也就是说Redis导致的。所以我的怀疑是:

1.由于lua连接redis取数据的过程出现问题(慢、连接失败、丢包等等),导致lua被block
2.lua的block导致nginx进程被block(是否会?)
3.nginx进程被block导致80端口无法访问(是否会?)

于是我做了下面的实验:

用tcpcopy从线上引流到一台测试机
测试机连接测试的redis
测试redis使用redis 的debug sleep命令模拟redis操作阻塞

这时候能看到,一旦redis开始阻塞,nginx 的 access log马上停掉,手动curl请求nginx一直block住等待。

不知道我的猜测是否正确,又有什么解决方法,请各位指点一下。

iammutex

unread,
May 12, 2014, 5:53:49 AM5/12/14
to open...@googlegroups.com
看到春哥曾经的回复如下,那么也就是说之前猜测是对的,那么如果lua因为连接redis阻塞了nginx,nginx被阻塞后是有可能直接80端口都无法访问吗?

========================

ngx_lua 非阻塞的保证限制在 ngx_lua 自身提供的 Nginx API for Lua 的范围内:

    http://wiki.nginx.org/HttpLuaModule#Nginx_API_for_Lua

使用标准 Lua 的 io 模块是肯定会阻塞的,所以应当尽量避免。

========================


aaashun

unread,
May 12, 2014, 7:52:35 AM5/12/14
to open...@googlegroups.com
lua-redis-parser只是redis相关协议的解析器, 本身没有io操作.
你用的是lua-resty-redis吧?
如果是的话, 那你说的redis相关操作是通过cosocket完成的, 是不会阻塞nginx的主循环的.

你可以尝试gdb -p {nginx-worker-pid}, 根据上下文调用堆栈再一下分析原因

Yichun Zhang (agentzh)

unread,
May 12, 2014, 2:53:00 PM5/12/14
to openresty
Hello!

2014-05-12 2:50 GMT-07:00 iammutex:
> 最近线上出现了一次故障,现象是一组使用lua+nginx的服务80端口无法访问。
>
> lua主要做的事是从redis里获取数据后进行逻辑判断再返回给客户端
>
> Redis连接使用的是:lua-redis-parser
>

lua-redis-parser 并不会进行任何 IO 操作,它只是一个 redis 协议的纯解析器和构造器。既然你使用了
lua-redis-parser,那么你使用的是 ngx_redis2 模块配合 ngx.location.capture()?

注意,openresty 提供了两种非阻塞方式访问 redis 的方式:

1. ngx_redis2 + ngx.location.capture() + lua-redis-parser:

https://github.com/openresty/redis2-nginx-module#readme

https://github.com/openresty/lua-nginx-module#ngxlocationcapture

2. 是 lua-resty-redis 库:

https://github.com/openresty/lua-resty-redis#readme

这两种方式下的所有 IO 操作都是 100% 非阻塞的。当然,出于性能、简单性和灵活性方面的考虑,我推荐使用第二种方式,即使用 lua-resty-redis。

> 由于现在多台机器同时出问题,怀疑是中心化的存储导致的,也就是说Redis导致的。所以我的怀疑是:
>
> 1.由于lua连接redis取数据的过程出现问题(慢、连接失败、丢包等等),导致lua被block

如果你使用的是 OpenResty 提供的 redis 客户端(即上面列举的两种),则 nginx 永远不会 block 在 IO 等待上,你丢多少个包都没事。

> 2.lua的block导致nginx进程被block(是否会?)

这取决于你的 Lua 代码是如何编写的。下列 Lua 编程错误会导致 nginx 事件循环被严重阻塞:

1. 如果你在 Lua 里写了一个死热循环,比如

while true do end

则当然会阻塞 nginx 进程。

2. 如果你在 Lua 里面进行*大量的*文件 IO 操作,这些文件 IO 操作也会阻塞 nginx 进程。

3. 如果你在 Lua 里面使用第三方的没有基于 ngx_lua 模块提供的 cosocket API 的 redis
等客户端库,则这些第三方 Lua 库必会阻塞 nginx 事件循环,比如下面这个库:

https://github.com/nrk/redis-lua (不要用这个库!必会阻塞 nginx!)

> 3.nginx进程被block导致80端口无法访问(是否会?)
>

当出现这种情况时,你应当观察你的 nginx worker 进程的状态,比如 CPU 占用和内存占用等等。

如果 CPU 很高,则表明它阻塞在 CPU 计算上,此时建议使用 on-CPU 火焰图定位问题:

https://github.com/openresty/nginx-systemtap-toolkit#sample-bt

https://github.com/openresty/stapxx#lj-lua-stacks

如果 CPU 很低,则表明它阻塞在某些系统调用上(比如阻塞 IO 或者 mutex
锁相关的系统调用上),或者被当前操作系统中的其他进程抢夺了太多的 CPU 资源。此时可以通过 off-CPU 火焰图加以分析问题的具体所在:

https://github.com/openresty/nginx-systemtap-toolkit#sample-bt-off-cpu

> 于是我做了下面的实验:
>
> 用tcpcopy从线上引流到一台测试机
> 测试机连接测试的redis
> 测试redis使用redis 的debug sleep命令模拟redis操作阻塞
>
> 这时候能看到,一旦redis开始阻塞,nginx 的 access log马上停掉,手动curl请求nginx一直block住等待。
>

请提供一个最小化了的但仍然完整的例子(包括 Lua 代码、相关 nginx 配置和你具体的操作步骤),以便我们可以在自己的环境里复现你看到的问题。

> 不知道我的猜测是否正确,又有什么解决方法,请各位指点一下。
>

在尝试解决问题之前,我们应当先确认和定位问题。毕竟反复试错是很浪费时间的。我在上面已经向你提供了火焰图工具。它们同时适合在生产环境使用。

Regards,
-agentzh

Yichun Zhang (agentzh)

unread,
May 12, 2014, 2:56:13 PM5/12/14
to openresty
Hello!

2014-05-12 2:53 GMT-07:00 iammutex:
> 看到春哥曾经的回复如下,那么也就是说之前猜测是对的,那么如果lua因为连接redis阻塞了nginx,nginx被阻塞后是有可能直接80端口都无法访问吗?
>
> ========================
>
> ngx_lua 非阻塞的保证限制在 ngx_lua 自身提供的 Nginx API for Lua 的范围内:
>
> http://wiki.nginx.org/HttpLuaModule#Nginx_API_for_Lua
>
> 使用标准 Lua 的 io 模块是肯定会阻塞的,所以应当尽量避免。
>
> ========================
>

我看不出来你是如何从我那个回复得出你的结论的。我前一封邮件列举的 OpenResty 访问 redis
的两种方式都符合我上面这个回复里的条件。ngx_lua 模块提供的 Lua API 可是非常丰富的,你有点进那个链接仔细查看吗?

Regards,
-agentzh

Yichun Zhang (agentzh)

unread,
May 13, 2014, 3:26:24 PM5/13/14
to iammutex, openresty
Hello!

2014-05-13 2:34 GMT-07:00 iammutex:
> 你说使用Lua的io模块肯定会阻塞的,应当尽量避免。我是从这句话里得到我的结论的,当时认为采用的Redis模块就是用Lua的io模块实现的,所以做此判断。
>

市面上有那么多那么多的 Redis 模块,你为什么不具体说明你使用的是哪一个模块呢?

另外,请尽量不要想当然,以节约你我和大家的时间。当你实在无法确定时,你可以询问,而不是直接把你的假想作为推理的事实依据。这会让我和其他人感到困惑。

> "我前一封邮件列举的 OpenResty 访问 redis 的两种方式" - 不清楚您前一封邮件是哪一封邮件的哪两种方式。
>

请检查你的 openresty 邮件列表设置,关闭 digest 功能。我所说的回复是下面这个链接(从中国大陆访问该页面可能需要翻墙):

https://groups.google.com/d/msg/openresty/94dQwn9zikg/zUpynuDaTIkJ

> PS:后续查到的原因是lua采用luasql连接mysql导致的阻塞。
>

请使用 OpenResty 自己的 lua-resty-mysql 库,它是 100% 非阻塞的:

https://github.com/openresty/lua-resty-mysql#readme

它也默认包含在了 OpenResty 安装中,可以直接使用。

继续将此讨论抄送给 openresty 中文邮件列表。

Regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages