1 Background

http://nginx.org/en/docs/http/ngx_http_stub_status_module.html

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

--with-http_stub_status_module

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

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

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;
}

我们假设你是在本机上实验,并且开启的是 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

Nginx解读内置非默认模块 ngx_http_stub_status_module的更多相关文章

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

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

  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内置的操作系统模块(os)与解释器交互模块(sys)

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

  5. day15 内置函数和模块

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

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

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

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

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

  8. Python基础-内置函数、模块、函数、json

    内置函数 1.id()返回对象的内存地址: 2. type() 返回对象类型:   3.print()打印输出: 4. input()接受一个标准输入数据,返回为string类型: 5. list() ...

  9. Django内置的分页模块

    自定义分页 未封装版: 优点:直观 缺点:代码乱,不易维护,可拓展性差 data = [] for i in range(1, 302): tmp = {"id": i, &quo ...

随机推荐

  1. 精读《React PowerPlug 源码》

    1. 引言 React PowerPlug 是利用 render props 进行更好状态管理的工具库. React 项目中,一般一个文件就是一个类,状态最细粒度就是文件的粒度.然而文件粒度并非状态管 ...

  2. Jenkins结合.net平台之Web项目编译

    前面我们讲解了如何使用msbuild.exe编译一个.net程序.示例中我们讲解的是编译控制台项目,但是我们知道web项目不仅需要编译类的嵌入的资源文件,还要拷贝诸如css,html,js,图片等资源 ...

  3. [PHP] 适配器模式的日常使用

    适配器模式就是将一个类的接口方法转换成我希望用的另一个方法 , 下面是个常见的用处 class Session{ public $mc; public function __construct(){ ...

  4. C# 在Word中添加表格的方法

    表格是组织整理数据的一种重要手段,应在生活中的方方面面.在Word文档中将繁杂的文字表述内容表格化,能快速.直接地获取关键内容信息.那么,通过C#,我们也可以在Word文档中添加表格,这里将介绍两种不 ...

  5. Java开发笔记(七十三)常见的程序异常

    一个程序开发出来之后,无论是用户还是程序员,都希望它稳定地运行,然而程序毕竟是人写的,人无完人哪能不犯点错误呢?就算事先考虑得天衣无缝,揣着一笔巨款跑去岛国买了栋抗震性能良好的海边别墅,谁料人算不如天 ...

  6. Python 100例(001)

    #!/usr/bin/env python # coding:utf-8 '''有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少?''' n = 1 num = [4, ...

  7. win10安装ubuntu16.04双系统

    前话:因为感兴趣吧也是专业需要,所以自学了U盘重装系统以及在win10的情况下安装Ubuntu16.04双系统.借此博客,记录下我安装Ubuntu的过程,方便日后回忆. 正因如此,本篇博客会写的十分简 ...

  8. js中函数表达式和自执行函数表达式的用法总结

    立即调用函数表达式 给函数体加大括号,在有变量声明的情形下,没有任何区别 但是,如果只是[自动执行]的情形下,就会不同 因为,一个匿名函数,不赋值或函数体不加小括号,是不能自动执行的 //以下情形并无 ...

  9. Html和Css学习笔记-css进阶-盒模型

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 此篇博客是我的复习笔记,html和css学的时间太久了,忘得差不多了,最近要使用一下,所以重新打开html的书略读,后记录了标签 ...

  10. 作为JavaScript开发人员,这些必备的VS Code插件你都用过吗?

    本文翻译自:https://www.sitepoint.com/vs-code-extensions-javascript-developers/ 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的 ...