nginx模块使用、过滤器模块以及handler模块

一、如何使用nginx的模块

1.ngx_code.c:

#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"
#include "ngx_array.h"
#include "ngx_hash.h"#include<stdio.h>
#include<stdlib.h>//nginx头文件中又包含下面的两个变量,所以我们必须包含#define unused(x)  x=xvolatile ngx_cycle_t  *ngx_cycle;void
ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,const char *fmt, ...) {unused(level);unused(log);unused(err);unused(fmt);}void print_pool(ngx_pool_t *pool) {printf("\nlast: %p, end: %p\n", pool->d.last, pool->d.end);}int main(){
#if 0ngx_str_t name =ngx_string("jerry");printf("name --> len:%ld, data: %s\n",name.len,name.data);
#else//内存池的使用ngx_pool_t *pool = ngx_create_pool(4096,NULL);print_pool(pool);void *p1 = ngx_palloc(pool,10);print_pool(pool);int *p2 = ngx_palloc(pool,sizeof(int));print_pool(pool);ngx_destroy_pool(pool);#endifreturn 0;
}

2. Makefile:

CXX = gcc
CXXFLAGS += -g -Wall -WextraNGX_ROOT = /home/jerry/snap/nginx/nginx-1.22.1
PCRE_ROOT = /home/jerry/snap/nginx/pcre-8.45TARGETS = ngx_code
TARGETS_C_FILE = $(TARGETS).cCLEANUP = rm -f $(TARGETS) *.oall: $(TARGETS)clean:$(CLEANUP)CORE_INCS = -I. \-I$(NGX_ROOT)/src/core \-I$(NGX_ROOT)/src/event \-I$(NGX_ROOT)/src/event/modules \-I$(NGX_ROOT)/src/os/unix \-I$(NGX_ROOT)/objs \-I$(PCRE_ROOT) \NGX_PALLOC = $(NGX_ROOT)/objs/src/core/ngx_palloc.o
NGX_STRING = $(NGX_ROOT)/objs/src/core/ngx_string.o
NGX_ALLOC = $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o
NGX_ARRAY = $(NGX_ROOT)/objs/src/core/ngx_array.o
NGX_HASH = $(NGX_ROOT)/objs/src/core/ngx_hash.o$(TARGETS): $(TARGETS_C_FILE)$(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING) $(NGX_ALLOC) $(NGX_ARRAY) $(NGX_HASH) $^ -o $@

3. 编译运行结果如下:

 二、 过滤器模块

        Nginx 的过滤器(Filter)模块是其 HTTP 处理流程中非常重要的组件,主要用于修改 HTTP 响应的头部或正文内容。它就像一个 “中间处理层”,可以在响应回发给客户端之前,对数据进行加工(如添加内容、压缩、转码等)。具体源码如下:

1. ngx_http_prefix_module.c:

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>//定义一个结构体 ngx_http_prefix_filter_conf_t,用于存储模块的配置信息。这里包含一个 ngx_flag_t 类型的 enable 标志,用于启用或禁用模块的功能。
typedef struct {ngx_flag_t enable;
} ngx_http_prefix_filter_conf_t;//定义一个上下文结构体 ngx_http_prefix_filter_ctx_t,用于存储与请求相关的状态信息。这里的 add_prefix 标志用于指示是否需要在响应体前添加前缀。
typedef struct {ngx_int_t add_prefix;
} ngx_http_prefix_filter_ctx_t;static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);//filter_prefix 是一个静态字符串,定义了要添加到响应体前的 HTML 前缀。
static ngx_str_t filter_prefix = ngx_string("<h2>Author : King</h2><p><a href=\"http://www.0voice.com\">0voice</a></p>");/*
ngx_http_prefix_filter_create_conf 用于创建模块的配置块。
使用 ngx_pcalloc 分配内存并初始化为零。
enable 被设置为 NGX_CONF_UNSET,表示默认未设置
*/static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));if (conf == NULL) {return NULL;}conf->enable = NGX_CONF_UNSET;return conf;
}/*
ngx_http_prefix_filter_merge_conf 用于合并父配置和子配置。这里合并 enable 标志的值,
优先使用子配置的值,如果子配置未设置,则使用父配置的值。默认值为 0。
*/static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;ngx_conf_merge_value(conf->enable, prev->enable, 0);return NGX_CONF_OK;
}/*
ngx_http_prefix_filter_commands 定义了一个名为 add_prefix 的指令。
该指令可以放在 http、server、location 和 limit 块中。
ngx_conf_set_flag_slot 是处理这个指令的回调函数,用于设置标志位 enable。
*/static ngx_command_t ngx_http_prefix_filter_commands[] = {{ngx_string("add_prefix"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,ngx_conf_set_flag_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_prefix_filter_conf_t, enable),NULL},ngx_null_command
};/*
ngx_http_prefix_filter_module_ctx 定义了模块的上下文,
包括创建配置块和合并配置的函数。
ngx_http_prefix_filter_init 用于初始化模块。
*/
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {NULL,ngx_http_prefix_filter_init,NULL,NULL,NULL,NULL,ngx_http_prefix_filter_create_conf,ngx_http_prefix_filter_merge_conf
};//ngx_http_prefix_filter_module 是模块的主要定义,包括模块的版本、上下文、命令和类型等。
ngx_module_t ngx_http_prefix_filter_module = {NGX_MODULE_V1,&ngx_http_prefix_filter_module_ctx,ngx_http_prefix_filter_commands,NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING
}; static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;/*
ngx_http_prefix_filter_init 是模块初始化函数。它将自定义的头部和主体过滤器插入到 Nginx 的过滤器链中。
ngx_http_prefix_filter_header_filter 和 ngx_http_prefix_filter_body_filter 将被设置为新的过滤器函数。
*/
//总的来说,这个函数就是截胡
static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf) {ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;return NGX_OK;
}/*
ngx_http_prefix_filter_header_filter 函数用于处理 HTTP 响应头。
如果响应状态码不是 200 OK,直接传递给下一个头部过滤器。检查模块上下文 ctx,
如果存在则直接传递。如果配置 enable 为 0,则直接传递。
否则,根据 Content-Type 判断是否是 text/html,
如果是,将前缀添加到响应头的 content_length 中,
并将 add_prefix 设置为 1。
*/static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {ngx_http_prefix_filter_ctx_t *ctx;ngx_http_prefix_filter_conf_t *conf;if (r->headers_out.status != NGX_HTTP_OK) {return ngx_http_next_header_filter(r);}ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);if (ctx) {return ngx_http_next_header_filter(r);}conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);if (conf == NULL) {return ngx_http_next_header_filter(r);}if (conf->enable == 0) {return ngx_http_next_header_filter(r);}ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));if (ctx == NULL) {return NGX_ERROR;}ctx->add_prefix = 0;ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);if (r->headers_out.content_type.len >= sizeof("text/html") - 1&& ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-1) == 0) {ctx->add_prefix = 1;if (r->headers_out.content_length_n > 0) {r->headers_out.content_length_n += filter_prefix.len;}}//return ngx_http_prefix_filter_header_filter(r);return ngx_http_next_header_filter(r);
}/*
ngx_http_prefix_filter_body_filter 函数用于处理 HTTP 响应体。
首先检查上下文 ctx 和 add_prefix 标志。
如果没有上下文或标志不是 1,直接传递给下一个主体过滤器。
如果 add_prefix 为 1,则设置 add_prefix 为 2,创建一个临时缓冲区 b,将前缀写入缓冲区,并将缓冲区添加到过滤器链 cl 中。
最后,将 cl 链传递给下一个主体过滤器。
*/static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);if (ctx == NULL || ctx->add_prefix != 1) {return ngx_http_next_body_filter(r, in);}ctx->add_prefix = 2;ngx_buf_t *b = ngx_create_temp_buf(r->pool, filter_prefix.len);b->start = b->pos = filter_prefix.data;b->last = b->pos + filter_prefix.len;ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);cl->buf = b;cl->next = in;return ngx_http_next_body_filter(r, cl);
}

