OpenResty中Lua变量的使用

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

全局变量

在 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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6