摘自:http://www.daileinote.com/computer/openresty/14
OpenResty ngx_lua 模块的 ngx.thread(轻量级线程) 可以实现并行的访问上游服务,其内部实现原理就是基于 Lua 的协程,只是由 ngx_lua 帮忙调度,隐藏了一些细节处理。
由 rewrite_by_lua、access_by_lua、content_by_lua 等运行的 Lua 代码本身就是协程,我们可以称之为入口点协程或者主协程,然后再由该协程调用 ngx.thread.spawn 再生成协程,可以称之为子协程或者用户协程。
默认情况下当前的handler(比如由 content_by_lua* 的内容阶段)是不会终止的,除非发生以下3中情况中的一种:
1.当主协程和所有用户协程都终止
2.当主协程或者子协程中的其中一个调用了 ngx.exit、ngx.exec、ngx.redirect、ngx.req.set_uri(uri, true)其中一种。
3.主协程发生错误终止
注意:其中一个子协程发生 Lua 错误并不会影响到其他子协程运行。
由于 nginx 自身的机制,一般情况下,在处理子请求时是不允许终止的。所以协程也一样,在处理子请求时也是不能终止,所以主协程必须要使用 ngx.thread.wait 来等待这些协程终止才能结束请求。不过调用 ngx.exit,传入状态码为 ngx.ERROR(-1),408,444,499 可以终止正在处理子请求的协程。
注意,协程是运行在非抢占式模式,所以它会独占cpu,除非
1.一次非阻塞io在一次运行不能完成
2.主动调用 coroutine.yield 交出执行权
3.发生 Lua 错误或者调用 ngx.exit、ngx.exec、ngx.redirect、ngx.req.set_uri(uri, true)其中一种。
前2中一般在稍后会重新运行,除非当前handler终止。
通过 coroutine.status() 可以获取协程状态,如果当前协程已经结束(不管是正常终止还是报错),父协程还在运行但是却没有调用 ngx.thread.wait,那么该协程的状态为僵尸状态(zombie)。
1 | local yield = coroutine.yield --别名 |
ngx.thread.wait
1 | syntax: ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...) |
等待一个或多个子协程终止,thread1,thread2 都是由 ngx.thread.spawn 返回的。
只有直接父协程才有资格 wait 自己的子协程。
1 | content_by_lua_block { |
wait 任意一个,只有有任意一个终止就会返回。
1 | local ok, res = wait(threads[0], threads[1], threads[2]) |
ngx.thread.kill
1 | syntax: ok, err = ngx.thread.kill(thread) |
终止正在运行的通过 ngx.thread.spawn 生成的协程,成功返回 true,失败返回 nil。
注意:只有父协程才能杀死子协程,如果子协程正在处理子请求,那么不能终止。
如果协程已经终止,那么返回 nil,err为 "already waited or killed"。
总结:使用协程时千万要注意,如果由于一些因素,子协程是一个死循环,或者永久等待,那么该连接不被被主动关闭,永远卡在该阶段的 handler,就算 reload 都没用,除非重启 nginx 才能终止。