当前位置: 首页 > news >正文

Nginx模块开发之http handler实现流量统计(2)

文章目录

  • 一、概述
  • 二、Nginx handler模块开发
    • 2.1、代码实现
    • 2.2、编写config文件
    • 2.3、编译模块到Nginx源码中
    • 2.4、修改conf文件
    • 2.5、执行效果
  • 总结

一、概述

上一篇【Nginx模块开发之http handler实现流量统计(1)】使用数组在单进程实现了IP的流量统计,这一篇将进行优化,使用红黑树的数据结构以及共享内存的方式实现进程间通信。

进程间通信的方式:
(1)进程在不同的机器中,使用网络进行通信。
(2)进程在同一个机器,并且进程间的关系是父子进程关系,可以使用共享内存。
(3)使用unix_sock,比如文件soket。在MySQL使用的就是这种进程通信方式。
(4)pipe,管道。

二、Nginx handler模块开发

2.1、代码实现

在重点地方添加了注释,主要是红黑树的添加和使用,以及共享内存的使用。

核心:
1.nginx获取请求。ngx_command_t中设置ngx_http_pagecount_set。
2.conf文件解析到模块的cmd时,初始化共享内存以及互斥锁。
3.红黑树的初始化。
4.组织网页时,需要遍历红黑树找到IP并做流量统计。


