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.pswp.cn/bicheng/81609.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81609.shtml
英文地址,請注明出處:http://en.pswp.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;在電子電工教學的多個關鍵環節發揮著重…