雖然代碼理解起來比較混亂,但是使用還是比較簡單的,常用的有創(chuàng)建 hash 和在 hash 中進行查找兩個操作,對于創(chuàng)建hash的操作,過程一般為:
- 構(gòu)造一個 ngx_hash_key_t 為成員的數(shù)組, 包含 key, value 和 使用key計算出的一個hash值
- 構(gòu)建一個 ngx_hash_init_t結(jié)構(gòu)體的變量, 其中包含了ngx_hash_t 的成員, 為hash的結(jié)構(gòu)體, 還包括一些其他初始設(shè)置,如bucket的大小,內(nèi)存池等
- 調(diào)用 ngx_hash_init 傳入 ngx_hash_init_t 結(jié)構(gòu), ngx_hash_key_t 的數(shù)組,和數(shù)組的長度, 進行初始化,這樣 ngx_hash_init_t的hash成員就是我們要的hash結(jié)構(gòu)
查找的過程很簡單
- 計算 key 的hash值
- 使用 ngx_hash_find 進行查找,需要同時傳入 hash值和key ,返回的就是value的指針
?
需要注意的是,nginx 的 hash 在查找時使用的是分桶后線性查找法,因此當分桶數(shù)確定時查找效率同其中的總 key-val 對數(shù)量成反比。
下面是一些demo代碼(可以從svn中找到)
#include
<stdio.h>
#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"
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
,
...)
{
}
static
ngx_str_t names
[]
=
{
ngx_string
(
"rainx"
),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ngx_string
(
"xiaozhe"
),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ngx_string
(
"zhoujian"
)};
static
char
*
descs
[]
=
{
"rainx's id is 1"
,
"xiaozhe's id is 2"
,
"zhoujian's id is 3"
};
// hash table的一些基本操作
int
main
()
{
? ? ngx_uint_t ? ? ? ? ?k
;
//, p, h;
? ? ngx_pool_t
*
? ? ? ? pool
;
? ? ngx_hash_init_t ? ? hash_init
;
? ? ngx_hash_t
*
? ? ? ? hash
;
? ? ngx_array_t
*
? ? ? ?elements
;
? ? ngx_hash_key_t
*
? ? arr_node
;
? ?
char
*
? ? ? ? ? ? ? find
;
? ?
int
? ? ? ? ? ? ? ? i
;
? ? ngx_cacheline_size
=
32
;
? ?
// hash key cal start
? ? ngx_str_t str ? ? ?
=
ngx_string
(
"hello, world"
);
? ? k ? ? ? ? ? ? ? ? ?
=
ngx_hash_key_lc
(
str
.
data
,
str
.
len
);
? ? pool ? ? ? ? ? ? ? ?
=
ngx_create_pool
(
1024
*
10
,
NULL
);
? ? printf
(
"caculated key is %u \n"
,
k
);
? ?
// hask key cal end
? ?
//
? ? hash
=
(
ngx_hash_t
*)
ngx_pcalloc
(
pool
,
sizeof
(
hash
));
? ? hash_init
.
hash ? ? ?
=
hash
;
? ? ? ? ? ? ? ? ? ? ?
// hash結(jié)構(gòu)
? ? hash_init
.
key ? ? ?
=
&
ngx_hash_key_lc
;
? ? ? ? ?
// hash算法函數(shù)
? ? hash_init
.
max_size ?
=
1024
*
10
;
? ? ? ? ? ? ? ? ?
// max_size
? ? hash_init
.
bucket_size
=
64
;
// ngx_align(64, ngx_cacheline_size);
? ? hash_init
.
name ? ? ?
=
"yahoo_guy_hash"
;
? ? ? ? ?
// 在log里會用到
? ? hash_init
.
pool ? ? ? ? ?
=
pool
;
? ? ? ? ? ? ? ?
// 內(nèi)存池
? ? hash_init
.
temp_pool ? ? ?
=
NULL
;
? ?
// 創(chuàng)建數(shù)組
? ? elements
=
ngx_array_create
(
pool
,
32
,
sizeof
(
ngx_hash_key_t
));
? ?
for
(
i
=
0
;
i
<
3
;
i
++)
{
? ? ? ? arr_node ? ? ? ? ? ?
=
(
ngx_hash_key_t
*)
ngx_array_push
(
elements
);
? ? ? ? arr_node
->
key ? ? ?
=
(
names
[
i
]);
? ? ? ? arr_node
->
key_hash ?
=
ngx_hash_key_lc
(
arr_node
->
key
.
data
,
arr_node
->
key
.
len
);
? ? ? ? arr_node
->
value ? ?
=
(
void
*)
descs
[
i
];
? ? ? ?
//
? ? ? ? printf
(
"key: %s , key_hash: %u\n"
,
arr_node
->
key
.
data
,
arr_node
->
key_hash
);
? ?
}
? ?
if
(
ngx_hash_init
(&
hash_init
,
(
ngx_hash_key_t
*)
elements
->
elts
,
elements
->
nelts
)!=
NGX_OK
){
? ? ? ?
return
1
;
? ?
}
? ?
// 查找
? ? k ? ?
=
ngx_hash_key_lc
(
names
[
0
].
data
,
names
[
0
].
len
);
? ? printf
(
"%s key is %d\n"
,
names
[
0
].
data
,
k
);
? ? find
=
(
char
*)
? ? ? ? ngx_hash_find
(
hash
,
k
,
(
u_char
*)
names
[
0
].
data
,
names
[
0
].
len
);
? ?
if
(
find
)
{
? ? ? ? printf
(
"get desc of rainx: %s\n"
,
(
char
*)
find
);
? ?
}
? ? ngx_array_destroy
(
elements
);
? ? ngx_destroy_pool
(
pool
);
? ?
return
0
;
}
運行結(jié)果
rainx@rainx
-
laptop
:~/
land
/
nginxsrp
/
src
/
demo
/
basic_types$
./
hash_op
caculated key
is
3654358412
key
:
rainx
,
key_hash
:
108275556
key
:
xiaozhe
,
key_hash
:
2225329080
key
:
zhoujian
,
key_hash
:
3269715264
rainx key
is
108275556
get
desc of rainx
:
rainx
's id is 1
ngx_list
ngx_list 的結(jié)構(gòu)并不復(fù)雜,ngx為我們封裝了ngx_list_create, ngx_list_init, 和 ngx_list_push等(建立,初始化,添加)操作, 但是對于我們來說最常用的是遍歷操作, 下面是nginx的注釋里面提到的遍歷的例子
? ?part
=
&
list
.
part
;
? ?data
=
part
->
elts
;
?
? ?
for
(
i
=
0
;;
i
++)
{
?
? ? ? ?
if
(
i
>=
part
->
nelts
)
{
? ? ? ? ? ?
if
(
part
->
next
==
NULL
)
{
? ? ? ? ? ? ? ?
break
;
? ? ? ? ? ?
}
?
? ? ? ? ? ?part
=
part
->
next
;
? ? ? ? ? ?data
=
part
->
elts
;
? ? ? ? ? ?i
=
0
;
? ? ? ?
}
?
? ? ? ?
...
?data
[
i
]
...
?
? ?
}
了解nginx的core module 的結(jié)構(gòu)和運行機制
參考資料
在開始這個task的學(xué)習(xí)的時候,經(jīng)過搜索發(fā)現(xiàn)了langwan同學(xué)之前對nginx的源代碼研究資料,很有參考意義,所以大量節(jié)省了我們的工作,我覺得對于本章的進行比較有用的是,下面這幾個文章
- nginx源代碼分析? http://hi.baidu.com/langwan/blog/item/6b18ef24cd859e064c088d28.html
- nginx 緩沖區(qū)構(gòu)造? http://hi.baidu.com/langwan/blog/item/822b758d5d1d9a1ab31bbaf8.html
- Nginx源代碼分析 - 日志處理? http://hi.baidu.com/langwan/blog/item/7e7db51978e04e4d43a9ad32.html
Debug信息的輸出
為了方便研究,將nginx的debug 信息打開,重新編譯
rainx@rainx
-
laptop
:~/
land
/
nginx
-
0.7
.
61
$
./
configure
--
prefix
=
/home/
rainx
/
land
/
test
--
with
-
debug
然后修改nginx.conf
worker_processes ?
2
;
error_log ?logs
/
error
.
log ?debug
;
打開debug信息的支持,并使用2個worker進程,通過查看 log 信息來了解 nginx 運行的情況
基于上面的配置信息,結(jié)合一個簡單的http訪問操作,我這里記錄了一個? log日志的例子
ngx_init_cycle
其中一個比較重要的函數(shù)調(diào)用是, ngx_init_cycle, 這個是使用kscope輸出的他的調(diào)用關(guān)系,他被main, ngx_master_process_cycle,ngx_single_process_cycle 調(diào)用, 其中后兩者是在reconfigure的時候被調(diào)用的
他主要做了如下幾件事情:
初始化
cycle
是基于舊有的
cycle
進行的,比如這里的
init_cycle
,會繼承
old cycle
的很多屬性,
比如
log
等,
但是同時會對很多資源重新分配,比如
pool
,
shared mem
,
file handler
,
listening socket
等,同時清除舊有的
cycle
的資源
另外,ngx_master/single_process_cycle 里面會對init_process進行調(diào)用, 并且循環(huán)調(diào)用 ngx_process_events_and_timers , 其中里面會調(diào)用ngx_process_events(cycle, timer, flags); 對事件循環(huán)進行polliing 時間一般默認為 500 ms
了解nginx的http core module 的結(jié)構(gòu)和運行機制
HTTP相關(guān)的Module都在 src/http 目錄和其子目錄下, 其中 src/http 下的文件為http模塊的核心文件, src/http/modules 下的文件為http模塊的擴展模塊。
其中:
ngx_http.[c|h]
ngx_http.c 中,注冊了 http 這個指令的處理模塊,對應(yīng)ngx_http_block函數(shù)
static
ngx_command_t ?ngx_http_commands
[]
=
{
? ?
{
ngx_string
(
"http"
),
? ? ? NGX_MAIN_CONF
|
NGX_CONF_BLOCK
|
NGX_CONF_NOARGS
,
? ? ? ngx_http_block
,
? ? ?
0
,
? ? ?
0
,
? ? ? NULL
},
? ? ? ngx_null_command
};
這個函數(shù)里面會進行一些conf資源分配/Merge,配置文件解析等工作。 這里面有個一比較重要的工作是注冊了nginx http 的 phase handler
? ?
if
(
ngx_http_init_phase_handlers
(
cf
,
cmcf
)
!=
NGX_OK
)
{
? ? ? ?
return
NGX_CONF_ERROR
;
? ?
}
phase handler的類型在 ngx_http_core_module 這里定義:
typedef
enum
{
? ? NGX_HTTP_POST_READ_PHASE
=
0
,
? ? NGX_HTTP_SERVER_REWRITE_PHASE
,
? ? NGX_HTTP_FIND_CONFIG_PHASE
,
? ? NGX_HTTP_REWRITE_PHASE
,
? ? NGX_HTTP_POST_REWRITE_PHASE
,
? ? NGX_HTTP_PREACCESS_PHASE
,
? ? NGX_HTTP_ACCESS_PHASE
,
? ? NGX_HTTP_POST_ACCESS_PHASE
,
? ? NGX_HTTP_TRY_FILES_PHASE
,
? ? NGX_HTTP_CONTENT_PHASE
,
? ? NGX_HTTP_LOG_PHASE
}
ngx_http_phases
;
每一個phase的handlers 都是一個數(shù)組,里面可以包含多個元素,通過 ngx_array_push 添加新的handler
其中每個phase的處理大都包含了對ngx_request_t 的 write 或者 read event的改寫,其中
在 ngx_http_core_content_phase 里面, 有對location handler的調(diào)用, 其中的 r->content_handler 就是運行時刻從location handler中注冊的,
? ?
if
(
r
->
content_handler
)
{
? ? ? ? r
->
write_event_handler
=
ngx_http_request_empty_handler
;
? ? ? ? ngx_http_finalize_request
(
r
,
r
->
content_handler
(
r
));
/*實際的請求發(fā)送處理*/
? ? ? ?
return
NGX_OK
;
? ?
}
其中, 在各個phase的結(jié)束階段,一般都是調(diào)用
? ? r
->
phase_handler
++;
? ?
return
NGX_AGAIN
;
移動request 中 phase_handler的指針,并且示意主程序繼續(xù)進行。
這里,無論是phase handler,還是 location handler,我們都是可以在程序里進行注冊的。
另外, ngx_http_block 里面調(diào)用了 ngx_http_optimize_servers ,這個函數(shù)對listening和connection相關(guān)的變量進行了初始化和調(diào)優(yōu),并最終在 ngx_http_add_listening (被ngx_http_add_listening調(diào)用) 中注冊了listening 的 handler 為 ngx_http_init_connection
? ? ls
->
handler
=
ngx_http_init_connection
;
ngx_http_init_connection 在 ngx_http_request.c中定義,后續(xù)會進行詳細的介紹
ngx_http_request.[c|h]
這里面,ngx_http_init_connection 注冊了connection事件的讀操作的回叫函數(shù), 并將寫操作設(shè)置為空函數(shù)
? ? rev
=
c
->
read
;
? ? rev
->
handler
=
ngx_http_init_request
;
? ? c
->
write
->
handler
=
ngx_http_empty_handler
;
當新的連接進入的時候,就執(zhí)行到 ngx_http_init_request, 開始對后面的流程進行處理,主要是將rev的handler 設(shè)置為ngx_http_process_request_line , 然后ngx_http_process_request_line 會先后有調(diào)度到 ngx_http_process_request_headers 和 ngx_http_process_request 函數(shù)對讀取過來的event進行處理,其中, ngx_http_process_request_headers 里面會對http的請求頭進行解析,ngx_http_process_request 設(shè)置event handler 到ngx_http_request_handler ,ngx_http_request_handler 中會根據(jù)事件可能是讀取還是寫入的操作分別調(diào)用 request 的 read_event_handler 和 write_event_handler ,所以后續(xù)程序?qū)?request 的 read/write event_handler 調(diào)整 本質(zhì)上類似對 rev 和 wev的handler的調(diào)整,只是回叫函數(shù)的參數(shù)變更為了 ngx_request_t 而不是之前的ngx_event_t
? ? c
->
read
->
handler
=
ngx_http_request_handler
;
? ? c
->
write
->
handler
=
ngx_http_request_handler
;
? ? r
->
read_event_handler
=
ngx_http_block_reading
;
根據(jù)上面代碼可以看出, 模塊開始使用 ngx_http_block_reading 這個handler對后續(xù)的讀請求進行處理
在注冊完事件后, ngx_http_process_request 會分別調(diào)用下面的兩個函數(shù)
? ? ngx_http_handler
(
r
);
? ? ngx_http_run_posted_requests
(
c
);
其中, ngx_http_handler 在ngx_http_core_module中定義,處理程序的主請求, ngx_http_run_posted_requests 在ngx_http_request.c 里定義,處理所有提交的子請求數(shù)據(jù)的輸出。
ngx_http_core_module.[c|h]
對于 ngx_http_core_module 是http 模塊中比較重要的模塊, 他本身是一個 NGX_HTTP_MODULE (不同于ngx_http_module, ngx_http_module本質(zhì)上是一個 NGX_CORE_MODULE
這里面對http block下面的一些指令進行了處理, 比如 server, location 等, 同時, 上面提到的 ngx_http_handler 也在這里面
ngx_http_handler 所作的最核心的工作就是在最后調(diào)用 并將 write event 設(shè)置為 ngx_http_core_run_phases, 開始依次處理各個階段的 handler
當handler處理完成后,http的處理流程也就基本上完成了..
? ?
while
(
ph
[
r
->
phase_handler
].
checker
)
{
? ? ? ? rc
=
ph
[
r
->
phase_handler
].
checker
(
r
,
&
ph
[
r
->
phase_handler
]);
? ? ? ?
if
(
rc
==
NGX_OK
)
{
? ? ? ? ? ?
return
;
? ? ? ?
}
? ?
}
run_phases 的過程實際上非常簡單, 一次的運行每一個handler, 當任意一個handler返回ok或者所有handler執(zhí)行完成后,整個流程結(jié)束。
這里需要注意的是, ph的下標變化是根據(jù) r->phase_handler 變量決定的, 所以在每個handler內(nèi)部,如果想要讓主程序繼續(xù)處理下一個 handler,需要手動的 r->phase_handler++ ,將phase handler數(shù)組的下標轉(zhuǎn)移到下一個成員。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

