文章目录
一、handler简介
Handler模块就是接受来自客户端的请求并产生输出的模块。
配置文件中使用location指令可以配置content handler模块,当Nginx系统启动的时候,每个handler模块都有一次机会把自己关联到对应的location上。
如果有多个handler模块都关联了同一个location,那么实际上只有一个handler模块真正会起作用。
所以在开发阶段应该避免多个handler模块关联同一个location的情况发生。
- 处理成功。
- 处理失败:处理的时候发生了错误。
- 拒绝处理:这个location的处理就会由默认的handler模块来进行处理。
例如,当请求一个静态文件的时候,如果关联到这个location上的一个handler模块拒绝处理,就会由默认的ngx_http_static_module模块进行处理,该模块是一个典型的handler模块。
二、Nginx handler模块开发
2.1、示例代码
#include <ngx_config.h>
#include <ngx_http.h>
#include <ngx_core.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/*
* ip的访问次数存放在一个key-value数据结构里面,ip是key,value是统计的次数
* 可用的数据结构:
* hash
* rbtree
* 最简单的是数组
*/
typedef struct {
int count;
struct in_addr addr;
}ngx_pv_table;
ngx_pv_table pv_table[256] = { 0 }; //这只适合局域网内存储,正在的数据结构最好用rbtree
// 重新组织网页 (网页组包)
void ngx_encode_http_page(char *html)
{
sprintf(html, "<h1>Hello, NGX handler! I am FLY.</h1>");
strcat(html, "<h2>");
int i = 0;
for (i = 0; i < 256; i++) {
if (pv_table[i].count != 0) {
char str[INET_ADDRSTRLEN] = { 0 };
char buffer[128] = { 0 };
sprintf(buffer, "req from : %s, count: %d <br/>",
inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)),
pv_table[i].count);
strcat(html, buffer);
}
}
strcat(html, "</h2>");
}
ngx_int_t ngx_http_count_handler(ngx_http_request_t *r)
{
// 这里做统计功能
// 获取ip地址
struct sockaddr_in *cliaddr = (struct sockaddr_in *)r->connection->sockaddr;
// 地址和我们看到的是反着的,通过右移得到ip地址的末尾.符号后面那个位数
int idx = cliaddr->sin_addr.s_addr >> 24;
pv_table[idx].count++;
memcpy(&pv_table[idx].addr, &cliaddr->sin_addr, sizeof(cliaddr->sin_addr));
// 重新组织网页
u_char html[1024] = { 0 };
int len = sizeof(html);
ngx_encode_http_page((char*)html);
/*
* 发送http响应
*/
r->headers_out.status = 200;
ngx_str_set(&r->headers_out.content_type, "text/html");
// 发送http 头
ngx_http_send_header(r);
// 内存池拿出一个buffer的内存空间
ngx_buf_t *b = ngx_palloc(r->pool, sizeof(ngx_buf_t));
b->pos = html;
b->last = html + len;
b->memory = 1;//内存里操作
b->last_buf = 1;//最后内存块
// 缓冲链
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
char *ngx_http_handler_count_set(ngx_conf_t *cf,ngx_command_t *cmd,void *conf)
{
ngx_http_core_loc_conf_t *ccf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
// 设置handler的入口函数
ccf->handler = ngx_http_count_handler;
memset(pv_table, 0, sizeof(pv_table));
return NGX_OK;
}
// conf文件中的每一行都是一个指令指令
ngx_command_t ngx_http_handler_module_cmd[] = {
{
//命令名称,比如listen,定义了就可以在conf文件中使用,注意不能和其他的起冲突
ngx_string("count"),
// 指示name命令放的位置在哪里以及可以带多少个参数,NGX_CONF_FLAGE表示开关标志
// predix on/off
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
// 命令解析,可以使用nginx内部的也可以自己实现
ngx_http_handler_count_set,//ngx_http_handler_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL,
},
ngx_null_command
};
// 用来解析对应的conf文件
static ngx_http_module_t ngx_http_handler_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
// 模块定义
ngx_module_t ngx_http_handler_module = {
NGX_MODULE_V1,
&ngx_http_handler_module_ctx,
ngx_http_handler_module_cmd,
// http的ascii值,指示是什么模块
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING // 填充
};
说明:
1、handler模块必须提供一个真正的处理函数(即上文中的ngx_http_count_handler
),这个函数负责对来自客户端请求的真正处理。这个函数的处理,既可以选择自己直接生成内容,也可以选择拒绝处理,由后续的handler去进行处理,或者是选择丢给后续的filter进行处理。
这个处理函数的原型如下:
// r是http的请求,里面包含请求所有的信息
// 该函数处理成功返回NGX_OK,处理发生错误返回NGX_ERROR,拒绝处理(留给后续的handler进行处理)返回NGX_DECLINE。
// 返回NGX_OK也就代表给客户端的响应已经生成好了,否则返回NGX_ERROR就发生错误了。
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
2.2、编写config文件
创建:
touch config
内容:
ngx_addon_name=ngx_http_handler_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_handler_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_handler_module.c"
2.3、编译模块到Nginx源码中
./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module
--with-http_gzip_static_module --with-http_secure_link_module
--with-http_stub_status_module --with-stream --with-pcre=/home/fly/workspace/pcre-8.41
--with-zlib=/home/fly/workspace/zlib-1.2.11 --with-openssl=/home/fly/workspace/openssl-1.1.0g
--add-module=/mnt/hgfs/sourcecode_learning/ngx_http_handler_module
configuring additional modules
adding module in /mnt/hgfs/sourcecode_learning/ngx_http_handler_module
+ ngx_http_handler_module was configured
creating objs/Makefile
cat objs/ngx_modules.c
make
sudo make install
2.4、修改conf文件
worker_processes 4;
events {
worker_connections 1024;
}
http {
upstream backend {
server 192.168.7.146:8889;
server 192.168.7.146:8890;
}
server {
listen 8888;
location / {
proxy_pass http://backend;
}
}
server {
listen 8889;
location / {
count;
}
}
server {
listen 8890;
}
server {
listen 8891;
}
}
2.5、执行效果
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf
在网页输入IP和端口,执行效果如下:
可以看到,返回的网页中多出了访问次数统计。
三、Nginx的热更新
(1)conf文件热更新:通过reload指令进行重新加成conf文件。reload过程中是重新开启新的进程来加载新的conf文件;比如原来有4个进程在运行,加载新的conf文件时就重新开启4个进程来加载新的配置文件。
(2)可执行程序的热更新:编译安装新的nginx,会把原来的nginx重命名为nginx.old,然后调用nginx reload就会更新。
总结
- 上述代码虽然实现了IP访问服务器的流量统计;但是,Nginx是多进程的,上述示例代码没有实现统计数在进程间的共享,这回造成其他进程是重新计数的问题。解决这个问题可以使用共享内存的方式在进程间通信。
- 上述代码使用了最简单的数据结构:数组。这不是好的决策,可以将其改为红黑树。
- nginx的handler模块开发也可以用在黑白名单的处理(比如当判断到同一个ip发送多个无效请求,可以将其加入到黑名单中)。
原文地址:https://blog.csdn.net/qq_29750559/article/details/134562312
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_1803.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!