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. 【朝花夕拾】Android性能篇之(六)Android进程管理机制

    前言        Android系统与其他操作系统有个很不一样的地方,就是其他操作系统尽可能移除不再活动的进程,从而尽可能保证多的内存空间,而Android系统却是反其道而行之,尽可能保留进程.An ...

  2. 环境与工具1:微信群刷屏 | itchat

    在微信群里面,"刷屏"的行为是被谴责的,伴随着"快发红包道歉"与"送飞机票"的出现.那如果小程硬是要做到"刷屏"来验证自 ...

  3. Java开发知识之Java中的集合Set接口以及子类应用

    ---恢复内容开始--- Java开发知识之Java中的集合Set接口以及子类应用 一丶Set接口以及作用 在上一讲.我们熟悉了接口的实现图.以及自己各有的子类. List接口主要存储的数据是可以重复 ...

  4. JavaScript与WebAssembly进行比较

    本文由云+社区发表 作者:QQ音乐前端团队 在识别和描述核心元素的过程中,我们分享了构建SessionStack时使用的一些经验法则,这是一个轻量级但健壮且高性能的JavaScript应用程序,以帮助 ...

  5. 打造自己的.NET Core项目模板

    前言 每个人都有自己习惯的项目结构,有人的喜欢在项目里面建解决方案文件夹:有的人喜欢传统的三层命名:有的人喜欢单一,简单的项目一个csproj就搞定.. 反正就是萝卜青菜,各有所爱. 可能不同的公司对 ...

  6. #5 Python面向对象(四)

    前言 本节将是Python面向对象的最后一篇博文了,这节将记录类的特殊方法.特殊成员方法.旧类和新类的不同,以及一些其他知识.Go! 一.类的特殊方法 Python有三种特殊方法:实例方法.静态方法. ...

  7. Aooms_微服务基础开发平台实战_003_配置文件与简单的web环境搭建

    一.前言 本篇文章介绍两个重点 (1) 工程核心配置文件application.yml (2) 如何在一个标准的的SpringCloud工程上构建起一个基本的web结构 二.配置文件applicati ...

  8. 【Angular专题】——(1)Angular,孤傲的变革者

    目录 一. 漫谈Angular 二. 如果你还在使用Angularjs 三. 我计划这样学习Angular技术栈 一. 漫谈Angular Angular,来自Google的前端SPA框架,与Reac ...

  9. jquery/js知识点收藏

    1]关于页面跳转 "window.location.href"."location.href"是本页面跳转 "parent.location.href ...

  10. Tomcat配置文件Executor元素属性介绍

    该元素可用于Tomcat 6.0.11以及更高版本. 允许您为一个Service的所有Connector配置一个共享线程池.在运行多个Connector的状况下,这样处理非常有用,而且每个Connec ...