您的位置:首页 > 理论基础 > 计算机网络

解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module

2014-11-01 14:11 1411 查看


解剖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_statuson


作用域: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: