解剖Nginx·模組開發篇(5)解讀內建非預設模組 ngx_http_stub_status_module
解剖Nginx·模組開發篇(5)解讀內建非預設模組 ngx_http_stub_status_module
- 作者:柳大·Poechant(鍾超)
- 郵箱:zhongchao.ustc#gmail.com(# -> @)
- 部落格:Blog.CSDN.net/Poechant
- 日期:June 3nd, 2012
1 Background
ngx_http_stub_status_module 是一個 Nginx 的內建 HTTP 模組,該模組可以提供 Nginx 的狀態資訊。預設情況下這個模組是不被編譯進來的,所以在編譯 Nginx 時要指定載入該模組:
--with-http_stub_status_module
當然了,如果你是重新編譯,僅僅-s reload
是不夠的,可能需要用到平滑升級:《高效能Web伺服器Nginx的配置與部署研究(14)平滑升級你的Nginx》。
為什麼拿它做例子?因為它也是個足夠短小精悍的模組,是一個典型 handler 模組。那麼以後我們講解模組的過程,都是:
- 簡要的介紹
- 使用的例項
- 指令介紹
- 原始碼分析
- 先上原始碼
- 分析
2 Simple example
location /nginx_status {
# copied from http://blog.kovyrin.net/2006/04/29/monitoring-nginx-with-rrdtool/
stub_status on;
access_log off;
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 connections -
server 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 header -
writing
– nginx reads request body, processes request, or writes response to a client -
waiting
– 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 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_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 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);
}
5 Reference
- http://wiki.nginx.org/HttpStubStatusModule
- http://blog.csdn.net/lengzijian/article/details/7356064
- http://www.codinglabs.org/html/intro-of-nginx-module-development.html
6 本節留下的問題:
- ngx_conf_set_str_slot:你可以先去了解下,或者等待在後面的博文中看到 :)
-
轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant
-
相關文章
- 解剖Nginx·模組開發篇(1)跑起你的 Hello World 模組!Nginx
- 解剖Nginx·模組開發篇(4)模組開發中的命名規則和模組載入與執行流程Nginx
- 解剖Nginx·模組開發篇(2)ngx_http_hello_world_module 模組基本結構定義NginxHTTP
- 解剖Nginx·模組開發篇(6)配置檔案config入門Nginx
- 解剖Nginx·模組開發篇(3)ngx_http_hello_world_module 模組的基本函式實現NginxHTTP函式
- nginx模組開發Nginx
- Nginx通過內建的Perl模組執行Perl程式Nginx
- nginx事件模組 -- 第二篇Nginx事件
- nginx事件模組-- 第四篇Nginx事件
- nginx事件模組 -- 第三篇Nginx事件
- nginx事件模組 -- 第一篇Nginx事件
- Nginx篇--解讀nginx配置Nginx
- 基於Hyperf + Vue + Element 開發的管理系統(內建聊天模組)Vue
- 詳解JavaScript模組化開發JavaScript
- DNN模組開發—分析設計DNN
- Nginx的Uwsgi模組(部分模組指令)Nginx
- NGINX模組(一)Nginx
- NGINX模組(二)Nginx
- Nginx limit模組限制併發數NginxMIT
- Nginx原始碼研究之nginx限流模組詳解Nginx原始碼
- Laravel 模組開發包 (nWidart/Laravel-modules) 原始碼解讀LaravelDart原始碼
- 利用nginx的stream模組實現內網埠的轉發代理Nginx內網
- 4G模組軟體指南 | 必讀篇之模組資訊(hmeta)
- nginx事件模組 -- 第五篇 epoll addNginx事件
- FastDFS的配置、部署與API使用解讀(7)Nginx的FastDFS模組ASTAPINginx
- nginx內建預定義變數Nginx變數
- Nginx 新增 lua 模組Nginx
- 為 Nginx 新增模組Nginx
- Nginx常用的模組Nginx
- Nginx的Uwsgi模組Nginx
- 使用typescript開發angular模組(編寫模組)TypeScriptAngular
- nginx內建高可用配置與第三方高可用模組nginx_ustream_check_mudule配置Nginx
- Python 內建模組:os模組Python
- 內建模組--壓縮模組--zipfile
- 一篇文章說透Nginx的rewrite模組Nginx
- nginx事件模組 -- 第七篇 建立連線Nginx事件
- Node.js process 模組解讀Node.js
- Node.js util 模組解讀Node.js