以下详细解析一下上面的代码:

一、头文件与基础定义

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
  • 作用:包含 Nginx 核心库和 HTTP 模块开发所需的头文件。
    • ngx_config.h:Nginx 编译相关的配置宏。
    • ngx_core.h:Nginx 核心数据结构(如 ngx_str_tngx_pool_t)和函数(如内存分配)。
    • ngx_http.h:HTTP 模块开发的核心头文件(如 ngx_http_request_t、过滤器相关类型)。

二、配置与上下文结构体

// 模块配置结构体(存储全局/局部配置)
typedef struct {ngx_flag_t enable;  // 0=禁用,1=启用(通过配置指令控制)
} ngx_http_prefix_filter_conf_t;// 请求上下文结构体(存储单次请求的状态)
typedef struct {ngx_int_t add_prefix;  // 0=不添加,1=待添加,2=已添加
} ngx_http_prefix_filter_ctx_t;
  • 配置结构体:存储模块的全局或局部配置(如是否启用),通过 Nginx 的配置系统(ngx_command_t)与配置文件中的指令绑定。
  • 上下文结构体:存储单次 HTTP 请求的状态(如是否需要添加前缀),避免不同请求之间的状态污染(通过 ngx_http_set_ctx 与请求绑定)。

三、函数声明与前缀内容

static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);// 要添加的 HTML 前缀内容
static ngx_str_t filter_prefix = ngx_string("<h2>Author : jerry</h2><p><a href=\"http://www.0voice.com\">0voice</a></p>");
  • 函数声明:模块初始化函数、头部过滤器、正文过滤器(均为静态,仅在本模块内可见)。
  • filter_prefix:使用 ngx_string 宏初始化一个 ngx_str_t 结构体(包含字符串指针和长度),存储要添加的 HTML 前缀内容。

四、配置块创建与合并

// 创建配置块(初始化时调用)
static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {// 从内存池(cf->pool)中分配配置结构体内存ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));if (conf == NULL) {return NULL;  // 内存分配失败}conf->enable = NGX_CONF_UNSET;  // 初始化为“未设置”状态(由后续合并配置决定默认值)return conf;
}// 合并父子配置(如 http 块与 server 块的配置冲突时)
static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;  // 父配置(如 http 块)ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;    // 子配置(如 server/location 块)// 合并 enable 标志:若子配置未设置(NGX_CONF_UNSET),则使用父配置的值;默认值为 0(禁用)ngx_conf_merge_value(conf->enable, prev->enable, 0);return NGX_CONF_OK;  // 合并成功
}
  • ngx_pcalloc:Nginx 内存池分配函数(自动清零内存),避免野指针。
  • NGX_CONF_UNSET:配置未显式设置时的标记值,通过 ngx_conf_merge_value 合并父子配置后确定最终值。

五、配置指令定义