#include <ngx_http.h>
#include <ngx_config.h>
#include <ngx_core.h>#define ENABLE_RBTREE	1static char *ngx_http_pagecount_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_pagecount_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_pagecount_init(ngx_conf_t *cf);
static void  *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf);
static ngx_int_t ngx_http_pagecount_shm_init (ngx_shm_zone_t *zone, void *data);
static void ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);// 命令解析
static ngx_command_t count_commands[] = {{ngx_string("count"),NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,ngx_http_pagecount_set,//遇到模块命令时调用NGX_HTTP_LOC_CONF_OFFSET,0, NULL},ngx_null_command
};static ngx_http_module_t count_ctx = {NULL,ngx_http_pagecount_init,//初始化NULL,NULL,NULL,NULL,// conf文件解析到location时调用ngx_http_pagecount_create_location_conf,NULL,
};//ngx_http_count_module 
ngx_module_t ngx_http_pagecount_module = {NGX_MODULE_V1,&count_ctx,count_commands,NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING
};typedef struct {int count; //count
} ngx_http_pagecount_node_t;typedef struct {ngx_rbtree_t rbtree;ngx_rbtree_node_t sentinel;} ngx_http_pagecount_shm_t;// 共享内存结构体
typedef struct
{ssize_t shmsize;ngx_slab_pool_t *shpool;// 互斥锁ngx_http_pagecount_shm_t *sh;
} ngx_http_pagecount_conf_t;// 共享内存初始化
ngx_int_t ngx_http_pagecount_shm_init (ngx_shm_zone_t *zone, void *data) {ngx_http_pagecount_conf_t *conf;ngx_http_pagecount_conf_t *oconf = data;conf = (ngx_http_pagecount_conf_t*)zone->data;if (oconf) {conf->sh = oconf->sh;conf->shpool = oconf->shpool;return NGX_OK;}//printf("ngx_http_pagecount_shm_init 0000\n");// 初始化锁conf->shpool = (ngx_slab_pool_t*)zone->shm.addr;conf->sh = ngx_slab_alloc(conf->shpool, sizeof(ngx_http_pagecount_shm_t));if (conf->sh == NULL) {return NGX_ERROR;}conf->shpool->data = conf->sh;//printf("ngx_http_pagecount_shm_init 1111\n");// 共享内存创建完之后初始化红黑树,// 要提供插入函数ngx_rbtree_init(&conf->sh->rbtree, &conf->sh->sentinel, ngx_http_pagecount_rbtree_insert_value);return NGX_OK;}static char *ngx_http_pagecount_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_shm_zone_t *shm_zone;ngx_str_t name = ngx_string("pagecount_slab_shm");ngx_http_pagecount_conf_t *mconf = (ngx_http_pagecount_conf_t*)conf;ngx_http_core_loc_conf_t *corecf;//ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_set000");// 设置共享内存的大小mconf->shmsize = 1024*1024;shm_zone = ngx_shared_memory_add(cf, &name, mconf->shmsize, &ngx_http_pagecount_module);if (NULL == shm_zone) {return NGX_CONF_ERROR;}shm_zone->init = ngx_http_pagecount_shm_init;shm_zone->data = mconf;corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);// 计数统计主函数corecf->handler = ngx_http_pagecount_handler;return NGX_CONF_OK;
}ngx_int_t   ngx_http_pagecount_init(ngx_conf_t *cf) {return NGX_OK;
}// conf文件解析到location时进入此函数。
// 此函数为模块创建所需要的结构体, 后面会使用。
void  *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf) {ngx_http_pagecount_conf_t *conf;// 共享内存的结构体conf = ngx_palloc(cf->pool, sizeof(ngx_http_pagecount_conf_t));if (NULL == conf) {return NULL;}// 初始化共享内存的大小conf->shmsize = 0;//ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_create_location_conf");// init conf data// ... return conf;}// 红黑树的插入函数
static void
ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{ngx_rbtree_node_t **p;//ngx_http_testslab_node_t *lrn, *lrnt;for (;;){if (node->key < temp->key){p = &temp->left;}else if (node->key > temp->key) {p = &temp->right;}else{return ;}// 没有找到,直接返回if (*p == sentinel){break;}temp = *p;}*p = node;node->parent = temp;node->left = sentinel;node->right = sentinel;ngx_rbt_red(node);
}// 查找IP和访问计数统计
static ngx_int_t ngx_http_pagecount_lookup(ngx_http_request_t *r, ngx_http_pagecount_conf_t *conf, ngx_uint_t key) {ngx_rbtree_node_t *node, *sentinel;node = conf->sh->rbtree.root;sentinel = conf->sh->rbtree.sentinel;ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 111 --> %x\n", key);while (node != sentinel) {if (key < node->key) {node = node->left;continue;} else if (key > node->key) {node = node->right;continue;} else { // key == nodenode->data ++;return NGX_OK;}}ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 222 --> %x\n", key);// insert rbtreenode = ngx_slab_alloc_locked(conf->shpool, sizeof(ngx_rbtree_node_t));if (NULL == node) {return NGX_ERROR;}node->key = key;node->data = 1;ngx_rbtree_insert(&conf->sh->rbtree, node);ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " insert success\n");return NGX_OK;
}static int ngx_encode_http_page_rb(ngx_http_pagecount_conf_t *conf, char *html) {sprintf(html, "<h1>Source Insight </h1>");strcat(html, "<h2>");//ngx_rbtree_traversal(&ngx_pv_tree, ngx_pv_tree.root, ngx_http_count_rbtree_iterator, html);ngx_rbtree_node_t *node = ngx_rbtree_min(conf->sh->rbtree.root, conf->sh->rbtree.sentinel);do {char str[INET_ADDRSTRLEN] = {0};char buffer[128] = {0};sprintf(buffer, "req from : %s, count: %d <br/>",inet_ntop(AF_INET, &node->key, str, sizeof(str)), node->data);strcat(html, buffer);node = ngx_rbtree_next(&conf->sh->rbtree, node);} while (node);strcat(html, "</h2>");return NGX_OK;
}static ngx_int_t ngx_http_pagecount_handler(ngx_http_request_t *r) {u_char html[1024] = {0};int len = sizeof(html);ngx_rbtree_key_t key = 0;struct sockaddr_in *client_addr =  (struct sockaddr_in*)r->connection->sockaddr;ngx_http_pagecount_conf_t *conf = ngx_http_get_module_loc_conf(r, ngx_http_pagecount_module);key = (ngx_rbtree_key_t)client_addr->sin_addr.s_addr;ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_handler --> %x\n", key);ngx_shmtx_lock(&conf->shpool->mutex);ngx_http_pagecount_lookup(r, conf, key);	ngx_shmtx_unlock(&conf->shpool->mutex);ngx_encode_http_page_rb(conf, (char*)html);//headerr->headers_out.status = 200;ngx_str_set(&r->headers_out.content_type, "text/html");ngx_http_send_header(r);//bodyngx_buf_t *b = ngx_pcalloc(r->pool,  sizeof(ngx_buf_t));ngx_chain_t out;out.buf = b;out.next = NULL;b->pos = html;b->last = html+len;b->memory = 1;b->last_buf = 1;return ngx_http_output_filter(r, &out);}

2.2、编写config文件

创建:

touch config

内容:

ngx_addon_name=ngx_http_pagecount_module
HTTP_MODULES="$HTTP_MODULES ngx_http_pagecount_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_pagecount_module.c"

注意,config文件要和模块的代码在相同目录。

2.3、编译模块到Nginx源码中

(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/nginx-module/ngx_http_pagecount_module

注意模块路径要正确。出现如下表示成功:

configuring additional modules
adding module in /mnt/hgfs/sourcecode_learning/nginx-module/ngx_http_pagecount_module+ ngx_http_pagecount_module was configured
creating objs/Makefile

(2)编译安装:

make && sudo make install

2.4、修改conf文件

编译安装完成后,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;}}

