zoukankan      html  css  js  c++  java
  • Nginx解读内置非默认模块 ngx_http_stub_status_module

    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:  
    ") + NGX_ATOMIC_T_LEN
               + sizeof("server accepts handled requests
    ") - 1
               + 6 + 3 * NGX_ATOMIC_T_LEN
               + sizeof("Reading:  Writing:  Waiting:  
    ") + 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 
    ", ac);
    
        b->last = ngx_cpymem(b->last, "server accepts handled requests
    ",
                             sizeof("server accepts handled requests
    ") - 1);
    
        b->last = ngx_sprintf(b->last, " %uA %uA %uA 
    ", ap, hn, rq);
    
        b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA 
    ",
                              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:  
    ") + NGX_ATOMIC_T_LEN
               + sizeof("server accepts handled requests
    ") - 1
               + 6 + 3 * NGX_ATOMIC_T_LEN
               + sizeof("Reading:  Writing:  Waiting:  
    ") + 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 
    ", ac);
    
        // 封装了 memcpy
        b->last = ngx_cpymem(b->last, "server accepts handled requests
    ",
                             sizeof("server accepts handled requests
    ") - 1);
    
        b->last = ngx_sprintf(b->last, " %uA %uA %uA 
    ", ap, hn, rq);
    
        b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA 
    ",
                              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
  • 相关阅读:
    subtitleedit
    NHibernate.Mapping1.1.csgen 模板
    js jqueryhotkeys
    c#.net将对象序列化,反序列化json
    ruby document
    sqlserver2008新数据类型
    [转]杂记
    UVA 532 Dungeon Master
    UVA 10557 XYZZY
    UVA 10129 Play on Words
  • 原文地址:https://www.cnblogs.com/felixzh/p/9016133.html
Copyright © 2011-2022 走看看