1 Background
http://nginx.org/en/docs/http/ngx_http_stub_status_module.html
ngx_http_stub_status_module 是一個(gè) Nginx 的內(nèi)置 HTTP 模塊,該模塊可以提供 Nginx 的狀態(tài)信息。默認(rèn)情況下這個(gè)模塊是不被編譯進(jìn)來(lái)的,所以在編譯 Nginx 時(shí)要指定加載該模塊:
--with-http_stub_status_module
為什么拿它做例子?因?yàn)樗彩莻€(gè)足夠短小精悍的模塊,是一個(gè)典型 handler 模塊。那么以后我們講解模塊的過(guò)程,都是:
- 簡(jiǎn)要的介紹
- 使用的實(shí)例
- 指令介紹
- 源碼分析
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; }
我們假設(shè)你是在本機(jī)上實(shí)驗(yàn),并且開啟的是 80 端口,那么在瀏覽器中輸入:
http://localhost/nginx_status
會(huì)看到這樣的信息:
Active connections: 291 server accepts handled requests 16630948 16630948 31070465 Reading: 6 Writing: 179 Waiting: 106
其含義很容易理解:
- 第一行
- 當(dāng)前的活躍連接數(shù):291
- 第二行
- 服務(wù)器已接受的連接數(shù):16630948(accepted connection #)
- 服務(wù)器已處理的連接數(shù):16630948(handled connection #)
- 服務(wù)器已處理的請(qǐng)求:31070465(可以算出,平均每個(gè)連接有 1.8 個(gè)請(qǐng)求)(handled connection #)
- 第三行
- Reading – Nginx 讀取的請(qǐng)求頭次數(shù)為 6;
- Writting – Nginx 讀取請(qǐng)求體、處理請(qǐng)求并發(fā)送響應(yīng)給客戶端的次數(shù)為 179;
- Waiting – 當(dāng)前活動(dòng)的長(zhǎng)連接數(shù):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
這個(gè)模塊中的唯一一個(gè)指令,是:
stub_status
- 語(yǔ)法:
stub_status on - 作用域:location
- 功能:統(tǒng)計(jì)這個(gè) 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; }
的確夠短小精悍吧?關(guān)鍵在于 Nginx 提供的模塊擴(kuò)展方式比較好,讓你可以少寫一些代碼(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 并無(wú)本質(zhì)區(qū)別。
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,其中最后一個(gè)比較陌生,相似的取值有:
#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
以下宏定義來(lái)自 ngx_conf_file.h:
#define NGX_CONF_NOARGS 0x00000001 // 命令不接受參數(shù) #define NGX_CONF_TAKE1 0x00000002 // 命令攜帶1個(gè)參數(shù) #define NGX_CONF_TAKE2 0x00000004 // 命令攜帶2個(gè)參數(shù) #define NGX_CONF_TAKE3 0x00000008 // 命令攜帶3個(gè)參數(shù) #define NGX_CONF_TAKE4 0x00000010 // 命令攜帶4個(gè)參數(shù) #define NGX_CONF_TAKE5 0x00000020 // 命令攜帶5個(gè)參數(shù) #define NGX_CONF_TAKE6 0x00000040 // 命令攜帶6個(gè)參數(shù) #define NGX_CONF_TAKE7 0x00000080 // 命令攜帶7個(gè)參數(shù) #define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2) // 命令攜帶1個(gè)或2個(gè)參數(shù) #define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3) // 命令攜帶1個(gè)或3個(gè)參數(shù) #define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3) // 命令攜帶2個(gè)或3個(gè)參數(shù) #define NGX_CONF_TAKE123 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3) // 命令攜帶1個(gè)、2個(gè)或3個(gè)參數(shù) #define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4) // 命令攜帶1個(gè)、2個(gè)、3個(gè)或4個(gè)參數(shù) #define NGX_CONF_ARGS_NUMBER 0x000000ff // 命令 #define NGX_CONF_BLOCK 0x00000100 // 塊域,后面跟 {…},比如 server {...} #define NGX_CONF_FLAG 0x00000200 // 命令接受“on|off”參數(shù) #define NGX_CONF_ANY 0x00000400 #define NGX_CONF_1MORE 0x00000800 // 命令攜帶至少1個(gè)參數(shù) #define NGX_CONF_2MORE 0x00001000 // 命令攜帶至少2個(gè)參數(shù) #define NGX_CONF_MULTI 0x00002000 // 命令攜帶多個(gè)參數(shù)
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 */ };
這個(gè)都是 NULL,夠簡(jiǎn)單,無(wú)話可說(shuō)了??
4.4 命令設(shè)置函數(shù) 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 對(duì)比下:
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; }
唯一的區(qū)別,就是 ngx_http_hello_world_module 多了一句 ngx_conf_set_str_slot。這個(gè)先留做一個(gè)問(wèn)題,后面會(huì)介紹,暫時(shí)與關(guān)鍵主題無(wú)關(guān)。
4.5 命令處理函數(shù) 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;
這個(gè)模塊要求接受的請(qǐng)求類是 GET、HEAD,其他類型的請(qǐng)求會(huì)被拒絕。
if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { return NGX_HTTP_NOT_ALLOWED; }
放棄請(qǐng)求體,因?yàn)檫@個(gè)模塊用不上。
rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; }
如果請(qǐng)求是 HEAD 類型的,則直接設(shè)置響應(yīng)頭的 content_type、status 字段,并發(fā)送響應(yīng)頭。
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; } }
創(chuàng)建一個(gè)緩沖區(qū),向緩沖區(qū)寫入我們上面在瀏覽器中看到的東西。
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));
緩沖區(qū)寫完了。然后設(shè)置下響應(yīng)頭的 status、content_length_n(還記得嗎?b->last – b->pos 剛好是緩沖區(qū)的第二個(gè)區(qū)域,是已寫入數(shù)據(jù)部分。)
r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = b->last - b->pos; b->last_buf = 1;
發(fā)送響應(yīng)頭。
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); }
站長(zhǎng)資訊網(wǎng)