static ngx_command_t ngx_http_prefix_filter_commands[] = {{ngx_string("add_prefix"),  // 配置指令名(在 nginx.conf 中使用)NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,  // 指令作用域(http/server/location/limit_except 块)ngx_conf_set_flag_slot,  // 指令处理函数(自动将“on/off”转换为 1/0,赋值给 enable)NGX_HTTP_LOC_CONF_OFFSET,  // 配置存储位置(location 块的配置)offsetof(ngx_http_prefix_filter_conf_t, enable),  // enable 字段在配置结构体中的偏移量NULL  // 额外参数(此处不需要)},ngx_null_command  // 指令数组结束标记
};
  • 指令作用域NGX_HTTP_MAIN_CONF(http 块)、NGX_HTTP_SRV_CONF(server 块)等,表示该指令可以在这些配置块中使用。
  • ngx_conf_set_flag_slot:Nginx 内置的指令处理函数,用于将 on/off 转换为 ngx_flag_t 类型的 1/0,并赋值给配置结构体的 enable 字段。

六、模块上下文与模块定义

// 模块上下文(定义模块的初始化、配置相关函数)
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {NULL,                          // preconfiguration(HTTP 框架初始化前调用)ngx_http_prefix_filter_init,   // postconfiguration(HTTP 框架初始化后调用,用于注册过滤器)NULL,                          // create_main_conf(创建 http 块配置)NULL,                          // init_main_conf(初始化 http 块配置)NULL,                          // create_srv_conf(创建 server 块配置)NULL,                          // merge_srv_conf(合并 server 块配置)ngx_http_prefix_filter_create_conf,  // create_loc_conf(创建 location 块配置)ngx_http_prefix_filter_merge_conf    // merge_loc_conf(合并 location 块配置)
};// 模块主结构体(向 Nginx 注册模块)
ngx_module_t ngx_http_prefix_filter_module = {NGX_MODULE_V1,                 // 模块版本(固定宏)&ngx_http_prefix_filter_module_ctx,  // 模块上下文ngx_http_prefix_filter_commands,     // 配置指令数组NGX_HTTP_MODULE,            // 模块类型(HTTP 模块)NULL,                       // init_master(Master 进程初始化)NULL,                       // init_module(模块初始化,全局仅一次)NULL,                       // init_process(Worker 进程初始化)NULL,                       // exit_process(Worker 进程退出)NULL,                       // exit_module(模块退出)NULL,                       // exit_master(Master 进程退出)NULL,                       // 模块名(可选)NGX_MODULE_V1_PADDING       // 填充字段(保持结构对齐)
};
  • ngx_http_module_t:HTTP 模块的上下文结构体,定义模块在不同生命周期的回调函数(如 postconfiguration 用于注册过滤器)。
  • ngx_module_t:Nginx 模块的主结构体,用于向 Nginx 核心注册模块的类型、上下文、指令等信息。

七、过滤器链初始化

// 保存原始的头部和正文过滤器链(用于传递请求)
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;// 模块初始化函数(在 HTTP 框架初始化后调用)
static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf) {// 将自定义头部过滤器插入到链顶,原头部过滤器变为“下一个”ngx_http_next_header_filter = ngx_http_top_header_filter;ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;// 同理处理正文过滤器链ngx_http_next_body_filter = ngx_http_top_body_filter;ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;return NGX_OK;  // 初始化成功
}
  • 过滤器链机制:Nginx 的 HTTP 响应处理通过 “过滤器链” 实现,每个过滤器处理完后将请求传递给链中的下一个过滤器。
  • ngx_http_top_header_filter:头部过滤器链的头部指针。通过将自定义过滤器设为新的链头(ngx_http_top_header_filter),并将原链头保存到 ngx_http_next_header_filter,确保自定义过滤器优先执行,处理完后通过 ngx_http_next_header_filter 传递给后续过滤器。

八、头部过滤器:ngx_http_prefix_filter_header_filter

static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {ngx_http_prefix_filter_ctx_t *ctx;    // 请求上下文(存储 add_prefix 标志)ngx_http_prefix_filter_conf_t *conf;  // 模块配置(enable 标志)// 1. 仅处理状态码为 200 OK 的响应if (r->headers_out.status != NGX_HTTP_OK) {return ngx_http_next_header_filter(r);  // 传递给下一个头部过滤器}// 2. 检查是否已存在上下文(避免重复处理)ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);if (ctx) {return ngx_http_next_header_filter(r);}// 3. 获取 location 块的配置(检查是否启用模块)conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);if (conf == NULL || conf->enable == 0) {  // 配置不存在或未启用return ngx_http_next_header_filter(r);}// 4. 创建上下文并绑定到请求ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));if (ctx == NULL) {return NGX_ERROR;  // 内存分配失败,终止请求}ctx->add_prefix = 0;  // 默认不添加前缀ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);  // 绑定上下文到请求// 5. 检查响应类型是否为 text/htmlif (r->headers_out.content_type.len >= sizeof("text/html") - 1  // 确保 content_type 长度足够&& ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-1) == 0) {ctx->add_prefix = 1;  // 标记需要添加前缀// 调整 Content-Length(若存在):原长度 + 前缀长度if (r->headers_out.content_length_n > 0) {r->headers_out.content_length_n += filter_prefix.len;}}// 6. 传递给下一个头部过滤器(关键修正:原代码错误递归调用自身,现已修正)return ngx_http_next_header_filter(r);
}
  • 核心逻辑
    • 仅处理 200 OK 响应,避免修改错误页面(如 404 Not Found)。
    • 通过 ngx_http_get_module_ctx 检查是否已存在上下文(防止重复处理同一请求)。
    • 通过 ngx_http_get_module_loc_conf 获取当前 location 块的配置,若未启用则跳过。
    • 为请求创建上下文(ngx_http_prefix_filter_ctx_t),并通过 ngx_http_set_ctx 绑定到请求(生命周期与请求一致)。
    • 检查响应的 Content-Type 是否为 text/html(忽略大小写),若是则标记 add_prefix=1,并调整 Content-Length(避免客户端接收数据时长度不符)。

九、正文过滤器:ngx_http_prefix_filter_body_filter

