nginx的 lua支持
一兰怠、nginx執(zhí)行步驟
nginx在處理每一個用戶請求時,都是按照若干個不同的階段依次處理的
post-read
讀取請求內(nèi)容階段你辣,nginx讀取并解析完請求頭之后就立即開始運行馅巷;server-rewrite
server請求地址重寫階段;find-config
配置查找階段格粪,用來完成當前請求與location配重塊之間的配對工作躏吊;rewrite
location請求地址重寫階段,當ngx_rewrite指令用于location中帐萎,就是再這個階段運行的比伏;post-rewrite
請求地址重寫提交階段,當nginx完成rewrite階段所要求的內(nèi)部跳轉(zhuǎn)動作疆导,如果rewrite階段有這個要求的話赁项;preaccess
訪問權(quán)限檢查準備階段,ngx_limit_req和ngx_limit_zone在這個階段運行,ngx_limit_req可以控制請求的訪問頻率悠菜,ngx_limit_zone可以控制訪問的并發(fā)度舰攒;access
權(quán)限檢查階段,ngx_access在這個階段運行悔醋,配置指令多是執(zhí)行訪問控制相關(guān)的任務摩窃,如檢查用戶的訪問權(quán)限,檢查用戶的來源IP是否合法篙顺;post-access
訪問權(quán)限檢查提交階段偶芍;try-files
配置項try_files處理階段;content
內(nèi)容產(chǎn)生階段德玫,是所有請求處理階段中最為重要的階段匪蟀,因為這個階段的指令通常是用來生成HTTP響應內(nèi)容的;log
日志模塊處理階段宰僧;
nginx_lua的常用模塊
在openwrt上我們一般利用nginx_lua的以下幾個功能
- set_by_lua:主要做一些參數(shù)配置時使用
- header_filter_by_lua:頭部請求信息的預處理
- access_by_lua_file:對應上面的第7階段材彪,我們可以在這里對業(yè)務請求信息做一些處理
- content_by_lua_file:對應上面的第10階段
- log_by_lua_file:對應上面的第11階段,在content_by_lua_file之后運行琴儿。
業(yè)務需求:本地文件下載打點(開始——結(jié)束)
- 需求分析:
- 路由器上放置一個靜態(tài)文件段化,記錄客戶端下載這個文件的下載開始和下載結(jié)束的過程。
- 靜態(tài)文件路由器端是放在location中登記的:
- 靜態(tài)文件的下載造成,由于http響應內(nèi)就是文件显熏,所以content_by_lua_file就不需要了。
- 下載請求的記錄晒屎,我們可以在content_by_lua_file中獲取喘蟆。
- 下載最終返回數(shù)據(jù)的記錄,我們可以在log_by_lua_file中獲取鼓鲁。
- nginx配置:
location ~* .(apk)$ {
root /tmp/loadapp;
add_header Content-Disposition attachment;
access_by_lua_file /system/nginx/lua/access_apk.lua;
log_by_lua_file /system/nginx/lua/log_apk.lua;
}
- 遇到的大問題:如何解決斷點續(xù)傳的打點蕴轨。
- 斷點續(xù)傳的大致原理:
- 客戶端向服務端并行發(fā)送多個http請求,http請求header中骇吭,會有一個range參數(shù)橙弱,range中包含了需要的文件片
- 服務端根據(jù)range信息取得響應的片返回給客戶端;
- 客戶端最終整合所有的文件片為一個文件燥狰。
- 斷點續(xù)傳是多請求多響應的棘脐,且下載是否完成是客戶端確認的,因此斷點續(xù)傳下龙致,路由器是無法準確知曉下載完畢的荆残。因此,如果需要對單個文件下載過程打點净当,就必須禁止斷點續(xù)傳。
- 斷點續(xù)傳的大致原理:
3. 如何針對單個文件禁止斷點續(xù)傳
- 我們得知nginx的服務端斷點續(xù)傳功能支持是在ngx_http_range_filter_module中的
static ngx_int_t
ngx_http_range_header_filter(ngx_http_request_t *r)
{
time_t if_range;
ngx_int_t rc;
ngx_http_range_filter_ctx_t *ctx;
if (r->http_version < NGX_HTTP_VERSION_10
|| r->headers_out.status != NGX_HTTP_OK
|| r != r->main
|| r->headers_out.content_length_n == -1
|| !r->allow_ranges)
{
return ngx_http_next_header_filter(r);
}
}
- 因此我們只需要在nginx收到http請求的時候,r->allow_ranges=0即可
- 于是我們在nginx_lua模塊的ngx_http_lua_req_method.c中像啼,封裝了set_ranges和get_ranges兩個模塊
static int
ngx_http_lua_ngx_req_set_ranges(lua_State * L)
{
ngx_http_request_t *r;
int range = luaL_checkinteger(L, 1);
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request object found");
}
r->allow_ranges = range;
return 1;
}
static int
ngx_http_lua_ngx_req_get_ranges(lua_State *L)
{
ngx_http_request_t *r;
r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "request object not found");
}
lua_pushinteger(L, r->allow_ranges);
return 1;
}
- 當收到http請求時俘闯,執(zhí)行set_ranges(0)
ngx.req.set_ranges(0)
- 我們在nginx端實現(xiàn)了對請求的range的攔截,但依然無法控制客戶端發(fā)出多個請求忽冻,因此nginx還是收到了多個http請求真朗。不過對nginx而言,這么多的請求中只有一個是返回給了具體的文件數(shù)據(jù)僧诚。因此我們只要找到這一對特殊的請求/響應即可遮婶。通過對tcpdump的分析,我們發(fā)現(xiàn)這個的請求的特征如下
local h1 = ngx.req.get_headers();
local range = h1["Range"];
local accept_encoding = h1["Accept_Encoding"];
if range == nil and accept_encoding == "identity" then