ngx lua模組原始碼簡單解析
對nginx lua模組的整個流程,原理簡單解析。由於nginx lua模組相關配置,指令,API非常多,所以本文件只以content_by_lua指令舉例說明。
讀本文件最好配合讀原始碼. 不適合對nginx和lua一點都不瞭解的人看。
1.相關配置
詳細配置見 https://github.com/openresty/lua-nginx-module#installation2.原始碼解析
src/ngx_http_lua_module.c為模組主入口檔案模組型別:NGX_HTTP_MODULE
解析配置階段(從上到下按執行順序) | |
函式名 | 作用 |
ngx_http_lua_create_main_conf | create main configuration 建立main級別配置ngx_http_lua_main_conf_t 見本文件2.1.1 |
ngx_http_lua_create_loc_conf | create location configuration 建立location級別配置ngx_http_lua_loc_conf_t 見本文件2.1.2 |
ngx_http_lua_content_by_lua | 配置設定解析 見本文件2.2.1 |
ngx_http_lua_init_main_conf | init main configuration 解析完配置後,如果某項配置沒配,給予預設值 見本文件2.2.2 |
ngx_http_lua_merge_loc_conf | merge location configuration 合併location配置 見本文件2.2.3 |
ngx_http_lua_init | Postconfiguration 配置解析完後,進行的配置檢查操作 見本文件2.2.4 |
處理請求階段 | |
ngx_http_lua_content_handler(ngx_http_request_t *r) | 處理請求體,建立執行lua code的上下文環境 |
ngx_http_lua_content_handler_inline(ngx_http_request_t *r) | 這個函式被ngx_http_lua_content_handler呼叫。本函式實際呼叫lua code來處理請求 見本文件2.3 |
2.1配置
由於配置太多。 使用content_by_lua指令舉例
static ngx_command_t ngx_http_lua_cmds[] = {
/* content_by_lua <inline script> */
{ ngx_string("content_by_lua"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_lua_content_by_lua,
NGX_HTTP_LOC_CONF_OFFSET,
0,
(void *) ngx_http_lua_content_handler_inline },
。。。
}
content_by_lua
語法 : content_by_lua <lua-script-str>
預設值: 無
上下文: location, location if
說明: 行為類似與一個“content handler”,給每個請求執行定義於lua-script-str中的lua code。這lua code也許會呼叫API calls,並且在一個獨立的全域性環境(例如 沙盒)中作為一個新的spawned coroutine執行
不要將這個指令和其他content handler指令在相同的location中使用。例如這個指令和proxy_pass指令不能同時使用在相同的location中
2.1.1 Main configuration
結構體 ngx_http_lua_main_conf_t 部分成員 配置項
ngx_http_lua_main_conf_t | |
lua_State *lua | Postconfiguration階段ngx_http_lua_init中被建立。Lua的狀態機 |
ngx_str_t lua_path; | Lua_package_path指定,指定的指令碼所使用的Lua模組搜尋路徑 |
ngx_str_t lua_cpath; | Lua_package_cpath指定,設定Lua c模組搜尋路徑 |
ngx_http_lua_conf_handler_pt init_handler | Value為ngx_http_lua_init_by_inline init_by_lua中指定 Value為ngx_http_lua_init_by_file init_by_lua_file中指定 在Postconfiguration階段ngx_http_lua_init中執行 |
Ngx_str_t init_src | Init_by_lua[_file] 指定lua原始碼,在Postconfiguration階段ngx_http_lua_init中執行 |
unsigned requires_capture_filter:1 | ngx_http_lua_content_by_lua中賦值為1 |
2.1.2 Location configuration
ngx_http_lua_loc_conf_t部分成員,配置項
ngx_http_lua_loc_conf_t | |
ngx_flag_t force_read_body | 是否強制讀取request body配置lua_need_request_body |
ngx_flag_t enable_code_cache | 是否使能code cache 預設是1 配置lua_code_cache |
ngx_http_handler_pt content_handler | Content_by_lua 指令 Cmd->post ngx_http_lua_content_handler_inline |
ngx_http_complex_value_t content_src | Content_by_lua 的lua code字串 |
u_char *content_src_key | Cached key for content_src 指令content_by_lua lua code的key。內容為 “nhli_” + md5 + ‘\0’值 |
Ngx_flag_t check_client_abort | 檢查client是否關閉連線 配置lua_check_client_abort |
2.2 配置解析
2.2.1 content_by_lua指令解析
ngx_http_lua_content_by_lua合法性檢查
1.將lua code儲存在location配置中,key是“nhli_” + md5(src) + ‘\0’
2.註冊在content handler階段執行的函式ngx_http_lua_content_handler
llcf->content_src.value = value[1];
llcf->content_src_key = p;
p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN);
p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len);
*p = '\0';
llcf->content_handler = (ngx_http_handler_pt) cmd->post;
lmcf->requires_capture_filter = 1; // main conf
clcf->handler = ngx_http_lua_content_handler; // register location content handler
2.2.2 Main部分配置預設值
ngx_http_lua_init_main_conf如果以下配置沒被顯式指定,那麼將被設定成以下值
ngx_http_lua_main_conf_t
regex_cache_max_entries 1024
regex_match_limit 0
max_pending_timers 1024
max_running_timers 256
2.2.3 Loc部分配置合併
如果在http 裡面和location裡面都有這個配置,那麼就以location配置為準2.2.4 解析完配置後初始化操作
在函式ngx_http_lua_init中功能:
1.建立lua_State
2.註冊nginx api for lua。 註冊lua code cache regex cache socket pool等全域性變數
3. 執行init_by_lua指令中lua code
if (multi_http_blocks || lmcf->requires_capture_filter) {// require_capture_filter 為1
rc = ngx_http_lua_capture_filter_init(cf);//
if (rc != NGX_OK) {
return rc;
}
}
lmcf->postponed_to_rewrite_phase_end = 0;
if (lmcf->lua == NULL) {
ngx_http_lua_init_vm(cf, lmcf); // init lua vm
lmcf->init_handler(cf->log, lmcf, lmcf->lua); // init_by_lua中的內容
}
註冊nginx api for lua
2.3 請求處理
2.3.1 Content handler原始碼解析
1.編譯lua code成位元組碼,或者從code cache中得到位元組碼(code cache開關開啟)2.如果code cache開啟,儲存位元組碼
3.新建coroutine
4.將全域性變數設為coroutine位元組碼的環境
5.將request結構體指標存入coroutine全域性變數
6.執行coroutine
ngx_http_lua_content_handler_inline {
/* load Lua inline script (w/ cache) sp = 1 */
rc = ngx_http_lua_cache_loadbuffer(L, llcf->content_src.value.data,
llcf->content_src.value.len,
llcf->content_src_key,
"content_by_lua",
llcf->enable_code_cache ? 1 : 0);
return ngx_http_lua_content_by_chunk(L, r);
}
ngx_http_handler->ngx_http_core_run_phases->ngx_http_core_content_phase->ngx_http_lua_content_handler
3. 使用介面
Nginx解析完請求後,找到了對應虛擬主機的配置,通常情況下會經過以下幾個階段的處理:
NGX_HTTP_POST_READ_PHASE: 讀取請求內容階段 |
|
NGX_HTTP_SERVER_REWRITE_PHASE: Server請求地址重寫階段 |
|
NGX_HTTP_FIND_CONFIG_PHASE: 配置查詢階段 |
|
NGX_HTTP_REWRITE_PHASE: Location請求地址重寫階段 |
Rewrite_by_lua rewrite_by_lua_file set_by_lua |
NGX_HTTP_POST_REWRITE_PHASE: 請求地址重寫提交階段 |
|
NGX_HTTP_PREACCESS_PHASE: 訪問許可權檢查準備階段 |
|
NGX_HTTP_ACCESS_PHASE: 訪問許可權檢查階段 |
Access_by_lua access_by_lua_file |
NGX_HTTP_POST_ACCESS_PHASE: 訪問許可權檢查提交階段 |
|
NGX_HTTP_TRY_FILES_PHASE: 配置項try_files處理階段 |
|
NGX_HTTP_CONTENT_PHASE: 內容產生階段 |
Content_by_lua content_by_lua_file |
NGX_HTTP_LOG_PHASE: 日誌模組處理階段 |
Log_by_lua log_by_lua_file |
內容產生階段完成以後,生成的輸出會被傳遞到filter模組去進行處理。filter模組也是與location相關的。所有的fiter模組都被組織成一條鏈。輸出會依次穿越所有的filter,直到有一個filter模組的返回值表明已經處理完成。
比如gzip有關的。
4. C語言嵌入lua原理
C作為程式語言, lua作為擴充套件語言C和lua之間通訊關鍵內容是一個虛擬的棧。幾乎所有的API呼叫都是對棧上的值進行操作。資料交換也通過棧。
4.1 協程(coroutine)
每個nginx work程序有一個lua_State結構,在Postconfiguration階段建立,見4.2.4lua_State表示一個獨立的lua虛擬機器,會建立自己的棧。
所有的nginx api for lua儲存在lua_State的全域性變數中。
LUA_GLOBALSINDEX 全域性變數,儲存所有API 公共變數 | |
__ngx_req | ngx_http_request_t 結構體指標 |
ngx.arg | 所有引數 |
ngx.req | 請求相關的函式 |
ngx.log | 日誌相關的函式 |
ngx.OK ngx.DONE ngx.AGAIN | 定義的一些返回值 |
。。。。。。 | |
LUA_REGISTRYINDEX 儲存一些公共資料 Lua 提供了一個登錄檔,這是一個預定義出來的表,可以用來儲存任何 C 程式碼想儲存的 Lua 值。 這個表可以用偽索引 LUA_REGISTRYINDEX 來定位 |
|
ngx_http_lua_coroutines_key | 儲存所有協程的table |
ngx_http_lua_ctx_tables_key | Lua request ctx data table |
ngx_http_lua_socket_pool_key | Lua socket connection pool table 連線池 |
ngx_http_lua_regex_cache_key | Lua precompiled regex object cache 正則表示式物件池 |
ngx_http_lua_code_cache_key | register table to cache user code |
lua_State *lua_newthread (lua_State *L)
建立一個coroutine. 將其壓入堆疊。返回維護這個coroutine的lua_State指標,
新的狀態機繼承共享原有狀態機中的所有物件(比如一些 table), 但是它有獨立的執行堆疊。
4.2 nginx與lua之間的資料互動
4.2.1 將request結構體指標存入lua全域性變數
程式碼位置:lua-nginx-module/src/ngx_http_lua_util.h:ngx_http_lua_set_req這段程式碼只有三行,在每個請求過來後,lua code執行前,都會執行
lua_pushliteral(L, "__ngx_req");//將字串"__ngx_req"壓棧
lua_pushlightuserdata(L, r);// 將request指標壓棧r壓棧
lua_rawset(L, LUA_GLOBALSINDEX);
//將request指標存入全域性變數 global[“__ngx_req”] = r
lua_rawset類似於 lua_settable (lua_State *L, int index);,
作一個等價於 t[k] = v 的操作, 這裡 t 是一個給定有效索引 index 處的值, v 指棧頂的值, 而 k 是棧頂之下的那個值。
4.2.2 從lua全域性變數得到request結構體指標
程式碼位置:slua-nginx-module/src/ngx_http_lua_util.h:ngx_http_lua_get_req為ngx_http_lua_set_req的反操作
{
ngx_http_request_t *r;
lua_pushliteral(L, "__ngx_req");//將字串"__ngx_req"壓棧
lua_rawget(L, LUA_GLOBALSINDEX);//將全域性變數global[“__ngx_req”]壓棧
r = lua_touserdata(L, -1);//global[“__ngx_req”]即為light user data型別,將這個指標轉為ngx_http_request_t *型別
lua_pop(L, 1);//從棧頂彈出一個元素
return r;
}
4.2.3 註冊C函式,處理request指標
所有的nginx api for lua註冊在lua-nginx-module/src/ngx_http_lua_util.c:ngx_http_lua_inject_ngx_api中
與request有關的註冊在
lua-nginx-module/src/ngx_http_lua_util.c:ngx_http_lua_inject_req_api中
舉例說,註冊處理method的函式ngx.req.get_method:
lua_createtable(L, 0 /* narr */, 23 /* nrec */); /* .req */
{
//將處理request結構體的c函式壓棧
lua_pushcfunction(L, ngx_http_lua_ngx_req_get_method);
//給.req table賦值,相當於.req[“get_method”]=ngx_http_lua_ngx_req_get_method,並將棧首元素出棧
lua_setfield(L, -2, "get_method");
}
//等價於ngx[“req”] = req table
lua_setfield(L, -2, "req");
註冊函式的寫法有統一的格式:
static int
ngx_http_lua_ngx_req_get_method(lua_State *L)
{
int n;
ngx_http_request_t *r;
n = lua_gettop(L);
if (n != 0) {
return luaL_error(L, "only one argument expected but got %d", n);
}
r = ngx_http_lua_get_req(L);//從lua全域性變數得到request結構體指標,見4.2.2
if (r == NULL) {
return luaL_error(L, "request object not found");
}
ngx_http_lua_check_fake_request(L, r);//檢查r合法性
lua_pushlstring(L, (char *) r->method_name.data, r->method_name.len);//將method壓棧
return 1;
}
4.2.4 在lua指令碼中呼叫C函式,處理request
在4.3.3中,我們註冊了ngx.req.get_method方法。我們可以在nginx的配置檔案中呼叫。語法: method_name = ngx.req.get_method()
上下文: set_by_lua, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua**
獲取當前請求的請求方法名。得到像“GET”和”POST”這樣的字串
如果當前的請求是nginx 子請求,那麼子請求的方法名將返回
配置舉例
server {
listen 80 default_server;
listen 443;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
location / {
content_by_lua '
method = ngx.req.get_method();
ngx.say(method);
';
}
}
結果:
[email protected]:~/nginx1.4.3/logs$ curl -i http://127.0.0.1
HTTP/1.1 200 OK
Server: NexQloud/2.0.0
Date: Mon, 30 Jun 2014 11:21:33 GMT
Content-Type: application/octet-stream
Transfer-Encoding: chunked
Connection: keep-alive
GET
C如何從lua中得到資料
在4.2.4中content_by_lua指令指定的lua語句中,呼叫了ngx.say() 函式,ngx.say函式定義在lua-nginx-module/src/ngx_http_lua_output.c:ngx_http_lua_ngx_say
實際上呼叫的是ngx_http_lua_ngx_echo函式。
這個函式呼叫了lua_tolstring lua_toboolean lua_touserdata等函式,將lua棧中的資料彈出棧,轉化成C裡面的資料,然後nginx做相應的處理,
int lua_toboolean (lua_State *L, int index) 把指定的索引處的的 Lua 值轉換為一個 C 中的 boolean 值( 0 或是 1 )。lua_tocfunction 把給定索引處的 Lua 值轉換為一個 C 函式。 這個值必須是一個 C 函式;如果不是就返回 NULL 。
lua_tointeger 把給定索引處的 Lua 值轉換為 lua_Integer 這樣一個有符號整數型別。 這個 Lua 值必須是一個數字或是一個可以轉換為數字的字串 (參見 §2.2.1); 否則,lua_tointeger 返回 0
lua_tolstring 把給定索引處的 Lua 值轉換為一個 C 字串。 如果 len 不為 NULL , 它還把字串長度設到 *len 中。 這個 Lua 值必須是一個字串或是一個數字; 否則返回返回 NULL
lua_tonumber 把給定索引處的 Lua 值轉換為 lua_Number 這樣一個 C 型別。就是double型別
lua_tothread 把給定索引處的值轉換為一個 Lua 執行緒
lua_touserdata 如果給定索引處的值是一個完整的 userdata ,函式返回記憶體塊的地址。 如果值是一個 light userdata ,那麼就返回它表示的指標。 否則,返回 NULL 。
Lua_type LUA_TNIL , LUA_TNUMBER , LUA_TBOOLEAN , LUA_TSTRING , LUA_TTABLE , LUA_TFUNCTION , LUA_TUSERDATA , LUA_TTHREAD , LUA_TLIGHTUSERDATA
ngx_http_lua_ngx_echo函式呼叫了
ngx_http_lua_ngx_echo
{
。。。
for (i = 1; i <= nargs; i++) {
type = lua_type(L, i);
switch (type) {
case LUA_TNUMBER:
case LUA_TSTRING:
lua_tolstring(L, i, &len);
size += len;
break;
case LUA_TTABLE:
size += ngx_http_lua_calc_strlen_in_table(L, i, i,
0 /* strict */);
break;
case LUA_TLIGHTUSERDATA:
dd("userdata: %p", lua_touserdata(L, i));
if (lua_touserdata(L, i) == NULL) {
size += sizeof("null") - 1;
break;
}
continue;
default:
msg = lua_pushfstring(L, "string, number, boolean, nil, "
"ngx.null, or array table expected, "
"but got %s", lua_typename(L, type));
return luaL_argerror(L, i, msg);
}
}
。。。
}
相關推薦
ngx lua模組原始碼簡單解析
對nginx lua模組的整個流程,原理簡單解析。由於nginx lua模組相關配置,指令,API非常多,所以本文件只以content_by_lua指令舉例說明。 讀本文件最好配合讀原始碼. 不適合對nginx和lua一點都不瞭解的人看。 1.相關配置 詳細配置見 http
caffe原始碼簡單解析——Blob(1)
使用caffe也有一段時間了,但更多是使用Python的介面,使用現有的ImageNet訓練好的模型進行圖片分類。為了更好的瞭解caffe這個框架,也為了提高自己的水平,在對卷積神經網路有了一些研究之後,終於開始研讀caffe的原始碼了,今天看了Blob類的一些內容,做個總
Android網路框架:OKHttp原始碼簡單解析(一)
這是第一次解析原始碼並把它寫出來,在之前,我一直以為只要會用別人的輪子就好,做出實際的效果就行,對看原始碼對自己的能力提升不以為然。後來偶然聽到一句話:看別人的DEMO,你就可以會用輪子,但是要想用好輪子,還是得看原始碼。我覺得看原始碼有兩個方面的好處: 1
ThreadLocal原始碼簡單解析
ThreadLocal ThreadLocal我一開始接觸的時候,以為是“本地執行緒”搞的我雲裡霧裡的,看了內部實現後,這個Local應該稱為“區域性”。 在《多執行緒併發程式設計實戰》提到:維持執行緒封閉性的一種規範方法,這個類為每個使用該變數的執行緒都存有一份
C#軟體授權、註冊、加密、解密模組原始碼解析並製作註冊機生成license
最近做了一個綠色免安裝軟體,領導臨時要求加個註冊機制,不能讓現場工程師隨意複製。事出突然,只能在現場開發(離開現場軟體就不受我們控了)。花了不到兩個小時實現了簡單的註冊機制,稍作整理。 基本原理:1.軟體一執行就把計算機的CPU、主機板、BIOS、MAC地
Spring原始碼深度解析-1、Spring核心類簡單介紹
在更新JAVA基礎原始碼學習的同時,也有必要把Spring抓一抓,以前對於spring的程度僅在於使用,以及一點IOC/AOP的概念,具體深層的瞭解不是很深入,每次看了一點原始碼就看不下去,然後一轉眼都忘記看了啥。 所以這次專門買了書,來細細品味下Spring。 希望能從這一波學習中加強自己
django---admin模組原始碼解析
django有一套強大的admin後臺資料庫管理工具,通過url(r'^admin/', admin.site.urls)完成對已註冊model的增刪改成,註冊方法是admin.site.register(Publish) 我們建立一個app,然後建立一個mo
原始碼安裝nginx並配置Lua模組
今天需要在測試機佈置nginx測試環境,測試機不能聯網。所以只能用scp上傳nginx原始碼安裝。期間遇到不少問題 1.下載nginx原始碼 $wget 'http://sysoev.ru/nginx/nginx-1.2.6.tar.gz' 2.下載lua-nginx-
python原始碼PyObject簡單解析
1. python一切皆物件, 物件定義object.h /* Define pointers to support a doubly-linked list of all live heap objects. */ #define _PyObject_HEAD_EXTR
nginx原始碼閱讀(十五).事件模組小結(從解析配置到事件的處理)
前言 本小節主要是整理一下前幾節分析的nginx的核心模組的ngx_events_module以及事件模組,關於事件模組什麼時候初始化以及事件的處理等,因此不會涉及到太多具體的程式碼,主要是把握事件模組的整體。 配置項結構體的建立及賦值 在使
(一) Mybatis原始碼分析-解析器模組
Mybatis原始碼分析-解析器模組 原創-轉載請說明出處 1. 解析器模組的作用 對XPath進行封裝,為mybatis-config.xml配置檔案以及對映檔案提供支援 為處理動態 SQL 語句中的佔位符提供支援 2. 解析器模組parsing包 3. 解析器模組parsing包 Gene
「從零單排canal 05」 server模組原始碼解析
基於1.1.5-alpha版本,具體原始碼筆記可以參考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_reading/canal 本文將對canal的server模組進行分析,跟之前一樣,我們帶著幾個問題來看原始碼
「從零單排canal 06」 instance模組原始碼解析
基於1.1.5-alpha版本,具體原始碼筆記可以參考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_reading/canal instance模組比較簡單,我們重點了解以下幾個問題 instance配置模式有
「從零單排canal 07」 parser模組原始碼解析
基於1.1.5-alpha版本,具體原始碼筆記可以參考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_reading/canal 本文將對canal的binlog訂閱模組parser進行分析。 parser模組(綠
使用lua-nginx模組實現請求解析與排程
**系統版本及需求**: ``OS``:CentOS 7.7.1908 ``OpenResty``:1.15.8.2 [TOC] # 描述 lua-nginx-module模組是什麼: > It is a core component of OpenResty. If you are usin
MySQL配置文件簡單解析
spa art ini update rep 模式 buffer lda reads 1 [mysqld] 2 basedir = /data/mysql 3 datadir = /data/mysqldata 4 tmpd
http協議簡單解析
orm 列表 partial 關閉連接 connect 時移 通過 ont sat HTTP協議(轉載自牛客網不知名大神) 1.簡介 HTTP協議(Hyper Text Transfer Protocol,超文本傳輸協議),是用於從萬維網(WWW:World Wide
lua中是 ffi 解析 【是如何處理數據包的/pkt是如何傳進去的】 fsfsfs
and 賦值 true dst cati multipl 又是 -- light lua中的ffi是如何解析的呢? 拿bcc中對proto的解析說起; metatype是有大學問的: ffi.metatype(ffi.typeof(‘struct ip_t‘), {
[ 轉載 ] Java基礎10--關於Object類下所有方法的簡單解析
zed final關鍵字 pro target 解釋 temp cat turn syn 關於Object類下所有方法的簡單解析 類Object是類層次結構的根類,是每一個類的父類,所有的對象包括數組,String,Integer等包裝類,所以了解Object是很有必要
Java之dom4j的簡單解析和生成xml的應用
util 讀寫 pro artifact gettext depend bject sta rgs 一、dom4j是一個Java的XML API,是jdom的升級品,用來讀寫XML文件的。dom4j是一個十分優秀的JavaXML API,具有性能優異、功能強大和極其易使