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