将 worker_processes 设为多进程(如 auto 或物理核心数)是解决 lua 阻塞导致全局响应延迟最直接有效的方法,因每个 worker 拥有独立 luavm 和事件循环,可实现协程级隔离与故障降级。

把 worker_processes 1 改成多进程,是解决 Lua 脚本阻塞全局响应最直接有效的办法。单 worker 模式下,所有请求共用一个事件循环和一个 LuaVM,一旦某个 Lua 协程因误操作(如长循环、阻塞调用)卡住,整个 worker 就无法处理新请求,表现为全站延迟或超时。
为什么单 worker 容易被 Lua 阻塞
OpenResty 中每个 worker 进程内只有一个 LuaVM,所有请求协程共享该 VM 的主线程调度权。虽然协程本身是协作式调度,但以下情况会破坏异步性:
- 在 Lua 中执行
os.execute("sleep 1")或同步文件读取io.open(...):read()—— 直接阻塞整个 worker 线程 - 写死循环且未主动让出控制权,例如
for i=1,1000000 do ... end而不插ngx.sleep(0) - 使用未封装为非阻塞的第三方 C 库,或错误调用 LuaJIT FFI 导致系统调用阻塞
推荐的 worker 配置与隔离策略
启用多个 worker 后,请求会被自动分发到不同进程,单个 worker 卡死只影响部分流量,大幅提升容错能力:
- 设置
worker_processes auto;,让 Nginx 自动匹配 CPU 核心数;生产环境建议显式设为物理核心数(如worker_processes 4;) - 配合
worker_cpu_affinity auto;绑定 CPU,减少上下文切换开销 - 每个 worker 拥有独立的 LuaVM 和共享字典(
lua_shared_dict),天然实现协程级隔离 - 避免跨 worker 共享状态依赖,如需通信,改用
lua-resty-worker-events或 Redis
对现有 Lua 脚本的关键加固措施
即使启用了多 worker,仍需规范 Lua 行为,防止局部恶化扩散:
- 禁用所有同步 IO:用
ngx.socket.tcp()替代socket.connect(),用ngx.location.capture()替代 HTTP 库的同步请求 - 长计算逻辑中插入
ngx.sleep(0),强制让出当前协程,交还调度权给事件循环 - 敏感操作加超时保护,如
local sock = ngx.socket.tcp(); sock:settimeout(3000) - 用
ngx.ctx存请求级数据,绝对不要用_G存状态——后者在多请求复用协程时会污染上下文
验证与监控建议
改完配置后,不能只看服务是否启动,要确认阻塞风险真正缓解:
- 用
ab -n 1000 -c 200 http://your.site/api或wrk -t4 -c200 -d30s http://your.site/api做基础压测,观察响应时间分布是否出现尖峰 - 检查 Nginx error log,搜索
"script timed out"或"lua coroutine yielded"异常提示 - 通过
nginx -T | grep worker_processes确认生效配置,避免 reload 失败导致旧配置残留 - 在关键 Lua 路径中加入
ngx.log(ngx.DEBUG, "start", ngx.now())和结束日志,定位慢点










