最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Nginx中間件入門指南

 更新時間:2025年10月23日 09:54:32   作者:jc0620  
Nginx也是一個web服務器,能夠支持正向代理(代理用戶去訪問服務器)、反向代理(代理服務器來讓用戶訪問)、負載均衡(將用戶的訪問分發(fā)給服務器集群),這篇文章主要介紹了Nginx中間件入門指南,需要的朋友可以參考下

Nginx (engine x) 是一個高性能的HTTP和反向代理web服務器,同時也提供了IMAP/POP3/SMTP服務。Nginx是由伊戈爾·賽索耶夫為俄羅斯訪問量第二的Rambler.ru站點(俄文:Рамблер)開發(fā)的,公開版本1.19.6發(fā)布于2020年12月15日。 
其將源代碼以類BSD許可證的形式發(fā)布,因它的穩(wěn)定性、豐富的功能集、簡單的配置文件和低系統(tǒng)資源的消耗而聞名。2022年01月25日,nginx 1.21.6發(fā)布。 
Nginx是一款輕量級的Web 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,在BSD-like 協(xié)議下發(fā)行。其特點是占有內(nèi)存少,并發(fā)能力強,事實上nginx的并發(fā)能力在同類型的網(wǎng)頁服務器中表現(xiàn)較好。

一、初識Nginx

Nginx是什么?

Nginx也是一個web服務器,能夠支持正向代理(代理用戶去訪問服務器)、反向代理(代理服務器來讓用戶訪問)、負載均衡(將用戶的訪問分發(fā)給服務器集群)。

conf的作用及讀取過程

Nginx的系統(tǒng)配置conf文件有什么作用?

用于定義 Nginx 的全局和特定站點的行為。

  • 有master進程監(jiān)聽到有用戶連接請求后,worker進程通過原子操作搶占連接,加入epoll監(jiān)聽,其中worker進程的數(shù)量在conf中確定。
  • http塊下多個的server塊,每個server塊代表一個虛擬主機,定義虛擬主機的設置,如監(jiān)聽端口、路由規(guī)則。
  • 在server塊中定義分發(fā)的服務器ip地址,實現(xiàn)負載均衡。
worker_processes 4;
events {
	worker_connections 1024;
}
http {
	upstream backend {
		server 192.168.159.130:9002 weight=2;//可以是任意的服務器地址  不一定是本機的
		server 192.168.159.130:9003 weight=2;
	}
	server {
		listen 9000;
		location / {//路由規(guī)則
            #root /home/king/share/nginx/html9000/;
			proxy_pass http://backend;   # ...轉(zhuǎn)發(fā)給 backend 服務器池處理
		}	
	}
	server {
		listen 9001;
		location / {
                        root /home/king/share/nginx/html9001/;# ...由本機文件系統(tǒng)處理
                }
	}
	server {
		listen 9002;
		location / {
                        root /home/king/share/nginx/html9002/;
                }
	}
	server {
		listen 9003;
		location / {
                        root /home/king/share/nginx/html9003/;
                }
	}
}

Nginx在代碼中對conf文件的處理是怎么樣的?

Nginx 主進程啟動,readline逐行讀取conf內(nèi)容。

例如:當讀取并解析到

worker_processes 1024;

這行配置時,在內(nèi)存中會發(fā)生以下事情:

  • 創(chuàng)建一塊內(nèi)存,并且并將其初始化為一個 ngx_core_conf_t 結(jié)構(gòu)體,用一個conf指針指向
  • 將conf指針傳入 ngx_set_worker_processes 函數(shù),這是讀到worker_processes后觸發(fā)的回調(diào)函數(shù)
  • 在函數(shù)內(nèi)部根據(jù)"1024",修改ngx_core_conf_t 結(jié)構(gòu)體中worker_processes 字段的值

HTTP狀態(tài)機

Nginx的狀態(tài)機是什么?

