jinhailang / blog

技术博客:知其然,知其所以然
https://github.com/jinhailang/blog/issues
60 stars 6 forks source link

小心 ngx_lua 协程 #32

Open jinhailang opened 6 years ago

jinhailang commented 6 years ago

ngx_lua 中创建协程的方式主要有三种:

这些函数创建的协程本质都是 lua coroutine,但由不同的角色控制(yieldresume),使用场景也不同,使用场景范围最大的是 ngx.timer.at 几乎每个阶段都可以使用,其他两个方法调用限制较多,具体看上面的链接。

前面已经说过,协程是不会主动出让 CPU 时间片的,除非被挂起,阻塞或者代码执行出错。因此,用户创建的协程,如果不主动退出,比如代码进入了死循环,就无法切换执行其他的协程,而且,因为父协程执行的优先级比子协程低,所有请求处理都在同一个主协程内,如果子协程执行时间太长,就会导致 worker 进程请求处理缓慢,甚至卡死,影响整个 OpenResty 处理效率。

另一个问题,当在请求子协程(rewrite, access, content 阶段)内调用 ngx.thread.spawncoroutine.create 创建协程,可能会阻塞当前请求,因为请求子协程是用户创建协程的父协程,必须要所有子协程结束,才能退出,执行下个阶段。ngx.timer.at 创建的协程是与请求协程无关的,可以认为它的父线程是 worker 主协程,所以,当它阻塞时,并不会影响请求处理。

因为协程之间数据是异步的,所以,在协程内使用外部数据要格外小心,最好直接使用参数调用,避免出现非预期的数据变化。

ngx.timer.at 是延迟执行,可以递归实现定时执行的效果,delay 延迟参数不能设置的太小,否则会拖慢当前 worker 的请求处理速度。当需要定时执行的时候,可以直接使用 ngx.timer.every,更简洁,而且 delay 不能为 0

总之,最关键的是与其他语言(例如 Golang)不同, lua 虚拟机没有实现协程管理功能,完全由用户自己控制,使用不当会严重影响程序性能!