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_log off;
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– number of all open connectionsserver accepts handled requests– nginx accepted 16630948 connections, handled 16630948 connections (no one was closed just it was accepted), and handles 31070465 requests (1.8 requests per connection)reading– nginx reads request headerwriting– nginx reads request body, processes request, or writes response to a clientwaiting– keep-alive connections, actually it is active – (reading + writing)
3 Directives
這個模塊中的唯一一個指令,是:
stub_status
- 語法:
stub_status on - 作用域:location
- 功能:統計這個 location 的信息。
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_t ngx_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_t ngx_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_t ngx_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_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; ngx_atomic_int_t ap, 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 requestsn") - 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 requestsn", sizeof("server accepts handled requestsn") - 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_t ngx_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_t ngx_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_NOARGS 0x00000001 // 命令不接受參數 #define NGX_CONF_TAKE1 0x00000002 // 命令攜帶1個參數 #define NGX_CONF_TAKE2 0x00000004 // 命令攜帶2個參數 #define NGX_CONF_TAKE3 0x00000008 // 命令攜帶3個參數 #define NGX_CONF_TAKE4 0x00000010 // 命令攜帶4個參數 #define NGX_CONF_TAKE5 0x00000020 // 命令攜帶5個參數 #define NGX_CONF_TAKE6 0x00000040 // 命令攜帶6個參數 #define NGX_CONF_TAKE7 0x00000080 // 命令攜帶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_BLOCK 0x00000100 // 塊域,后面跟 {…},比如 server {...} #define NGX_CONF_FLAG 0x00000200 // 命令接受“on|off”參數 #define NGX_CONF_ANY 0x00000400 #define NGX_CONF_1MORE 0x00000800 // 命令攜帶至少1個參數 #define NGX_CONF_2MORE 0x00001000 // 命令攜帶至少2個參數 #define NGX_CONF_MULTI 0x00002000 // 命令攜帶多個參數
4.3 上下文定義 ngx_http_stub_status_module_ctx
static ngx_http_module_t ngx_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_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; ngx_atomic_int_t ap, 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 requestsn") - 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 requestsn", sizeof("server accepts handled requestsn") - 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); }
站長資訊網