当nginx解析conf文件时,可以为cmd加上handler。
代码中在重点地方带有详细的注释。
#include
#include
#include #include
#include /*
* 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, "Hello, NGX handler! I am FLY.
");strcat(html, "");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
",inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)),pv_table[i].count);strcat(html, buffer);}}strcat(html, "
");
}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/offNGX_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 // 填充};
创建:
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"
注意,config文件要和模块的代码在相同目录。
(1)配置中添加模块:
./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
(2)查看是否添加模块到动态代码中:
cat objs/ngx_modules.c
(3)编译安装:
make
sudo make install
编译安装完成后,conf文件添加count;
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;}}
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf
在网页输入IP和端口,执行效果如下:
可以看到,返回的网页中多出了访问次数统计。
(1)conf文件热更新:通过reload指令进行重新加成conf文件。reload过程中是重新开启新的进程来加载新的conf文件;比如原来有4个进程在运行,加载新的conf文件时就重新开启4个进程来加载新的配置文件。
(2)可执行程序的热更新:编译安装新的nginx,会把原来的nginx重命名为nginx.old,然后调用nginx reload就会更新。
本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器课程