1 Background

ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息。默认情况下这个模块是不被编译进来的,所以在编译 Nginx 时要指定加载该模块:

--with-http_stub_status_module

当然了,如果你是重新编译,仅仅-s reload是不够的,可能需要用到平滑升级:《高性能Web服务器Nginx的配置与部署研究(14)平滑升级你的Nginx》

为什么拿它做例子?因为它也是个足够短小精悍的模块,是一个典型 handler 模块。那么以后我们讲解模块的过程,都是:

  1. 简要的介绍
  2. 使用的实例
  3. 指令介绍
  4. 源码分析
    1. 先上源码
    2. 分析

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

  1. http://wiki.nginx.org/HttpStubStatusModule
  2. http://blog.csdn.net/lengzijian/article/details/7356064
  3. http://www.codinglabs.org/html/intro-of-nginx-module-development.html

6 本节留下的问题:

  • ngx_conf_set_str_slot:你可以先去了解下,或者等待在后面的博文中看到 :)

解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module的更多相关文章

  1. Nginx解读内置非默认模块 ngx_http_stub_status_module

    1 Background http://nginx.org/en/docs/http/ngx_http_stub_status_module.html ngx_http_stub_status_mod ...

  2. Nginx-解读内置非默认模块 ngx_http_stub_status_module

    1.Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...

  3. Filebeat使用内置的mysql模块收集日志存储到ES集群并使用kibana存储

    Filebeat内置了不少的模块,可以直接使用他们对日志进行收集,支持的模块如下: [root@ELK-chaofeng07 logstash]# filebeat modules list Enab ...

  4. Python内置的urllib模块不支持https协议的解决办法

    Django站点使用django_cas接入SSO(单点登录系统),配置完成后登录,抛出“urlopen error unknown url type: https”异常.寻根朔源发现是python内 ...

  5. 在Eclipse+ADT中开发Android系统的内置应用

    转自:  http://www.iteye.com/topic/1050439 在Eclipse+ADT中开发Android系统的内置应用 Android系统内置有:Browser(浏览器).Mms( ...

  6. day15 内置函数和模块

    day15 内置函数和模块 1.三元表达式 代码如下: x = 1 y = 2 res = 'ok' if x > y else 'no' print(res) 输出结果:no 2.内置函数:重 ...

  7. Python内置的操作系统模块(os)与解释器交互模块(sys)

    Python内置的操作系统模块(os)与解释器交互模块(sys) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本片博客只介绍Python调用操作系统的模块即os模块,以及Pyth ...

  8. 第三十五篇 类的内置属性(attr属性),包装和授权,__getattr__

    双下划线开头的attr方法,都是类内置的方法. 一. 如果没有在类里定义这三个方法,调用的时候就调用类内置的默认的方法 class Too: pass # 类没有定义这三个属性,就用系统默认的方法 t ...

  9. Ionic4.x Theming(主题) 增加内置主题 颜色 修改内置组件默认样式 修改底部 Tabs 背景颜色以及按钮颜色

    1.Ionic4.x Theming(主题) Ionic4.x 修改主题颜色的话需要在 src/theme/variables.scss 文件中修改. https://ionicframework.c ...

随机推荐

  1. PHP实现的多文件上传类及用法示例

    这篇文章主要介绍了PHP实现的多文件上传类及用法,详细分析了php实现的多文件上传类与具体的使用技巧,需要的朋友可以参考下 1.upFiles.css.php 文件 <?php class Up ...

  2. 莫名其妙的js基础学习!

    JavaScript基本组成部分: 1,ECMAScript:js的语法标准,基本的变量,运算符,函数,if语句,for语句等 2,DOM:操作网页上的元素API,比如盒子的移动,变色,轮播图等. 3 ...

  3. bzoj 2632 [neerc2011]Gcd guessing game——贪心(存疑)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2632 官方题解:http://neerc.ifmo.ru/archive/2011/neer ...

  4. Vim下的Web开发之html,CSS,javascript插件

    Vim下的Web开发之html,CSS,javascript插件   HTML 下载HTML.zip 解压HTML.zip,然后将里面的所有文件copy到C:\Program Files\Vim\vi ...

  5. NOIP2013 Day1

    1.转圈游戏 https://www.luogu.org/problem/show?pid=1965 这道题失误极大,把freopen注释掉了,导致第一题暴0. 注意:在考试时一定要留下最后的时间检查 ...

  6. JDK 9 & JDK 10 新特性

    JDK 9 新增了不少特性,官方文档:https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-527735CF-44E1-4144-9 ...

  7. Eclipse中设置JDK、${user}变量

    为eclipse设置jdk方法: 两个方法: 1.设置PATH路径-eclipse自动会查找! 2.在快捷方式中加上参数:-VM java虚拟机路径 Eclipse中设置${user}变量 在Ecli ...

  8. Java-Runoob-高级课程:Java 8 新特性

    ylbtech-Java-Runoob-高级课程:Java 8 新特性 1.返回顶部 1. Java 8 新特性 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本. Or ...

  9. mysql 使用 informatin_schema tables 创建 shell commands

    SELECT CONCAT("mysqldump -uroot -p ", TABLE_SCHEMA, " ", TABLE_NAME, " > ...

  10. [转]Jsp 页面中的错误

    1. Jsp 语法格式问题,导致其不能被翻译成 Servlet 源文件. 2. Jsp 翻译成 Servlet 源文件后出现的语法格式问题. 3. Jsp 翻译成 Servlet 后运行时发生异常.