static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {// 1. 获取请求上下文,检查是否需要添加前缀ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);if (ctx == NULL || ctx->add_prefix != 1) {  // 无上下文或未标记添加return ngx_http_next_body_filter(r, in);  // 传递给下一个正文过滤器}// 2. 标记前缀已添加(避免重复处理)ctx->add_prefix = 2;// 3. 创建临时缓冲区,存储前缀内容ngx_buf_t *b = ngx_create_temp_buf(r->pool, filter_prefix.len);  // 创建指定长度的缓冲区if (b == NULL) {return NGX_ERROR;  // 内存分配失败}b->start = b->pos = filter_prefix.data;  // 缓冲区起始位置指向前缀内容b->last = b->pos + filter_prefix.len;     // 缓冲区结束位置(前缀内容长度)b->temporary = 1;  // 标记缓冲区为临时(Nginx 会自动管理内存)// 4. 构造新的缓冲区链(前缀在前,原始内容在后)ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);  // 分配链节点if (cl == NULL) {return NGX_ERROR;}cl->buf = b;       // 链节点指向前缀缓冲区cl->next = in;     // 链的下一个节点指向原始响应体(in 是原始缓冲区链)// 5. 传递给下一个正文过滤器(处理后的链包含前缀+原始内容)return ngx_http_next_body_filter(r, cl);
}
  • 核心逻辑
    • 检查上下文标志 add_prefix=1(仅当头部过滤器标记需要添加时执行)。
    • 使用 ngx_create_temp_buf 创建临时缓冲区,存储前缀内容(filter_prefix)。
    • 使用 ngx_alloc_chain_link 分配链节点,将前缀缓冲区与原始响应体链(in)拼接,形成新的链(cl->next = in)。
    • 将新链传递给下一个正文过滤器(如 chunked 编码过滤器、gzip 过滤器等),最终发送给客户端。

十、总结

这段代码实现了一个 Nginx 过滤器模块,核心功能是:在启用 add_prefix on 的 location 块中,对 200 OK 的 text/html 响应,在正文前添加固定的 HTML 前缀(如作者信息)。

关键流程:
  1. 配置加载:通过 add_prefix 指令控制模块是否启用(on/off)。
  2. 过滤器注册:在 ngx_http_prefix_filter_init 中将自定义过滤器插入到过滤器链顶部。
  3. 头部过滤:检查响应状态码、配置、Content-Type,调整 Content-Length 并标记需要添加前缀。
  4. 正文过滤:根据标记,将前缀内容插入到响应体前,传递给后续过滤器。

2.config:

ngx_addon_name=ngx_http_prefix_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_prefix_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_prefix_filter_module.c"

3. 具体操作步骤

3.1 现在文件夹中创建config文件和.c文件

3.2 配置config文件,让源码再逼阿姨的过程中包含自己的Module

3.3 写.c文件

3.4 在源码下config编译

./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/jerry/snap/nginx/pcre-8.45 --with-zlib=/home/jerry/snap/nginx/zlib-1.2.13 --with-openssl=/home/jerry/snap/nginx/openssl-1.1.1s --add-module=/usr/local/nginx/ngx_code_test/ngx_http_prefix_filter_module

3.5 make + make install 

3.6 在我们自己的jerry.conf文件中添加.c文件里的特殊关键字: 这里是add_prefix

root@jerry-virtual-machine:/usr/local/nginx# vim conf/jerry.conf

3.7 在对应目录添加html文件:

sudo mkdir -p /usr/local/nginx/html8001/html

