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 模块。那么以后我们讲解模块的过程,都是:
- 简要的介绍
- 使用的实例
- 指令介绍
- 源码分析
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 connectionsserver 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 headerwriting– nginx reads request body, processes request, or writes response to a clientwaiting– 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
- http://wiki.nginx.org/HttpStubStatusModule
- http://blog.csdn.net/lengzijian/article/details/7356064
- http://www.codinglabs.org/html/intro-of-nginx-module-development.html
Nginx解读内置非默认模块 ngx_http_stub_status_module的更多相关文章
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
1 Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...
- Nginx-解读内置非默认模块 ngx_http_stub_status_module
1.Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...
- Filebeat使用内置的mysql模块收集日志存储到ES集群并使用kibana存储
Filebeat内置了不少的模块,可以直接使用他们对日志进行收集,支持的模块如下: [root@ELK-chaofeng07 logstash]# filebeat modules list Enab ...
- Python内置的操作系统模块(os)与解释器交互模块(sys)
Python内置的操作系统模块(os)与解释器交互模块(sys) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本片博客只介绍Python调用操作系统的模块即os模块,以及Pyth ...
- day15 内置函数和模块
day15 内置函数和模块 1.三元表达式 代码如下: x = 1 y = 2 res = 'ok' if x > y else 'no' print(res) 输出结果:no 2.内置函数:重 ...
- Python内置的urllib模块不支持https协议的解决办法
Django站点使用django_cas接入SSO(单点登录系统),配置完成后登录,抛出“urlopen error unknown url type: https”异常.寻根朔源发现是python内 ...
- Ionic4.x Theming(主题) 增加内置主题 颜色 修改内置组件默认样式 修改底部 Tabs 背景颜色以及按钮颜色
1.Ionic4.x Theming(主题) Ionic4.x 修改主题颜色的话需要在 src/theme/variables.scss 文件中修改. https://ionicframework.c ...
- Python基础-内置函数、模块、函数、json
内置函数 1.id()返回对象的内存地址: 2. type() 返回对象类型: 3.print()打印输出: 4. input()接受一个标准输入数据,返回为string类型: 5. list() ...
- Django内置的分页模块
自定义分页 未封装版: 优点:直观 缺点:代码乱,不易维护,可拓展性差 data = [] for i in range(1, 302): tmp = {"id": i, &quo ...
随机推荐
- 【Python3爬虫】selenium入门
selenium 是一个用于Web应用程序测试的工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样.支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Fire ...
- 如何零基础开始自学Python编程
转载——原作者:赛门喵 链接:https://www.zhihu.com/question/29138020/answer/141170242 0. 明确目标 我是真正零基础开始学Python的,从一 ...
- 基于spark实现并行化Apriori算法
详细代码我已上传到github:click me 一. 实验要求 在 Spark2.3 平台上实现 Apriori 频繁项集挖掘的并行化算法.要求程序利用 Spark 进行并行计算. ...
- StackExchange.Redis .net core Timeout performing 超时问题
最近在做的一个项目,用的.net core 2.1,然后缓存用的Redis,缓存相关封装是同事写的,用的驱动是StackExchange.Redis version 2.0.571 ,一直听说这个驱动 ...
- leetcode — binary-tree-inorder-traversal
import java.util.Arrays; import java.util.Stack; import java.util.TreeMap; /** * * Source : https:// ...
- windows资源管理器多标签打开 windows文件夹多标签浏览 浏览器tab页面一样浏览文件夹 clover win8 win10 报错 无响应问题怎么解决 clover卡死 clover怎么换皮肤
大家都知道,我们打开一堆文件夹的时候,是什么样子 “厚厚的一叠”图标堆叠在一起的,非常的不方便 那么,是不是可以像浏览器一样的tab页面展示呢? 答案是可以的 安装好就是这样子的 是不是方便漂亮了很多 ...
- 痞子衡嵌入式:第一本Git命令教程(1)- 准备(init/config/.gitignore)
今天是Git系列课程第一课,痞子衡给大家要讲的是创建仓库的准备工作. 1.建仓库git init 第一步是创建一个空仓库,这是一切操作的前提. // 打开git bash命令行,切换到指定目录下 ja ...
- Spring Cloud Alibaba基础教程:使用Nacos作为配置中心
通过本教程的前两篇: <Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现> <Spring Cloud Alibaba基础教程:支持的几种服务消费方 ...
- 使用LR编写下载类脚本
如何下载并保存文件到本地,实现文件下载的脚本制作.以下是本人测试某系统总结整理的脚本,仅供参考. #include "lrs.h" Action() { // 示例一: //第一种 ...
- java多线程中的三种特性
java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...