解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
2012-06-03 12:51
2446 查看
解剖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_statustype: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.html6 本节留下的问题:
ngx_conf_set_str_slot:你可以先去了解下,或者等待在后面的博文中看到 :)-转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant-
相关文章推荐
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
- Nginx-解读内置非默认模块 ngx_http_stub_status_module
- 解剖Nginx·模块开发篇(2)ngx_http_hello_world_module 模块基本结构定义
- 解剖Nginx·模块开发篇(2)ngx_http_hello_world_module 模块基本结构定义
- 解剖Nginx·模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现
- 解剖Nginx·模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现
- 在CentOS 6.9 x86_64上开启nginx 1.12.2的stub_status模块(ngx_http_stub_status_module)监控
- Nginx服务器工作状态ngx_http_stub_status_module 模块
- NGINX服务器工作状态NGX_HTTP_STUB_STATUS_MODULE 模块
- 监控Nginx服务器工作状态ngx_http_stub_status_module 模
- nginx监控模块http_stub_status_module安装
- nginx模块学习一 http_stub_status_module 客户端连接状态
- 解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!
- 解剖Nginx·模块开发篇(4)模块开发中的命名规则和模块加载与运行流程
- 解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!
- Nginx性能统计模块http_stub_status_module使用
- 解剖Nginx·模块开发篇(6)配置文件config入门