在Nginx源碼里有作者自定義的11個http狀態(tài),也有一份黑白名單,里面限制了某些ip、限制了某些資源的訪問、限制了某些用戶的訪問,這些限制也是存在于特定狀態(tài)下的。通過明確的階段劃分,Nginx 實現(xiàn)了處理流程的標準化和模塊化。

Nginx驚群

Nginx的驚群現(xiàn)象及應對方法是什么?

Nginx 的“驚群現(xiàn)象”是指:
在多進程(worker)模式下,多個進程同時等待某個事件(如新連接),當事件發(fā)生時,所有進程都被喚醒,但只有一個能成功 accept,其他進程再次休眠,造成資源浪費和性能下降。

應對方法:

在 worker 進程間引入 accept_mutex(接受互斥鎖)。只有獲得鎖的進程才會去 accept 新連接,其他進程等待,避免所有進程一起被喚醒。通過配置 conf中events模塊的accept_mutex on實現(xiàn)

二、Nginx組件拆出來使用

Nginx內(nèi)部實現(xiàn)了很多組件,在鏈接對應頭文件的基礎上,可以直接編寫代碼,調(diào)用其特定的組件比如:內(nèi)存池、線程池、原子操作、日志、數(shù)據(jù)結(jié)構(gòu)(ngx_str_t  ;ngx_array_t  ;ngx_list_t  ;ngx_rbtree_t)

下面是內(nèi)存池、string、日志模塊的拆分使用

#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"
#define unused(x)  x=x
volatile 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, ...) {    //日志模塊調(diào)用
	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 0
	ngx_str_t name = ngx_string("King");    //string數(shù)據(jù)結(jié)構(gòu)使用
	printf("name --> len: %ld, data: %s\n", name.len, name.data);
#elif 0
	ngx_pool_t *pool = ngx_create_pool(4096, NULL);    //ngx_pool_t   內(nèi)存池使用
	print_pool(pool);
	int *p1 = ngx_palloc(pool, sizeof(int));
	print_pool(pool);
	void *p2 = ngx_palloc(pool, 0x10);
	print_pool(pool);
	void *p3 = ngx_palloc(pool, 0x15);
	print_pool(pool);
	ngx_destroy_pool(pool);
#else
#endif
}

三、Nginx模塊開發(fā)

過濾器模塊

接收到后端的response,返回response給瀏覽器客戶端

任務:開發(fā)一個在 HTTP 響應的 HTML 內(nèi)容前插入一段固定的文本?(如作者信息和鏈接)的Nginx HTTP Filter 模塊,ngx_http_prefix_filter_module.c。