2.5、执行效果

关闭已启动的nginx:

sudo /usr/local/nginx/sbin/nginx -s stop

启动nginx:

sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf 

在网页输入IP和端口,执行效果如下:
在这里插入图片描述

总结

  1. 实现一个基于Nginx的IP统计或访问流量统计,可以借鉴以上代码;只要做一些业务上的修改就可以直接使用。可能需改动的地方就是红黑树的key、value的数据结构,以及ngx_encode_http_page_rb函数的业务代码,其他可以基本不用改动就可以二次开发。
  2. Nginx需要熟悉的数据结构:内存池、queue、list、array、shmem等。同时需要清楚Nginx的11个状态。
  3. 在实际应用中,需要掌握Nginx的conf文件配置(https的配置、负载均衡的配置、反向代理、CPU亲缘性配置等)以及模块开发(filter、handler等)。

相关文章:

Nginx模块开发之http handler实现流量统计(2)

文章目录 一、概述二、Nginx handler模块开发2.1、代码实现2.2、编写config文件2.3、编译模块到Nginx源码中2.4、修改conf文件2.5、执行效果 总结 一、概述 上一篇【Nginx模块开发之http handler实现流量统计&#xff08;1&#xff09;】使用数组在单进程实现了IP的流量统计&a…...

案例012:Java+SSM+uniapp基于微信小程序的科创微应用平台设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…...

vue3+elementPlus登录向后端服务器发起数据请求Ajax

后端的url登录接口 先修改main.js文件 // 导入Ajax 前后端数据传输 import axios from "axios"; const app createApp(App) //vue3.0使用app.config.globalProperties.$http app.config.globalProperties.$http axios app.mount(#app); login.vue 页面显示部分…...

存储区域

将应用程序加载到内存空间执行时&#xff0c;操作系统负责代码段、数据段和BSS段的加载&#xff0c;并在内存中为这些段分配空间。 栈段亦由操作系统分配和管理&#xff0c;而不需要程序员显示地管理&#xff1b;堆段由程序员自己管理&#xff0c;即显示地申请和释放空间。 进…...

C#串口通信从入门到精通(27)——高速通信下解决数据处理慢的问题(20ms以内)

前言 我们在开发串口通信程序时,有时候会遇到比如单片机或者传感器发送的数据速度特别快,比如10ms、20ms发送一次,并且每次发送的数据量还比较大,如果按照常规的写法,我们会发现接收的数据还没处理完,新的数据又发送过来了,这就会导致处理数据滞后,软件始终处理的不是…...

Redis-Redis高可用集群之水平扩展

Redis3.0以后的版本虽然有了集群功能&#xff0c;提供了比之前版本的哨兵模式更高的性能与可用性&#xff0c;但是集群的水平扩展却比较麻烦&#xff0c;今天就来带大家看看redis高可用集群如何做水平扩展&#xff0c;原始集群(见下图)由6个节点组成&#xff0c;6个节点分布在三…...

2023全球数字贸易创新大赛-人工智能元宇宙-4-10

目录 竞赛感悟: 创业的话 好的项目 数字工厂,智慧制造:集群控制的安全问题...

go defer用法_类似与python_java_finially

defer 执行 时间 defer 一般 定义在 函数 开头, 但是 他会 最后 被执行 A defer statement defers the execution of a function until the surrounding function returns. 如果说 为什么 不在 末尾 定义 defer 呢, 因为 当 错误 发生时, 程序 执行 不到 末尾 就会 崩溃. d…...

Log4j2.xml不生效:WARN StatusLogger Multiple logging implementations found:

背景 将 -Dlog4j.debug 添加到IDEA的类的启动配置中 运行上图代码&#xff0c;这里log4j2.xml控制的日志级别是info&#xff0c;很明显是没生效。 DEBUG StatusLogger org.slf4j.helpers.Log4jLoggerFactory is not on classpath. Good! DEBUG StatusLogger Using Shutdow…...

