在OpenResty中須要避免全局變量的使用

lua-variable-scope

在代碼中導入模塊時應注意一些細節,推介使用以下格式:nginx

local xxx = require('xxx')

而非:git

require('xxx')

理由以下:從設計上講,全局環境的生命週期和一個Nginx的請求的生命週期是相同的。爲了作到會話隔離,每一個請求都有本身的Lua全局變量環境。Lua模塊在第一次請求打到服務器上的時候被加載起來,經過package.loaded表內建的require()完成緩存,爲後續代碼複用。而且一些Lua模塊內的module()存在邊際問題,對加載完成的模塊設置成全局表變量,可是這個全局變量在請求處理最後將被清空,而且每一個後續請求都擁有本身(乾淨)的全局空間。因此它將由於訪問nil值收到Lua異常。github

通常來講,在ngx_lua的上下文中使用Lua全局變量真的不是什麼好主意:緩存

  1. 濫用全局變量的反作用會對併發場景產生反作用,好比當使用者把這些變量看做是本地變量的時候;服務器

  2. Lua的全局變量須要向上查找一個全局環境(只是一個Lua表),代價比較高;併發

  3. 一些Lua的全局變量引用只是拼寫錯誤,這會致使出錯很難排查。ide

因此,咱們極力推介在使用變量的時候老是使用local來定義以限定起生效範圍是有理由的。工具

使用工具(lua-releng tool)[https://github.com/openresty/nginx-devel-utils/blob/master/lua-releng]查找你的Lua源文件:ui

$ lua-releng     
Checking use of Lua global variables in file lib/foo/bar.lua ...  
    1       [1489]  SETGLOBAL       7 -1    ; contains
    55      [1506]  GETGLOBAL       7 -3    ; setvar
    3       [1545]  GETGLOBAL       3 -4    ; varexpand

上述輸出說明文件lib/foo/bar.lua的1489行寫入一個名爲contains的全局變量,1506行讀取一個名爲setvar的全局變量,1545行讀取一個名爲varexpand的全局變量,this

這個工具能保證Lua模塊中的局部變量所有是用local關鍵字定義過的,不然將會拋出一個運行時庫。這樣能阻止相似變量這樣的資源的競爭。理由請參考(Data Sharing within an Nginx Worker)[http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker]

翻譯:王院生
做者:agentzh

English source:

Lua Variable Scope

Care must be taken when importing modules and this form should be used:

local xxx = require('xxx')

instead of the old deprecated form:

require('xxx')

Here is the reason: by design, the global environment has exactly the same lifetime as the Nginx request handler associated with it. Each request handler has its own set of Lua global variables and that is the idea of request isolation. The Lua module is actually loaded by the first Nginx request handler and is cached by the require() built-in in the package.loaded table for later reference, and the module() builtin used by some Lua modules has the side effect of setting a global variable to the loaded module table. But this global variable will be cleared at the end of the request handler, and every subsequent request handler all has its own (clean) global environment. So one will get Lua exception for accessing the nil value.

Generally, use of Lua global variables is a really really bad idea in the context of ngx_lua because

  1. misuse of Lua globals has very bad side effects for concurrent requests when these variables are actually supposed to be local only,

  2. Lua global variables require Lua table look-up in the global environment (which is just a Lua table), which is kinda expensive, and

  3. some Lua global variable references are just typos, which are hard to debug.

It's highly recommended to always declare them via "local" in the scope that is reasonable.

To find out all the uses of Lua global variables in your Lua code, you can run the lua-releng tool across all your .lua source files:

$ lua-releng
Checking use of Lua global variables in file lib/foo/bar.lua ...
        1       [1489]  SETGLOBAL       7 -1    ; contains
        55      [1506]  GETGLOBAL       7 -3    ; setvar
        3       [1545]  GETGLOBAL       3 -4    ; varexpand

The output says that the line 1489 of file lib/foo/bar.lua writes to a global variable named contains, the line 1506 reads from the global variable setvar, and line 1545 reads the global varexpand.

This tool will guarantee that local variables in the Lua module functions are all declared with the local keyword, otherwise a runtime exception will be thrown. It prevents undesirable race conditions while accessing such variables. See Data Sharing within an Nginx Worker for the reasons behind this.

Back to TOC

相關文章
相關標籤/搜索