流程:在讀取到conf的"add_prefix"后,會執(zhí)行對應的set函數(shù),將對應的值保存到讀取到location模塊初始化創(chuàng)建的配置結(jié)構(gòu)體里面。當conf文件讀取結(jié)束,會觸發(fā)prefix過濾器初始化,將我們的prefix_filter加入到Nginx的Filter鏈表里面。當后端傳來了HTTP響應,就會觸發(fā)我們的header_topbody_top過濾器函數(shù),在header函數(shù)中里面檢查和標記請求,在body函數(shù)中加入對應的文本,然后傳入下一個過濾器。

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
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;
三個核心處理函數(shù)
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);
static ngx_str_t filter_prefix = ngx_string("<h2>Author : King</h2><p><a href=\"http://www.0voice.com\">0voice</a></p>");
static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {
#創(chuàng)建一個獨立的配置結(jié)構(gòu)體,并初始化默認值。
	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;
}
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;// 父配置(如 server 塊)
	ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;// 子配置(如 location 塊)
    // 合并規(guī)則:如果子配置未設置,則繼承父配置的值
	ngx_conf_merge_value(conf->enable, prev->enable, 0);
	return NGX_CONF_OK;
}
static ngx_command_t ngx_http_prefix_filter_commands[] = {
#conf中可用的指令
	{
		ngx_string("add_prefix"), # 指令名
		NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,#指令可能出現(xiàn)的在conf中的部分
		ngx_conf_set_flag_slot,    #遇到命令的時候執(zhí)行這個set函數(shù)
		NGX_HTTP_LOC_CONF_OFFSET,    #讀取到conf中add_prefix值后,存儲的位置(location 級別)
		offsetof(ngx_http_prefix_filter_conf_t, enable),    #具體存儲在結(jié)構(gòu)體的哪個位置
		NULL
	},
	ngx_null_command //結(jié)尾標識
};
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {
	NULL,
	ngx_http_prefix_filter_init,    #讀完最后一行conf時執(zhí)行此回調(diào)函數(shù)
	NULL,    #解析到主模塊 執(zhí)行此回調(diào)函數(shù)
	NULL,     // 初始化主配置
	NULL,    #解析到server塊 執(zhí)行此回調(diào)函數(shù)
	NULL,     // server配置繼承
	ngx_http_prefix_filter_create_conf,    #解析到location塊 執(zhí)行此回調(diào)函數(shù) 會創(chuàng)建一個獨立的配置結(jié)構(gòu)體,并初始化默認值
	ngx_http_prefix_filter_merge_conf    //location配置繼承
};
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;
Nginx 處理 HTTP 響應時,會依次調(diào)用兩類 Filter:?Header Filter?:處理響應頭;?Body Filter?:處理響應體(如修改 HTML 內(nèi)容)。這些 Filter 以鏈表形式組織,下面是頭插法插入我們的Header 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;
}
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);
	}
    // 為需要加入的前綴  分配空間,隨后把這個指針和當前的HTTP請求綁定
	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);
    // 僅對 text/html 響應插入前綴
	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;// 修正 Content-Length
		}
	}
	return ngx_http_prefix_filter_header_filter(r);
}
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
#插入前綴內(nèi)容
	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;
    # 創(chuàng)建包含前綴內(nèi)容的緩沖區(qū),存放預設的前綴文本(filter_prefix)
	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;
    # 將前綴緩沖區(qū)插入到響應體鏈表的頭部
	ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
	cl->buf = b;
	cl->next = in;
	return ngx_http_next_body_filter(r, cl);
}

編寫ngx_http_prefix_filter_module.c后,還需要在同一目錄下編寫config文件,讓Nginx識別到這個模塊。

#模塊的名字
ngx_addon_name=ngx_http_prefix_filter_module
#Makefile中的關鍵字  在其后面加上我們的庫
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"

handler模塊

接收瀏覽器客戶端請求,直接返回請求給瀏覽器客戶端

任務:統(tǒng)計頁面訪問次數(shù),可以直接在Nginx上通過handler模塊計數(shù),因為所有的數(shù)據(jù)都會經(jīng)過Nginx,可以統(tǒng)計后直接返回給客戶端。

流程:在conf文件中讀取到關鍵詞"count",執(zhí)行對應的set函數(shù),每執(zhí)行一次,都會設置一個handler函數(shù),還會創(chuàng)建一個共享內(nèi)存區(qū),并且指定對應的回調(diào)init函數(shù)?;卣{(diào)init函數(shù)會在Nginx啟動和reload的時候執(zhí)行。handler函數(shù)的執(zhí)行時機是當客戶端請求發(fā)送來的時候,每來一個請求,都會執(zhí)行handler函數(shù)。在handler函數(shù)里面,就能拿到請求方的ip地址,根據(jù)ip的最后一位1-255,search查找ngx_rbtree是否有相同節(jié)點,決定是否插入。通過encode_page函數(shù)組織html網(wǎng)頁,遍歷ngx_rbtree獲取總訪問次數(shù),將內(nèi)容都寫入。最后組織一個header和body,返回給客戶端。

亮點:采用ngx_rbtree增強健壯性,增加slab共享內(nèi)存,解決多進程間通信的問題。所有 worker 進程都能訪問同一份計數(shù)數(shù)據(jù),通過slab自帶的鎖依次通過lookup函數(shù)遍歷紅黑樹

代碼

#include <ngx_http.h>
#include <ngx_config.h>
#include <ngx_core.h>
/*
#include <arpa/inet.h>
#include <netinet/in.h>
*/
#define ENABLE_RBTREE	1
#讀到關鍵字的時候調(diào)用 用于指定handler函數(shù) 和 創(chuàng)建共享內(nèi)存區(qū)并指定其初始化回調(diào)函數(shù)
static char *ngx_http_pagecount_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
#有請求的時候調(diào)用,加鎖統(tǒng)計當前客戶端 IP 的訪問次數(shù),并生成統(tǒng)計頁面返回給用戶
static ngx_int_t ngx_http_pagecount_handler(ngx_http_request_t *r);
#讀到location模塊調(diào)用,為每個 location 創(chuàng)建并初始化配置結(jié)構(gòu)體
static void  *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf);
#開啟/reload Nginx時候調(diào)用,初始化共享內(nèi)存區(qū)的回調(diào)函數(shù)
static ngx_int_t ngx_http_pagecount_shm_init (ngx_shm_zone_t *zone, void *data);
#在handler中的lookup函數(shù)中調(diào)用,自定義紅黑樹的插入規(guī)則
static void ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,
        ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
