解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
1 Background
ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息。默认情况下这个模块是不被编译进来的,所以在编译 Nginx 时要指定加载该模块:
--with-http_stub_status_module
当然了,如果你是重新编译,仅仅-s reload是不够的,可能需要用到平滑升级:《高性能Web服务器Nginx的配置与部署研究(14)平滑升级你的Nginx》。
为什么拿它做例子?因为它也是个足够短小精悍的模块,是一个典型 handler 模块。那么以后我们讲解模块的过程,都是:
- 简要的介绍
- 使用的实例
- 指令介绍
- 源码分析
- 先上源码
- 分析
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 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
6 本节留下的问题:
- ngx_conf_set_str_slot:你可以先去了解下,或者等待在后面的博文中看到 :)
解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module的更多相关文章
- 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 ...
- 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内置的urllib模块不支持https协议的解决办法
Django站点使用django_cas接入SSO(单点登录系统),配置完成后登录,抛出“urlopen error unknown url type: https”异常.寻根朔源发现是python内 ...
- 在Eclipse+ADT中开发Android系统的内置应用
转自: http://www.iteye.com/topic/1050439 在Eclipse+ADT中开发Android系统的内置应用 Android系统内置有:Browser(浏览器).Mms( ...
- day15 内置函数和模块
day15 内置函数和模块 1.三元表达式 代码如下: x = 1 y = 2 res = 'ok' if x > y else 'no' print(res) 输出结果:no 2.内置函数:重 ...
- Python内置的操作系统模块(os)与解释器交互模块(sys)
Python内置的操作系统模块(os)与解释器交互模块(sys) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本片博客只介绍Python调用操作系统的模块即os模块,以及Pyth ...
- 第三十五篇 类的内置属性(attr属性),包装和授权,__getattr__
双下划线开头的attr方法,都是类内置的方法. 一. 如果没有在类里定义这三个方法,调用的时候就调用类内置的默认的方法 class Too: pass # 类没有定义这三个属性,就用系统默认的方法 t ...
- Ionic4.x Theming(主题) 增加内置主题 颜色 修改内置组件默认样式 修改底部 Tabs 背景颜色以及按钮颜色
1.Ionic4.x Theming(主题) Ionic4.x 修改主题颜色的话需要在 src/theme/variables.scss 文件中修改. https://ionicframework.c ...
随机推荐
- iPhone 和 Galaxy高速拍照原理具体分析
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zoosenpin/article/details/30027263 1 原理分析 首先我们看一下An ...
- MySQL5.6版本性能调优my.cnf详解
[client] port = 3306 socket = /tmp/mysql.sock [mysqld] port = 3306 socket = /tmp/mysql.sock basedir ...
- 可以方便配合 Git 的现代编辑器
可以方便配合 Git 的现代编辑器 虽然有些人说编辑器不重要,有人用记事本来编辑 PHP(我是不推荐的),但学是要推荐一些现在编辑器. 可以很方便的配合 Git,比如有冲突时不会看得晕乎乎,使用鼠标点 ...
- 【转】HP laserjet p2055dn的自动双面打印功能
原文网址:http://zhidao.baidu.com/link?url=n_NW7Qfa_7HlrEhLucdvKO43jj3SpFXJhGAfQ-WqF979jm80eUv8s1atqtxE7w ...
- php mysql 字符集(三) (转)
http://bbs.csdn.net/topics/390097514 gbk页面插入数据到utf8表,然后取出到gbk页面 首先, 这个set names x等价于SET character_se ...
- Web 漏洞分析与防御之 CSRF(二)
原文地址:Web 漏洞分析与防御之 CSRF(二) 博客地址:http://www.extlight.com 一.全称 跨站请求伪造(Cross-site Request Forgery) 二.原理 ...
- Redis持久化的简单介绍
Redis的强大功能很大程度上是由于其将所有数据都存储在内存中,为了使Redis在重启后仍能保证数据不丢失,需要将数据从内存中以某种形式持久化到硬盘中. Redis支持两种方式的持久化,一种是RDB方 ...
- 老齐python-基础4(元祖、字典、集合)
1.元祖 元祖的特性结合了字符串和列表 元祖是用圆括号括起来的,其中的元素之间用逗号(英文半角)隔开.元祖中的元素是任意类型的python对象(包括以后自定义的对象) 元祖中的元素不可更改,所以修改列 ...
- CSS 属性单词
.container {padding:0px; height:90%; width:100%; margin:0;}#header {height:0px; width:100%; padding: ...
- 【转】Jmeter入门:如何建立和使用Jmeter测试环境
一.工具描述 apache jmeter是100%的java桌面应用程序,它被设计用来加载被测试软件功能特性.度量被测试软件的性能.设计jmeter的初衷是测试web应用, 后来又扩充了其它的功能.j ...