Openresty中的并发模型是什么



我很难理解openresty(或nginx)的并发模型。我阅读了Lua变量作用域,它解释了变量的生存期,但它没有说明对它们的并发访问。

很难用语言来解释,所以让我试着用代码来解释。想象一下,我有一个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进行另外10次调用,我能确定/count会返回0吗
  • 员工数量如何影响结果
  • 代码发生的阶段(即init_by_lua而不是content_by_lua)如何影响结果

nginx使用基于事件的体系结构,这意味着它使用一个带有事件循环的单线程1,该事件循环在套接字准备好读取或写入时处理套接字。这意味着请求并不是真正同时处理的,但几个请求可以一个接一个地快速处理,即使如果存在任何套接字/IO延迟,单个请求处理可能会延迟。

如果我对/incr进行10次并发调用,然后再调用/count,我能确定结果会是10吗(我假设不会,但为什么)?

是。只要在所有/incr请求完成后调用/count,结果将为10。假设已经完成了9个请求,但是第10个请求由于某种原因被发送方延迟了,如果/count在第十个请求被nginx处理之前就被处理了,那么结果应该是9个。

如果我对/incr进行10次并发调用,同时对/decr进行另10次调用,我能确定/count会返回0吗?

是的,但不保证处理这些请求的顺序。请注意,在这种情况下,您不需要锁定状态或使用全局信号量或类似的东西。如果在读取状态和写回状态之间有一些I/O调用,则可能会遇到麻烦(因为在此期间可以处理不同的请求),但示例并非如此。

员工数量如何影响结果?

Lua实例在由同一工作进程处理的请求之间共享,因此多个工作进程不会给您相同的结果。您的所有/incr请求都可以发送到一个工作进程,但您的/count请求可以发送到不同的工作进程,其中不同的Lua实例(仍然)将count设置为0。如果需要在实例之间共享数据,则可能需要使用类似lua_shared_dict的东西。有关其他选项,请参阅有关数据共享的部分。

代码发生的阶段(即init_by_lua而不是content_by_lua)如何影响结果?

init_by_lua仅在主进程加载配置文件时执行。

1我过于简单化了,因为据我记忆所及,它可以派生多个实例来处理多核系统和其他一些情况。

最新更新