【LeetCode】挑战100天 Day14(热题+面试经典150题)

【LeetCode】挑战100天 Day14&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-162.1 题目2.2 题解 三、面试经典 150 题-163.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…...

VMware安装windows操作系统

一、下载镜像包 地址&#xff1a;镜像包地址。 找到需要的版本下载镜像包。 二、安装 打开VMware新建虚拟机&#xff0c;选择用镜像文件。将下载的镜像包加载进去即可。...

历时半年,我发布了一款习惯打卡小程序

半年多前&#xff0c;我一直困扰于如何记录习惯打卡情况&#xff0c;在参考了市面上绝大多数的习惯培养程序后&#xff0c;终于创建并发布了这款习惯打卡小程序。 “我的小日常打卡”小程序主要提供习惯打卡和专注训练功能。致力于培养用户养成一个个好的习惯&#xff0c;改掉…...

被DDOS了怎么办 要如何应对

DDoS攻击的特点和类型 1. 特点 DDoS攻击的特点是通过大量合法的请求或者无效的请求&#xff0c;消耗目标服务器的网络带宽和系统资源&#xff0c;使其无法正常运行。攻击者通常使用多个主机发起攻击&#xff0c;以达到更高的攻击效果。 2. 常见类型 &#xff08;1&#xff09;S…...

时间序列预测实战(十七)PyTorch实现LSTM-GRU模型长期预测并可视化结果(附代码+数据集+详细讲解)

一、本文介绍 本文给大家带来的实战内容是利用PyTorch实现LSTM-GRU模型&#xff0c;LSTM和GRU都分别是RNN中最常用Cell之一&#xff0c;也都是时间序列预测中最常见的结构单元之一&#xff0c;本文的内容将会从实战的角度带你分析LSTM和GRU的机制和效果&#xff0c;同时如果你…...

【免费使用】基于PaddleSeg开源项目开发的人像抠图Web API接口

基于PaddleSeg开源项目开发的人像抠图API接口&#xff0c;服务器不存储照片大家可放心使用。 1、请求接口 请求地址&#xff1a;http://apiseg.hysys.cn/predict_img 请求方式&#xff1a;POST 请求参数&#xff1a;{"image":"/9j/4AAQ..."} 参数是jso…...

Centos7 Python环境和yum修复

1、删除现有残余包 [rootlocalhost ]# rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps[rootlocalhost ]# rpm -qa|grep yum|xargs rpm -ev --allmatches --nodeps[rootlocalhost ]# whereis python |xargs rm -frv[rootlocalhost ]# whereis python ##验证清除&…...

Ubuntu下使用protoBuf

一、protobuf简介&#xff1a; 1.1 protobuf的定义&#xff1a; protobuf是用来干嘛的&#xff1f; protobuf是一种用于 对结构数据进行序列化的工具&#xff0c;从而实现 数据存储和交换。 &#xff08;主要用于网络通信中 收发两端进行消息交互。所谓的“结构数据”是指类…...

AT89S52单片机

目录 一.AT89S52单片机的硬件组成 1.CPU(微处理器) (1)运算器 (2)控制器 2.数据存储器 (RAM) (1)片内数据存储器 (2)片外数据存储器 3.程序存储器(Flash ROM) 4.定时器/计数器 5.中断系统 6.串行口 7.P0口、P1口、P2口和P3口 8.特殊功能寄存器 (SFR) 常用的特殊功…...

数字孪生智慧校园 Web 3D 可视化监测

当今&#xff0c;智慧校园发展阶段亟需推动信息可视化建设与发展&#xff0c;将大数据、云计算、可视化等高新技术相融合&#xff0c;为校园师生创造科学智能的学习环境&#xff0c;并实现教学资源最大化和信息服务智能化。帮助学校更好地应用校园可视化技术&#xff0c;提升校…...

Python Web框架的三强之争:Flask、Django和FastAPI

JetBrains 公布 2022 Python 开发者调查结果。 完整报告地址&#xff1a;https://lp.jetbrains.com/zh-cn/python-developers-survey-2022/ 这是由 Python 软件基金会 (PSF) 和 JetBrains 共同开展的第六次官方年度 Python 开发者调查&#xff0c;回复于 2022 年 10 月至 12 …...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...