#在handler中調(diào)用,遍歷紅黑樹統(tǒng)計數(shù)量
static int ngx_encode_http_page_rb(ngx_http_pagecount_conf_t *conf, char *html);
#在handler中調(diào)用,對于一個新key(ip)的處理
static ngx_int_t ngx_http_pagecount_lookup(ngx_http_request_t *r, ngx_http_pagecount_conf_t *conf, ngx_uint_t key);
static ngx_command_t count_commands[] = {
	{//告訴Nginx  conf中的count是關鍵字,并且指定其處理函數(shù)set
		ngx_string("count"),
		NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,    //指令可以出現(xiàn)的位置
		ngx_http_pagecount_set,         //讀到conf中的count指令時調(diào)用
		NGX_HTTP_LOC_CONF_OFFSET,   
		0, NULL
	},
	ngx_null_command
};
static ngx_http_module_t count_ctx = {
	NULL,
	NULL,    
    //沒用上了 因為handler和共享內(nèi)存池(包括紅黑樹)都在set函數(shù)里創(chuàng)建并且綁定回調(diào)了,Nginx啟動的時候會自動調(diào)用回調(diào)
	NULL,
	NULL,
	NULL,
	NULL,
	ngx_http_pagecount_create_location_conf,	//讀到location配置時調(diào)用,確保每個 location 都有獨立的配置空間
	NULL,
};
//ngx_http_count_module   最先編寫的內(nèi)容
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; 
} ngx_http_pagecount_node_t;
typedef struct {	//共享內(nèi)存中的 “數(shù)據(jù)區(qū)”紅黑樹
	ngx_rbtree_t rbtree;
	ngx_rbtree_node_t sentinel;
} ngx_http_pagecount_shm_t;
typedef struct	//location 級別的配置結(jié)構(gòu)體
{
    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) {
//zone:Nginx 共享內(nèi)存區(qū)對象,包含分配好的內(nèi)存地址等信息。
//data:上一次初始化時的配置數(shù)據(jù)
	ngx_http_pagecount_conf_t *conf;
	ngx_http_pagecount_conf_t *oconf = data;
	conf = (ngx_http_pagecount_conf_t*)zone->data;
	if (oconf) {//已經(jīng)初始化過了  復用紅黑樹和 slab 內(nèi)存池指針
		conf->sh = oconf->sh;
		conf->shpool = oconf->shpool;
		return NGX_OK;
	}
	//沒初始化過  新建紅黑樹和 slab 內(nèi)存池
	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;
	}
    //綁定紅黑樹  到Nginx內(nèi)置new slab 內(nèi)存池
	conf->shpool->data = conf->sh;
	//初始化紅黑樹
	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;
    //創(chuàng)建Nginx內(nèi)置的配置結(jié)構(gòu)體corecf
	ngx_http_core_loc_conf_t *corecf;
	mconf->shmsize = 1024*1024;
    //創(chuàng)建一個名為 pagecount_slab_shm 的共享內(nèi)存區(qū)(通過ngx內(nèi)置的共享內(nèi)存管理函數(shù)  )
	shm_zone = ngx_shared_memory_add(cf, &name, mconf->shmsize, &ngx_http_pagecount_module);
	if (NULL == shm_zone) {
		return NGX_CONF_ERROR;
	}
	//把創(chuàng)建的共享內(nèi)存區(qū)初始化
	shm_zone->init = ngx_http_pagecount_shm_init;
	shm_zone->data = mconf;
    //獲取Nginx內(nèi)置的配置空間,在里面設置handler,讓Nginx知道遇到HTTP請求時調(diào)用我們寫的處理函數(shù)
	corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	corecf->handler = ngx_http_pagecount_handler;
	return NGX_CONF_OK;
}
void  *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf) {
//為每個 location 創(chuàng)建并初始化配置結(jié)構(gòu)體
	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;
	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)
{
//自定義紅黑樹的插入規(guī)則,據(jù)節(jié)點的 key(IP 地址的數(shù)值)決定插入到樹的哪個位
   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);
}
static ngx_int_t ngx_http_pagecount_lookup(ngx_http_request_t *r, ngx_http_pagecount_conf_t *conf, ngx_uint_t key) {
//在紅黑樹中查找指定 key(IP),如果找到則計數(shù)加一;如果沒找到則插入新節(jié)點并初始化計數(shù)為 1
	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 == node
			node->data ++;
			return NGX_OK;
		}
	}
	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 222 --> %x\n", key);
	// insert rbtree
	node = 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) {
//遍歷紅黑樹,將所有 IP 及其訪問次數(shù)以 HTML 格式輸出到字符串 html,用于頁面展示
	sprintf(html, "<h1>67777 </h1>");
	strcat(html, "<h2>");
	ngx_rbtree_node_t *node = ngx_rbtree_min(conf->sh->rbtree.root, conf->sh->rbtree.sentinel);
	do {//遍歷紅黑樹節(jié)點
		char str[INET_ADDRSTRLEN] = {0};
		char buffer[128] = {0};
		sprintf(buffer, "req from : %s, count: %d 
",
		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) {
	// HTTP 請求的處理函數(shù),統(tǒng)計當前客戶端 IP 的訪問次數(shù),并生成統(tǒng)計頁面返回給用戶
	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);
    //先對共享內(nèi)存池加鎖,保證多進程/多線程安全(共享內(nèi)存池自帶的鎖)
	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);
	//header
	r->headers_out.status = 200;
	ngx_str_set(&r->headers_out.content_type, "text/html");
	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);
}

