OpenResty中Lua变量的使用

全局变量

在 OpenResty 里面,只有在 init_by_lua* 和 init_worker_by_lua* 阶段才能定义真正的全局变量。 这是因为其他阶段里面,OpenResty 会设置一个隔离的全局变量表,以免在处理过程污染了其他请求。 即使在上述两个可以定义全局变量的阶段,也尽量避免这么做。全局变量能解决的问题,用模块变量也能解决, 而且会更清晰、更干净。

模块变量

把定义在模块里面的变量称为模块变量。无论定义变量时有没有加 local,有没有通过 _M 把变量引用起来, 定义在模块里面的变量都是模块变量。
在使用Lua语言内置的require方法加载模块,之后就可以在Lua中操作模块变量,加载模块的操作只会执行一次(在同一个nginx worker中),所有的协程都会共享同一份拷贝(包括代码和数据)。基于这种协程间共享技术是高性能Lua应用的基础。

  • Lua协程执行流程:
    在这里插入图片描述

注意,这种数据共享的方式是基于Nginx Worker内运行的Lua协程,不能跨Worker之间的进程边界。
在使用这种方式共享时,仅推荐共享只读数据,当计算过程中没有非阻塞性 I/O 操作时(包括 ngx.sleep), 也可以在 nginx worker 进程内所有并发请求中共享可改变的数据。如果出现阻塞操作,会导致协程切换,共享可变数据则会造成数据错乱。

  • 共享只读数据
 -- mydata.lua
 local _M = {}

 local data = {
     dog = 3,
     cat = 4,
     pig = 5,
 }

 function _M.get_age(name)
     return data[name]
 end

 return _M

nginx.conf配置中加载:

 location /lua {
     content_by_lua_block {
         local mydata = require "mydata"
         ngx.say(mydata.get_age("dog"))
     }
 }
  • 共享可变数据
-- var.lua
local count = 1

local _M = {}

local function add()
    count = count + 1
end

local function sub()
    count = count - 1
end

function _M.calc()
    add()
    -- 模拟协程调度
    ngx.sleep(ngx.time()%0.003)
    sub()
    return count
end

return _M
-- index.lua
local var = require "var"

if var.calc() == 1 then
    ngx.say("ok")
else
    ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
    ngx.say("error")
end

nginx.conf中引用:

 location = /index {
     content_by_lua_file conf/lua/index.lua;
 }

以上的示例在单个客户端和两个客户端请求下的测试情况如下:

[root@localhost wrk]# ./wrk -t 1 -c 1 -d 1s http://localhost:6666/index 
Running 1s test @ http://192.168.170.150:6666/index
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   552.73us  661.80us   2.54ms   78.76%
    Req/Sec     4.41k     8.22k   20.27k    80.00%
  4370 requests in 1.00s, 0.85MB read
Requests/sec:   4348.51
Transfer/sec:    866.19KB

[root@localhost wrk]# ./wrk -t 2 -c 2 -d 1s http://localhost:6666/index 
Running 1s test @ http://192.168.170.150:6666/index
  2 threads and 2 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   440.60us  610.98us   2.70ms   80.39%
    Req/Sec     9.60k    11.81k   27.40k    70.00%
  19113 requests in 1.00s, 3.78MB read
  Non-2xx or 3xx responses: 2729
Requests/sec:  19043.00
Transfer/sec:      3.76MB

在两个客户端访问时出现部分请求错误,那些出现异常的请求Lua协程调度执行过程大概如下:

coroutine Acoroutine Bcount
add2
sleep2
add3
sleep3
sub2

(2 != 1) => HTTP_INTERNAL_SERVER_ERROR!

本地变量

跟全局变量、模块变量相对,把 *_by_lua* 里面定义的变量称之为本地变量。 本地变量仅在当前阶段有效,如果要跨阶段使用,需要借助 ngx.ctx 或者附加在模块变量里。
注意的是 ngx.timer.*。虽然 timer 代码占的是别的上下文的位置,但是每个 timer 都是运行在自己的协程里面, 里面定义的变量都是协程内部的。
举个例子,让我们在 init_worker_by_lua_block 里面定义一个 timer。

init_worker_by_lua_block {
    local delay = 5
    local handler
    handler = function()
        counter = counter or 0
        counter = counter + 1
        ngx.log(ngx.ERR, counter)
        local ok, err = ngx.timer.at(delay, handler)
        if not ok then
            ngx.log(ngx.ERR, "failed to create the timer: ", err)
            return
        end
    end
    local ok, err = ngx.timer.at(delay, handler)
    if not ok then
        ngx.log(ngx.ERR, "failed to create the timer: ", err)
        return
    end
}

以上的counter虽然没有local修饰,并且定义在init_worker_by_lua*中,但是运行后会发现counter输出的都是1,主要是因为counter定义在handler这个函数内部,每个timer都运行在一个独立的协程里,timer的每次触发,都会重新把counter定义一遍。

参考资料

https://moonbingbing.gitbooks.io/openresty-best-practices/content/ngx_lua/lua-variable-scope.html
https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenResty 是一个基于 Nginx 的全功能 Web 应用服务器,它通过在 Nginx 进程嵌入 Lua 脚本解释器,使得开发者可以使用 Lua 脚本来扩展 Nginx 功能。 在 OpenResty ,不同阶段的处理共享一个 Lua 虚拟机实例,这使得在不同的阶段可以共享变量和其他资源。具体而言,OpenResty 支持以下几个阶段的共享: 1. 预配置阶段:在这个阶段,可以使用 Lua 脚本动态地配置 Nginx 的一些基本参数,如 worker 数量、最大连接数等。这些配置可以在不重启服务的情况下实时生效。 2. 初始化阶段:通过在 init_worker_by_lua 阶段执行的 Lua 脚本,可以共享一些需要在所有工作进程内预先初始化的数据和资源。例如,可以在这个阶段初始化数据库连接池或加载一些共享代码库,以提高后续请求的处理效率。 3. 请求处理阶段:在 access_by_lua 阶段,可以共享在初始化阶段初始化的数据和资源。这些数据和资源可以用于处理用户请求,如访问一些共享的内存缓存、全局变量等。 4. 日志阶段:在 log_by_lua 阶段,可以将请求处理得到的信息记录到一个共享的日志文件。这个共享的文件描述符可以在处理不同请求的不同阶段使用。 总之,OpenResty 的共享特性能够帮助开发者在不同阶段共享数据和资源,提高开发效率和性能。通过合理利用这些共享机制,可以实现更灵活、可扩展的 Web 应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值