我很难尝试围绕openresty(或nginx)的并发模型。我阅读了Lua variable scope,它解释了变量的生命周期,但它没有说明对它们的并发访问。
很难用文字解释,所以让我试着用代码来解释。想象一下,我有这个Lua模块:
local counter = {count = 0}
function counter.incr(amount)
counter.count = counter.count + (amount or 1)
end
return counter
然后我在openresty中使用它:
server {
location /incr {
content_by_lua '
local counter = require 'counter'
counter.incr(1)
'
}
location /decr {
content_by_lua '
local counter = require 'counter'
counter.incr(-1)
'
}
location /count {
content_by_lua '
local counter = require 'counter'
ngx.write(counter.count)
'
}
}
我想了解并发模型,以便回答这些问题:
/incr
进行10次并发呼叫,稍后再拨打/count
,我可以确定结果为10(我假设不是,但为什么)?/incr
并同时拨打10 /decr
,我可以确定/count
会返回0吗?init_by_lua
而不是content_by_lua
)如何影响结果?答案 0 :(得分:14)
nginx正在使用基于事件的体系结构,这意味着它使用单个线程 1 和一个事件循环,当它们准备好进行读取或写入时处理套接字。这意味着请求并非真正同时处理,但可以逐个快速处理多个请求,即使在存在任何套接字/ IO延迟的情况下,单个请求处理可能会有延迟。
如果我对/ incr进行10次并发调用,之后我会调用/ count,我可以确定结果是10(我假设不是,但为什么)?
是。只要在所有/count
请求完成后,/incr
被称为,结果就是10.想象一下,9个请求已经完成,但第10个请求是出于某种原因由发件人推迟,如果在第n个请求由nginx处理之前处理/count
,则结果应为9。
如果我对/ incr进行10次并发调用,同时又进行10次/ decr,我可以确定/ count会返回0吗?
是的,但不保证处理这些请求的顺序。请注意,在这种情况下,您不需要锁定您的状态或使用全局信号量或类似的东西。如果在读取状态和写入状态之间有一些I / O调用(因为在此期间可以处理不同的请求),可能会遇到麻烦,但这不是您的示例所做的。
工人数量如何影响结果?
Lua实例在同一个工作进程处理的请求之间共享,因此多个worker不会给你相同的结果。您的所有/incr
请求都可以发送给一个工作人员,但您的/count
请求可以转到另一个工作人员,而另一个Lua实例(仍然)将count
设置为0。如果您需要在实例之间共享数据,您可能需要使用类似lua_shared_dict的内容。有关其他选项,另请参阅data sharing部分。
代码发生的阶段(即init_by_lua而不是content_by_lua)如何影响结果?
init_by_lua
仅在主进程加载配置文件时执行。
1 我过于简单化了,因为它可以分叉多个实例来处理多核系统和其他一些情况以及我记得的情况。