upstream模塊

ginx 的 upstream 模塊用于實現(xiàn)反向代理和負載均衡??梢酝ㄟ^配置 upstream 塊,將請求分發(fā)到多個后端服務器。對于upstream模塊,直接用 Nginx conf配置實現(xiàn) 自己的upstream,比如:

http {
    //定義了一個后端服務器組,包含兩個服務器
	upstream backend {
		server 192.168.159.130:9002 weight=2;//可以是任意的服務器地址  不一定是本機的
		server 192.168.159.130:9003 weight=2;
	}
	server {
		listen 9000;
		location / {//路由規(guī)則
            #root /home/king/share/nginx/html9000/;
			proxy_pass http://backend;   # ...轉(zhuǎn)發(fā)給 backend 服務器池處理
	}	
}

到此這篇關于Nginx中間件的文章就介紹到這了,更多相關Nginx中間件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

根河市| 新郑市| 敦煌市| 独山县| 湘阴县| 登封市| 崇左市| 洛浦县| 德化县| 平江县| 汤阴县| 石门县| 南部县| 遵义县| 珠海市| 广昌县| 井研县| 县级市| 天柱县| 清远市| 宝应县| 榆林市| 内黄县| 青川县| 嘉义县| 和田市| 灯塔市| 孝义市| 梁山县| 永登县| 奉化市| 留坝县| 微博| 犍为县| 通化市| 西充县| 财经| 拉孜县| 长泰县| 荆门市| 巴林右旗|