Nginx解讀內建非預設模組 ngx_http_stub_status_module
1 Background
http://nginx.org/en/docs/http/ngx_http_stub_status_module.html
ngx_http_stub_status_module 是一個 Nginx 的內建 HTTP 模組,該模組可以提供 Nginx 的狀態資訊。預設情況下這個模組是不被編譯進來的,所以在編譯 Nginx 時要指定載入該模組:
--with-http_stub_status_module
為什麼拿它做例子?因為它也是個足夠短小精悍的模組,是一個典型 handler 模組。那麼以後我們講解模組的過程,都是:
- 簡要的介紹
- 使用的例項
- 指令介紹
- 原始碼分析
2 Simple example
location /nginx_status { stub_status on; access_logoff;access_log
/
usr
/
local
/
nginx
/
logs
/
status.log;
#日誌
allow SOME.IP.ADD.RESS; deny all; }
我們假設你是在本機上實驗,並且開啟的是 80 埠,那麼在瀏覽器中輸入:
http://localhost/nginx_status
會看到這樣的資訊:
Active connections: 291 server accepts handled requests 16630948 16630948 31070465 Reading: 6 Writing: 179 Waiting: 106
其含義很容易理解:
-
第一行
- 當前的活躍連線數:291
-
第二行
- 伺服器已接受的連線數:16630948(accepted connection #)
- 伺服器已處理的連線數:16630948(handled connection #)
- 伺服器已處理的請求:31070465(可以算出,平均每個連線有 1.8 個請求)(handled connection #)
-
第三行
- Reading – Nginx 讀取的請求頭次數為 6;
- Writting – Nginx 讀取請求體、處理請求併發送響應給客戶端的次數為 179;
- Waiting – 當前活動的長連線數:106。
Nginx 官方的解釋如下:
active connections server accepts handled requests reading writing waiting
3 Directives
這個模組中的唯一一個指令,是:
stub_status
stub_status on
4 Source analysis
先看完整程式碼:
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> static char *ngx_http_set_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_command_tngx_http_status_commands[] = { { ngx_string("stub_status"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_set_status, 0, 0, NULL }, ngx_null_command }; static ngx_http_module_tngx_http_stub_status_module_ctx = { NULL,/* preconfiguration */ NULL,/* postconfiguration */ NULL,/* create main configuration */ NULL,/* init main configuration */ NULL,/* create server configuration */ NULL,/* merge server configuration */ NULL,/* create location configuration */ NULL/* merge location configuration */ }; ngx_module_tngx_http_stub_status_module = { NGX_MODULE_V1, &ngx_http_stub_status_module_ctx,/* module context */ ngx_http_status_commands,/* module directives */ NGX_HTTP_MODULE,/* module type */ NULL,/* init master */ NULL,/* init module */ NULL,/* init process */ NULL,/* init thread */ NULL,/* exit thread */ NULL,/* exit process */ NULL,/* exit master */ NGX_MODULE_V1_PADDING }; static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r) { size_tsize; ngx_int_trc; ngx_buf_t*b; ngx_chain_tout; ngx_atomic_int_tap, hn, ac, rq, rd, wr; if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; } rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } ngx_str_set(&r->headers_out.content_type, "text/plain"); if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } } size = sizeof("Active connections:\n") + NGX_ATOMIC_T_LEN + sizeof("server accepts handled requests\n") - 1 + 6 + 3 * NGX_ATOMIC_T_LEN + sizeof("Reading:Writing:Waiting:\n") + 3 * NGX_ATOMIC_T_LEN; b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; ap = *ngx_stat_accepted; hn = *ngx_stat_handled; ac = *ngx_stat_active; rq = *ngx_stat_requests; rd = *ngx_stat_reading; wr = *ngx_stat_writing; b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac); b->last = ngx_cpymem(b->last, "server accepts handled requests\n", sizeof("server accepts handled requests\n") - 1); b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq); b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n", rd, wr, ac - (rd + wr)); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; b->last_buf = 1; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } return ngx_http_output_filter(r, &out); } static char *ngx_http_set_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t*clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_status_handler; return NGX_CONF_OK; }
的確夠短小精悍吧?關鍵在於 Nginx 提供的模組擴充套件方式比較好,讓你可以少寫一些程式碼(NDK 可以讓你寫的更少,這是後話)。
4.1 模組定義 ngx_http_stub_status_module
ngx_module_tngx_http_stub_status_module = { NGX_MODULE_V1, &ngx_http_stub_status_module_ctx,/* module context */ ngx_http_status_commands,/* module directives */ NGX_HTTP_MODULE,/* module type */ NULL,/* init master */ NULL,/* init module */ NULL,/* init process */ NULL,/* init thread */ NULL,/* exit thread */ NULL,/* exit process */ NULL,/* exit master */ NGX_MODULE_V1_PADDING };
與此前介紹的 ngx_http_hello_world_module 並無本質區別。
4.2 命令集定義 ngx_http_status_commands
static ngx_command_tngx_http_status_commands[] = { { ngx_string("stub_status"), NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_set_status, 0, 0, NULL }, ngx_null_command };
命令集定義如上,得到如下資訊:
- name:stub_status
-
type:server conf、location conf、conf flag,其中最後一個比較陌生,相似的取值有:
#define NGX_CONF_ARGS_NUMBER 0x000000ff #define NGX_CONF_BLOCK 0x00000100 #define NGX_CONF_FLAG 0x00000200 #define NGX_CONF_ANY 0x00000400 #define NGX_CONF_1MORE 0x00000800 #define NGX_CONF_2MORE 0x00001000 #define NGX_CONF_MULTI 0x00002000
- set:ngx_http_set_status
下面解釋下一些 types:
4.2.1 NGX_CONF_XXX
以下巨集定義來自 ngx_conf_file.h:
#define NGX_CONF_NOARGS0x00000001 // 命令不接受引數 #define NGX_CONF_TAKE10x00000002 // 命令攜帶1個引數 #define NGX_CONF_TAKE20x00000004 // 命令攜帶2個引數 #define NGX_CONF_TAKE30x00000008 // 命令攜帶3個引數 #define NGX_CONF_TAKE40x00000010 // 命令攜帶4個引數 #define NGX_CONF_TAKE50x00000020 // 命令攜帶5個引數 #define NGX_CONF_TAKE60x00000040 // 命令攜帶6個引數 #define NGX_CONF_TAKE70x00000080 // 命令攜帶7個引數 #define NGX_CONF_TAKE12(NGX_CONF_TAKE1|NGX_CONF_TAKE2) // 命令攜帶1個或2個引數 #define NGX_CONF_TAKE13(NGX_CONF_TAKE1|NGX_CONF_TAKE3) // 命令攜帶1個或3個引數 #define NGX_CONF_TAKE23(NGX_CONF_TAKE2|NGX_CONF_TAKE3) // 命令攜帶2個或3個引數 #define NGX_CONF_TAKE123(NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3) // 命令攜帶1個、2個或3個引數 #define NGX_CONF_TAKE1234(NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4) // 命令攜帶1個、2個、3個或4個引數 #define NGX_CONF_ARGS_NUMBER 0x000000ff // 命令 #define NGX_CONF_BLOCK0x00000100 // 塊域,後面跟 {…},比如 server {...} #define NGX_CONF_FLAG0x00000200 // 命令接受“on|off”引數 #define NGX_CONF_ANY0x00000400 #define NGX_CONF_1MORE0x00000800 // 命令攜帶至少1個引數 #define NGX_CONF_2MORE0x00001000 // 命令攜帶至少2個引數 #define NGX_CONF_MULTI0x00002000 // 命令攜帶多個引數
4.3 上下文定義 ngx_http_stub_status_module_ctx
static ngx_http_module_tngx_http_stub_status_module_ctx = { NULL,/* preconfiguration */ NULL,/* postconfiguration */ NULL,/* create main configuration */ NULL,/* init main configuration */ NULL,/* create server configuration */ NULL,/* merge server configuration */ NULL,/* create location configuration */ NULL/* merge location configuration */ };
這個都是 NULL,夠簡單,無話可說了⋯⋯
4.4 命令設定函式 ngx_http_set_status
static char *ngx_http_set_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t*clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_status_handler; return NGX_CONF_OK; }
和 ngx_http_hello_world_module 對比下:
static char* ngx_http_hello_world(ngx_conf_t* cf, ngx_command_t* cmd, void* conf) { ngx_http_core_loc_conf_t* clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_hello_world_handler; ngx_conf_set_str_slot(cf, cmd, conf); return NGX_CONF_OK; }
唯一的區別,就是 ngx_http_hello_world_module 多了一句 ngx_conf_set_str_slot。這個先留做一個問題,後面會介紹,暫時與關鍵主題無關。
4.5 命令處理函式 ngx_http_status_handler
static ngx_int_t ngx_http_status_handler(ngx_http_request_t *r) { size_tsize; ngx_int_trc; ngx_buf_t*b; ngx_chain_tout; ngx_atomic_int_tap, hn, ac, rq, rd, wr;
這個模組要求接受的請求類是 GET、HEAD,其他型別的請求會被拒絕。
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; }
放棄請求體,因為這個模組用不上。
rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; }
如果請求是 HEAD 型別的,則直接設定響應頭的 content_type、status 欄位,併發送響應頭。
ngx_str_set(&r->headers_out.content_type, "text/plain"); if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } }
建立一個緩衝區,向緩衝區寫入我們上面在瀏覽器中看到的東西。
size = sizeof("Active connections:\n") + NGX_ATOMIC_T_LEN + sizeof("server accepts handled requests\n") - 1 + 6 + 3 * NGX_ATOMIC_T_LEN + sizeof("Reading:Writing:Waiting:\n") + 3 * NGX_ATOMIC_T_LEN; b = ngx_create_temp_buf(r->pool, size); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } out.buf = b; out.next = NULL; ap = *ngx_stat_accepted; hn = *ngx_stat_handled; ac = *ngx_stat_active; rq = *ngx_stat_requests; rd = *ngx_stat_reading; wr = *ngx_stat_writing; // 封裝了 sprintf b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac); // 封裝了 memcpy b->last = ngx_cpymem(b->last, "server accepts handled requests\n", sizeof("server accepts handled requests\n") - 1); b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq); b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n", rd, wr, ac - (rd + wr));
緩衝區寫完了。然後設定下響應頭的 status、content_length_n(還記得嗎?b->last - b->pos 剛好是緩衝區的第二個區域,是已寫入資料部分。)
r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; b->last_buf = 1;
傳送響應頭。
rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; }
filter。
return ngx_http_output_filter(r, &out); }
Linux公社的RSS地址 :ofollow,noindex" target="_blank">https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址:https://www.linuxidc.com/Linux/2018-10/154672.htm