vim index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>8001 服务页面</title><style>body { font-family: Arial, sans-serif; text-align: center; background-color: #f0f0f0; }.container { margin: 100px auto; padding: 20px; background-color: white; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }h1 { color: #2c3e50; }p { color: #7f8c8d; font-size: 1.1em; }</style>
</head>
<body><div class="container"><h1>欢迎访问 8001 端口服务!</h1><p>这是来自 nginx 8001 端口的静态页面。</p><p>当前时间:<span id="currentTime"></span></p></div><script>// 动态显示当前时间function updateTime() {const timeElement = document.getElementById('currentTime');const now = new Date().toLocaleString();timeElement.textContent = now;}// 每秒钟更新一次时间setInterval(updateTime, 1000);updateTime(); // 初始加载时立即显示</script>
</body>
</html>

3.8 启动nginx服务器,使用我们自己的配置文件

 3.9 打开浏览器,输入网址加端口:

192.168.186.138:8000

 三、Handler模块

        在 Nginx 的 HTTP 处理框架中,Handler 模块是处理具体请求的核心组件,负责生成最终的响应内容。当请求经过 Nginx 的路由匹配(如匹配到某个 location 块)后,Nginx 会调用对应的 Handler 来处理请求,例如读取静态文件、与后端程序(如 PHP-FPM、Java 应用)交互获取动态内容等。具体代码如下:

1.ngx_http_handler_count_module.c:


#include <ngx_http.h>
#include <ngx_core.h>#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>#define ENABLE_RBTREE   1/*
ngx_pv_table_t 结构体定义了一个包含 count 和 addr 的结构体。
count: 记录来自特定 IP 地址的请求次数。
addr: 存储 IP 地址。
*/typedef struct{int count;struct in_addr addr;
}ngx_pv_table_t;ngx_pv_table_t pv_table[256];#if ENABLE_RBTREEstatic ngx_rbtree_t ngx_pv_rbtree;static ngx_rbtree_node_t ngx_pv_sentinel;//插入新结点void ngx_rbtree_insert_count_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,ngx_rbtree_node_t *sentinel)
{   ngx_rbtree_node_t  **p;for ( ;; ) {/** Timer values* 1) are spread in small range, usually several minutes,* 2) and overflow each 49 days, if milliseconds are stored in 32 bits.* The comparison takes into account that overflow.*//*  node->key < temp->key */
#if 0p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)? &temp->left : &temp->right;
#elseif (node->key < temp->key) {p = &temp->left;} else if (node->key > temp->key) {p = &temp->right;} else { // node->key == temp->keyreturn ;}#endifif (*p == sentinel) {break;}temp = *p;}*p = node;node->parent = temp;node->left = sentinel;node->right = sentinel;ngx_rbt_red(node);
}//
ngx_rbtree_node_t *ngx_rbtree_count_search(ngx_rbtree_t *rbtree, ngx_rbtree_key_t key) {ngx_rbtree_node_t *temp = rbtree->root;ngx_rbtree_node_t  **p;for ( ;; ) {if (key < temp->key) {p = &temp->left;} else if (key > temp->key) {p = &temp->right;} else { // node->key == temp->keyreturn temp;}if (*p == &ngx_pv_sentinel) {return NULL;}temp = *p;}}void ngx_rbtree_count_traversal(ngx_rbtree_t *T, ngx_rbtree_node_t *node, char *html) {	if (node != &ngx_pv_sentinel) {		ngx_rbtree_count_traversal(T, node->left, html);		//printf("key:%d, color:%d\n", node->key, node->color);	char str[INET_ADDRSTRLEN] = {0};char buffer[128] = {0};snprintf(buffer, 128, "req from : %s, count : %d <br/>",inet_ntop(AF_INET, &node->key, str, sizeof(str)), node->data);strcat(html, buffer);ngx_rbtree_count_traversal(T, node->right, html);	}
}#endifchar  *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_int_t ngx_http_handler_count_handler(ngx_http_request_t *r);
ngx_int_t   ngx_http_handler_count_init(ngx_conf_t *cf);/*
ngx_http_handler_count_cmds 定义了一个 Nginx 配置指令 count。
ngx_string("count"): 配置指令的名称。
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS: 指令作用于位置配置,并且不需要额外参数。
ngx_http_handler_count_set: 当指令被解析时调用的处理函数。
NGX_HTTP_LOC_CONF_OFFSET: 配置项的偏移量。
0: 没有特别的标志。
NULL: 额外的配置项。
*/
static ngx_command_t ngx_http_handler_count_cmds[] = {{ngx_string("count"),NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,ngx_http_handler_count_set,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command
};/*ngx_http_handler_count_ctx 定义了模块的上下文。
这些字段用于定义模块的初始化、配置和处理函数。
ngx_http_handler_count_init 被注释掉了,如果需要模块初始化,请取消注释。*/
// share mem
static ngx_http_module_t ngx_http_handler_count_ctx = {NULL,//ngx_http_handler_count_init, //NULL,NULL,NULL,NULL,NULL,NULL,NULL,
}; /*
ngx_http_handler_count_module 定义了模块的主要结构。
NGX_MODULE_V1: 模块的版本。
&ngx_http_handler_count_ctx: 模块上下文。
ngx_http_handler_count_cmds: 模块的配置指令。
NGX_HTTP_MODULE: 模块类型(HTTP 模块)。
NULL: 一些其他的字段(可选)设置为空。
*/ngx_module_t ngx_http_handler_count_module = {NGX_MODULE_V1,&ngx_http_handler_count_ctx,ngx_http_handler_count_cmds,NGX_HTTP_MODULE,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING
};//当在conf文件中遇到count这个命令时,会调用这个回调函数 
char  *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
#if ENABLE_RBTREEngx_rbtree_init(&ngx_pv_rbtree,&ngx_pv_sentinel,ngx_rbtree_insert_count_value);
#endif    ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);corecf->handler = ngx_http_handler_count_handler;return NGX_CONF_OK;}//组织网页
static int ngx_http_encode_page(char *html) {sprintf(html, "<h1>zvoice.jerry</h1>");strcat(html, "<h2>");#if ENABLE_RBTREEngx_rbtree_count_traversal(&ngx_pv_rbtree, ngx_pv_rbtree.root, html);#elseint i = 0;for (i = 0;i < 256;i ++) {if (pv_table[i].count != 0) {char str[INET_ADDRSTRLEN] = {0};char buffer[128] = {0};snprintf(buffer, 128, "req from : %s, count : %d <br/>",inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)), pv_table[i].count);strcat(html, buffer);}}
#endifstrcat(html, "<h2/>");return 0;
}/*
ngx_http_handler_count_handler 函数处理 HTTP 请求。
u_char html[1024] = {0};: 定义一个用于存储 HTML 内容的缓冲区。
int len = sizeof(html);: 获取缓冲区的大小。
struct sockaddr_in *client_addr = (struct sockaddr_in *)r->connection->sockaddr;: 获取客户端地址信息。
计算 idx 作为 IP 地址的索引,将计数增加 1,并更新 pv_table 中的 IP 地址。
使用 ngx_http_encode_page 函数将数据编码成 HTML。
设置 HTTP 响应状态为 200,并设置内容类型为 text/html。
通过 ngx_http_send_header 发送响应头。
创建一个 ngx_buf_t 缓冲区,用于存储 HTML 内容,并通过 ngx_http_output_filter 发送响应体
*///每次发起请求,就会走到这里
ngx_int_t ngx_http_handler_count_handler(ngx_http_request_t *r) {//可以获取到对端的信息u_char html[1024] = {0};int len = sizeof(html);struct sockaddr_in *client_addr = (struct sockaddr_in *)r->connection->sockaddr; // ip#if ENABLE_RBTREE//查找,找到就把count+=ngx_rbtree_key_t key = (ngx_rbtree_key_t)client_addr->sin_addr.s_addr;ngx_rbtree_node_t *node = ngx_rbtree_count_search(&ngx_pv_rbtree, key);if (!node) {node = ngx_pcalloc(r->pool, sizeof(ngx_rbtree_node_t));node->key = key;node->data = 1;ngx_rbtree_insert(&ngx_pv_rbtree, node);} else {node->data ++;}#else//拿到ip后,取最后三位,即256个作为索引,存储对应的ip的count值int idx = client_addr->sin_addr.s_addr >> 24;pv_table[idx].count ++;memcpy(&pv_table[idx].addr, &client_addr->sin_addr, sizeof(client_addr->sin_addr));
#endifngx_http_encode_page((char *)html);//返回状态默认200r->headers_out.status = 200;ngx_str_set(&r->headers_out.content_type, "text/html");//先把http头发出去ngx_http_send_header(r);//接着发body部分ngx_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);
}//

以下详细讲解一下这段代码:

1. 头文件与宏定义

#include <ngx_http.h>    // Nginx HTTP模块核心头文件
#include <ngx_core.h>    // Nginx核心功能头文件(内存管理、字符串等)
#include <arpa/inet.h>   // 网络地址转换函数(如inet_ntop)
#include <netinet/in.h>  // 网络地址结构(如struct in_addr)
#include <stdio.h>       // 标准输入输出(如snprintf)#define ENABLE_RBTREE   1  // 宏开关:1表示使用红黑树存储统计数据,0表示使用数组
  • 作用:引入 Nginx 核心库、网络相关库,通过宏控制存储方式(红黑树 / 数组)。

2. 统计数据结构体与存储容器

/* 统计数据结构体:记录IP地址和请求次数 */
typedef struct {int count;                  // 请求次数struct in_addr addr;        // IP地址(IPv4,4字节)
} ngx_pv_table_t;ngx_pv_table_t pv_table[256];   // 数组存储(仅当ENABLE_RBTREE=0时使用)
  • 说明ngx_pv_table_t用于存储单个 IP 的统计信息。pv_table是长度为 256 的数组,通过 IP 地址的最后一个字节(0-255)作为索引(可能冲突)。

3. 红黑树相关实现(当 ENABLE_RBTREE=1 时)

3.1 红黑树与哨兵节点
static ngx_rbtree_t ngx_pv_rbtree;          // 红黑树实例
static ngx_rbtree_node_t ngx_pv_sentinel;    // 哨兵节点(用于标记树的边界)
  • 红黑树:Nginx 内置的平衡二叉树结构,用于高效存储和查找 IP 统计数据(插入、查找时间复杂度 O (logN))。
  • 哨兵节点:所有叶子节点的子节点指向哨兵,避免 NULL 指针判断,简化代码。
3.2 红黑树插入函数
void ngx_rbtree_insert_count_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {ngx_rbtree_node_t  **p;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 地址)唯一。
  • 关键逻辑:根据键值大小遍历树,找到插入位置后设置父子关系,新节点初始化为红色(红黑树插入规则)。
3.3 红黑树搜索函数
ngx_rbtree_node_t *ngx_rbtree_count_search(ngx_rbtree_t *rbtree, ngx_rbtree_key_t key) {ngx_rbtree_node_t *temp = rbtree->root;  // 从根节点开始搜索for (;;) {if (key < temp->key) {        // 目标键更小,搜索左子树temp = temp->left;} else if (key > temp->key) { // 目标键更大,搜索右子树temp = temp->right;} else {                      // 找到匹配键,返回当前节点return temp;}if (temp == &ngx_pv_sentinel) {  // 遇到哨兵,说明无匹配return NULL;}}
}
  • 功能:根据键(IP 地址)搜索红黑树,返回对应节点(若存在)。
  • 关键逻辑:从根节点开始,根据键值大小递归搜索左右子树,直到找到匹配或遇到哨兵。
3.4 红黑树遍历函数
void ngx_rbtree_count_traversal(ngx_rbtree_t *T, ngx_rbtree_node_t *node, char *html) {if (node != &ngx_pv_sentinel) {       // 非哨兵节点递归遍历ngx_rbtree_count_traversal(T, node->left, html);  // 左子树// 将当前节点的IP和计数写入HTMLchar str[INET_ADDRSTRLEN] = {0};char buffer[128] = {0};snprintf(buffer, 128, "req from : %s, count : %d <br/>",inet_ntop(AF_INET, &node->key, str, sizeof(str)), node->data);strcat(html, buffer);ngx_rbtree_count_traversal(T, node->right, html);  // 右子树}
}
  • 功能:中序遍历红黑树(左 - 根 - 右),将每个节点的 IP 和计数追加到 HTML 字符串中。
  • 关键逻辑:递归遍历左右子树,使用inet_ntop将 IP 地址(node->key)转换为点分十进制字符串,拼接统计信息到 HTML。

4. Nginx 模块核心结构

4.1 配置指令定义
static ngx_command_t ngx_http_handler_count_cmds[] = {{ngx_string("count"),               // 配置指令名称:`count`NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS, // 作用域:location块;无参数ngx_http_handler_count_set,         // 指令解析回调函数NGX_HTTP_LOC_CONF_OFFSET,            // 配置存储偏移量(无需自定义)0,                                   // 无额外标志NULL                                 // 无额外配置数据},ngx_null_command  // 指令数组结束标记
};
  • 作用:定义 Nginx 配置指令count,当在location块中使用该指令时,触发ngx_http_handler_count_set函数。
4.2 模块上下文
static ngx_http_module_t ngx_http_handler_count_ctx = {NULL,  // preconfiguration(配置解析前回调)NULL,  // postconfiguration(配置解析后回调)NULL,  // create main configuration(创建主配置)NULL,  // init main configuration(初始化主配置)NULL,  // create server configuration(创建server配置)NULL,  // merge server configuration(合并server配置)NULL,  // create location configuration(创建location配置)NULL   // merge location configuration(合并location配置)
};
  • 说明:Nginx 模块上下文,用于注册配置相关的回调函数。当前模块未使用这些回调,故全部置空。
4.3 模块主结构体
ngx_module_t ngx_http_handler_count_module = {NGX_MODULE_V1,                // 模块版本(固定)&ngx_http_handler_count_ctx,  // 模块上下文ngx_http_handler_count_cmds,  // 配置指令数组NGX_HTTP_MODULE,              // 模块类型(HTTP模块)NULL, NULL, NULL, NULL,       // 其他生命周期回调(未使用)NULL, NULL, NULL, NULL,       // 其他生命周期回调(未使用)NGX_MODULE_V1_PADDING         // 填充字段(保持结构对齐)
};
  • 作用:向 Nginx 注册模块,声明模块类型、上下文和配置指令。

5. 配置指令处理函数

char *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
#if ENABLE_RBTREEngx_rbtree_init(&ngx_pv_rbtree, &ngx_pv_sentinel, ngx_rbtree_insert_count_value);  // 初始化红黑树
#endif// 获取当前location的核心配置ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);corecf->handler = ngx_http_handler_count_handler;  // 设置请求处理函数为ngx_http_handler_count_handlerreturn NGX_CONF_OK;  // 配置解析成功
}
  • 功能:当 Nginx 解析到count指令时,初始化红黑树(若启用),并将当前location的请求处理函数设置为ngx_http_handler_count_handler
  • 关键操作ngx_rbtree_init初始化红黑树(指定插入函数和哨兵);corecf->handler绑定请求处理入口。

6. HTML 生成函数

static int ngx_http_encode_page(char *html) {sprintf(html, "<h1>zvoice.jerry</h1>");  // 标题strcat(html, "<h2>");                     // 子标题开始#if ENABLE_RBTREE// 遍历红黑树,拼接统计数据ngx_rbtree_count_traversal(&ngx_pv_rbtree, ngx_pv_rbtree.root, html);
#else// 遍历数组pv_table,拼接统计数据for (int i = 0; i < 256; i++) {if (pv_table[i].count != 0) {  // 仅处理有计数的条目char str[INET_ADDRSTRLEN] = {0};char buffer[128] = {0};snprintf(buffer, 128, "req from : %s, count : %d <br/>",inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)), pv_table[i].count);strcat(html, buffer);}}
#endifstrcat(html, "</h2>");  // 子标题结束return 0;
}
  • 功能:生成包含统计结果的 HTML 字符串。根据ENABLE_RBTREE选择遍历红黑树或数组。
  • 注意strcat可能存在缓冲区溢出风险(HTML 缓冲区固定为 1024 字节)。

7. 请求处理核心函数

ngx_int_t ngx_http_handler_count_handler(ngx_http_request_t *r) {u_char html[1024] = {0};  // HTML响应缓冲区(固定1024字节)int len = sizeof(html);// 获取客户端IP地址(IPv4)struct sockaddr_in *client_addr = (struct sockaddr_in *)r->connection->sockaddr;#if ENABLE_RBTREE// 红黑树模式:查找或插入IP统计ngx_rbtree_key_t key = (ngx_rbtree_key_t)client_addr->sin_addr.s_addr;  // IP地址作为键(32位无符号整数)ngx_rbtree_node_t *node = ngx_rbtree_count_search(&ngx_pv_rbtree, key);  // 搜索是否存在该IPif (!node) {  // 未找到,插入新节点node = ngx_pcalloc(r->pool, sizeof(ngx_rbtree_node_t));  // 从Nginx内存池分配节点node->key = key;node->data = 1;  // 初始计数为1ngx_rbtree_insert(&ngx_pv_rbtree, node);  // 插入红黑树} else {  // 已存在,计数+1node->data++;}#else// 数组模式:通过IP最后一个字节作为索引(可能冲突)int idx = client_addr->sin_addr.s_addr >> 24;  // 右移24位,取最后一个字节(0-255)pv_table[idx].count++;                         // 计数+1memcpy(&pv_table[idx].addr, &client_addr->sin_addr, sizeof(client_addr->sin_addr));  // 存储IP地址
#endifngx_http_encode_page((char *)html);  // 生成HTML// 设置HTTP响应头r->headers_out.status = NGX_HTTP_OK;  // 状态码200ngx_str_set(&r->headers_out.content_type, "text/html");  // 内容类型为HTML// 发送响应头if (ngx_http_send_header(r) == NGX_ERROR) {return NGX_ERROR;}// 构造响应体(使用Nginx缓冲区链)ngx_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);
}
  • 功能:处理每个 HTTP 请求,统计客户端 IP 的请求次数,生成并返回 HTML 响应。
  • 关键逻辑
    • 获取客户端 IP:通过r->connection->sockaddr获取客户端地址(IPv4)。
    • 统计逻辑
      • 红黑树模式:以 IP 地址的 32 位整数作为键,搜索或插入节点,更新计数。
      • 数组模式:取 IP 地址的最后一个字节(右移 24 位)作为索引,直接更新数组中的计数。
    • 响应构造:生成 HTML 后,设置 HTTP 状态码、内容类型,通过 Nginx 的缓冲区链发送响应头和体。

2. config:

ngx_addon_name=ngx_http_handler_count_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_handler_count_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_handler_count_module.c"

3. 具体操作步骤

##同前面

./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/jerry/snap/nginx/pcre-8.45 --with-zlib=/home/jerry/snap/nginx/zlib-1.2.13 --with-openssl=/home/jerry/snap/nginx/openssl-1.1.1s --add-module=/usr/local/nginx/ngx_code_test/ngx_http_handler_count_module
  1. 使用的nginx.conf中,加上count选项:

        2. 不同ip地址请求时的区别:

 0voice · GitHub 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/81609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Odoo】Pycharm导入运行Odoo15

【Odoo】Pycharm导入运行Odoo15 前置准备1. Odoo-15项目下载解压2. PsrtgreSQL数据库 项目导入运行1. 项目导入2. 设置项目内虚拟环境3. 下载项目中依赖4. 修改配置文件odoo.conf 运行Pycharm快捷运行 前置准备 1. Odoo-15项目下载解压 将下载好的项目解压到开发目录下 2. …

网络安全-等级保护(等保) 2-5 GB/T 25070—2019《信息安全技术 网络安全等级保护安全设计技术要求》-2019-05-10发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…

【SpringBoot】✈️整合飞书群机器人发送消息

&#x1f4a5;&#x1f4a5;✈️✈️欢迎阅读本文章❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;本篇文章阅读大约耗时3分钟。 ⛳️motto&#xff1a;不积跬步、无以千里 &#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;&#x1f381;&#x1f381;&am…

我的多条件查询

背景&#xff1a;2个表&#xff0c;是一对多的关系&#xff0c;一个实时视频帧可以出现多个检测结果 要求&#xff0c;可以根据&#xff0c;ids&#xff0c;起始时间&#xff0c;识别出的鸟的种类&#xff0c;来进行删除。 出现的问题&#xff0c; 一致性没有实现&#xff1a…

关于网站提交搜索引擎

发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由&#xff1a; 提高可见性&#xff1a;通过将您的网站提交给搜索引擎&#xff0c;可以提高您的网站在搜索结果中出现的机会。当用…

【Oracle专栏】扩容导致数据文件 dbf 丢失,实操

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 同事检查扩容情况,发现客户扩容后数据盘后,盘中原有文件丢失,再检查发现数据库没有启动。通过检查发现数据盘中丢失的是oracle的 dbf 表空间文件。数据库无法启动。 检查情况:1)没有rman备份 …

负载均衡—会话保持技术详解

一、会话保持的定义 会话保持&#xff08;Session Persistence&#xff09;是一种负载均衡策略&#xff0c;其核心机制是确保来自同一客户端的连续请求&#xff0c;在特定周期内被定向到同一台后端服务器进行处理。这种机制通过记录和识别客户端的特定标识信息&#xff0c;打破…

CSRF攻击 + 观测iframe加载时间利用时间响应差异侧信道攻击 -- reelfreaks DefCamp 2024

参考: https://0x90r00t.com/2024/09/30/3708/ 题目信息 有些事情最好还是保持低调。当然&#xff0c;除非你是个真正的怪胎。 注意&#xff1a;该网站通过HTTPS提供服务 标志格式&#xff1a;DCTF&#xff5b;&#xff5d;题目实现了一个类似视频网站的东西 在其提供的数据库中…

JS逆向-某易云音乐下载器

文章目录 介绍下载链接Robots文件搜索功能JS逆向**函数a&#xff1a;生成随机字符串****函数b&#xff1a;AES-CBC加密****函数c&#xff1a;RSA公钥加密** 歌曲下载总结 介绍 在某易云音乐中&#xff0c;很多歌曲听是免费的&#xff0c;但下载需要VIP&#xff0c;此程序旨在“…

黑马k8s(十)

1.Pod生命周期-钩子函数 2.Pod生命周期-容器探测 因为没有hello.txt文件 查看详情&#xff1a; 修改为查看命令&#xff1a; 查看一下详情&#xff1a; 因为只有一个80端口&#xff0c;没有8080&#xff0c;所以会重启 查看详情&#xff1a; 修改成80&#xff1a; 因为没有…

每日算法刷题Day9 5.17:leetcode定长滑动窗口3道题,用时1h

9. 1652.拆炸弹(简单&#xff0c;学习) 1652. 拆炸弹 - 力扣&#xff08;LeetCode&#xff09; 思想 为了获得正确的密码&#xff0c;你需要替换掉每一个数字。所有数字会 同时 被替换。 如果 k > 0 &#xff0c;将第 i 个数字用 接下来 k 个数字之和替换。如果 k < 0…

Java IO及Netty框架学习小结

Netty netty官网: Netty 什么是Netty&#xff1f; Netty 是 一个异步事件驱动的网络应用程序框架&#xff0c;用于快速开发可维护的高性能协议服务器和客户端。Netty 是一个 NIO 客户端服务器框架&#xff0c;可以快速轻松地开发网络应用程序&#xff08;例如协议服务器和客…

计算机网络笔记(二十七)——4.9多协议标签交换MPLS

4.9.1MPLS的工作原理 一、MPLS基本工作原理 MPLS&#xff08;Multiprotocol Label Switching&#xff09;是一种介于数据链路层和网络层之间的转发技术&#xff0c;通过固定长度的标签进行高速数据转发。其核心特点是通过预建立的标签交换路径&#xff08;Label Switching Pa…

AI 赋能 Copula 建模:大语言模型驱动的相关性分析革新

技术点目录 R及Python语言及相关性研究初步二元Copula理论与实践&#xff08;一&#xff09;二元Copula理论与实践&#xff08;二&#xff09;【R语言为主】Copula函数的统计检验与选择【R语言为主】高维数据与Vine Copula 【R语言】正则Vine Copula&#xff08;一&#xff09;…

【洛谷P3386】二分图最大匹配之Kuhn算法/匈牙利算法:直观理解

题目&#xff1a;洛谷P3386 【模板】二分图最大匹配 &#x1f955; 匈牙利算法本来是针对带权图最大匹配的&#xff0c;这里由于题目只是求最大匹配的边数&#xff0c;所以我们也只考虑无权的情况。 &#x1f680; 本文旨在服务于看了别的关于匈牙利算法的文章但不甚理解的童…

【数据结构】二分查找(返回插入点)5.14

二分查找基础版 package 二分查找; public class BinarySearch { public static void main(String[] args) { // TODO Auto-generated method stub } public static int binarySearchBasic(int[] a,int target) { int i0,ja.length-1; //设置指针初值 while…

Ubuntu 命令

Ubuntu 命令速查表​ ​分类​​命令​​功能描述​​示例/常用选项​​​​文件与目录​ls列出目录内容ls -a&#xff08;显示隐藏文件&#xff09;; ls -lh&#xff08;详细列表易读大小&#xff09; cd切换目录cd ~&#xff08;主目录&#xff09;; cd ..&#xff08;上级…

Java集合框架详解与使用场景示例

Java集合框架是Java标准库中一组用于存储和操作数据的接口和类。它提供了多种数据结构&#xff0c;每种数据结构都有其特定的用途和性能特点。在本文中&#xff0c;我们将详细介绍Java集合框架的主要组成部分&#xff1a;List、Set和Queue&#xff0c;并通过代码示例展示它们的…

《Python星球日记》 第78天:CV 基础与图像处理

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、计算机视觉(CV)简介1. 什么是计算机视觉?2. 计算机视觉的应用场景3. 图像的基本属性a》像素(Pixel)b》通道(Channel)c》分辨率(Res…

LabVIEW在电子电工教学中的应用

在电子电工教学领域&#xff0c;传统教学模式面临诸多挑战&#xff0c;如实验设备数量有限、实验过程存在安全隐患、教学内容更新滞后等。LabVIEW 作为一款功能强大的图形化编程软件&#xff0c;为解决这些问题提供了创新思路&#xff0c;在电子电工教学的多个关键环节发挥着重…