解剖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 ...
随机推荐
- 使用 commander && inquirer 构建专业的node cli
备注: 比较简单就是使用nodejs 的两个类库帮助我们进行开发而已,具体的使用参考类库文档 1. 项目初始化 a. 安装依赖 yarn init -y yarn add commander in ...
- 可以方便配合 Git 的现代编辑器
可以方便配合 Git 的现代编辑器 虽然有些人说编辑器不重要,有人用记事本来编辑 PHP(我是不推荐的),但学是要推荐一些现在编辑器. 可以很方便的配合 Git,比如有冲突时不会看得晕乎乎,使用鼠标点 ...
- Sqlite基础简介
1.什么是SQLite ? -> SQLite简介 SQLite 是一个进程内的库,是一种轻量级的.自给自足的.无服务器的.无需配置的,事务性的SQL数据库引擎.和他其他的数据库一样,SQLit ...
- Unit08: Spring集成mybatis
Unit08: Spring集成mybatis 1. Spring集成mybatis (1)方式一 step1. 导包. spring-webmvc,mybatis,mybatis-spring, o ...
- Socket客户端
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- Python基本序列-字典
Python 基本序列-字典 字典(dict)是"键-值 对"的无序可变序列,字典中的每个元素包含两部分,"键"和"值". 字典中的&quo ...
- 写java代码有感。。。构造方法最好带着,
(一) 小结:具体我最大的担心,害怕就是方法调用的时候,new对象之后,赋值,是在new后面的括号里实现,还是在 对象.方法名()这样的.当然带参数的构造方法,调用时本身就直接调用,普通方法,选后者. ...
- STL算法与树结构模板
STL算法 STL 算法是一些模板函数,提供了相当多的有用算法和操作,从简单如for_each(遍历)到复杂如stable_sort(稳定排序),头文件是:#include <algorithm ...
- Tool:Visual Studio Code
ylbtech-Tool:Visual Studio Code Microsoft在2015年4月30日Build 开发者大会上正式宣布了 Visual Studio Code 项目:一个运行于 Ma ...
- centos开机提示CPU无法识别的问题
centos版本和对应cpu兼容性,官方网址: https://access.redhat.com/support/policy/intel Red Hat Enterprise Linux Vers ...