Nginx:HTTP框架是如何介入请求
考資料 <深入理解Nginx>(陶輝)
? ? ? ? ? ? Nginx事件模塊博客地址:http://www.cnblogs.com/runnyu/p/4914698.html
?
Nginx是一個(gè)事件驅(qū)動(dòng)構(gòu)架的Web服務(wù)器,在上次的博客中我們可以看到Nginx是如何驅(qū)動(dòng)事件的處理的。
本次將介紹HTTP框架是如何介入跟處理HTTP網(wǎng)絡(luò)事件的。因?yàn)闀系乃悸芬呀?jīng)足夠清晰而且內(nèi)容比較獨(dú)立,因此本次基本上就把本章的重點(diǎn)記錄一下。
?
HTTP框架存在的目的
1.Nginx事件框架主要是針對(duì)傳輸層的TCP的,作為Web服務(wù)器HTTP模塊需要處理的則是HTTP,HTTP框架必須要針對(duì)基于TCP的事件框架解決好HTTP的網(wǎng)絡(luò)傳輸、解析、組裝等問(wèn)題。
2.雖然事件驅(qū)動(dòng)構(gòu)架在性能上是不錯(cuò)的,但是HTTP模塊的業(yè)務(wù)通常較復(fù)雜,HTTP框架的存在則可以讓我們屏蔽事件驅(qū)動(dòng)架構(gòu),盡量只關(guān)注業(yè)務(wù),提供開發(fā)效率。
?
?
新連接建立時(shí)的行為
在上次博客的最后可以看到,在ngx_event_accept方法建立連接的最后一步,將會(huì)調(diào)用ngx_listening_t監(jiān)聽結(jié)構(gòu)體的handler方法。這時(shí)候HTTP框架就開始介入請(qǐng)求了。
HTTP框架在初始化時(shí)就會(huì)將每個(gè)監(jiān)聽ngx_listening_t結(jié)構(gòu)體的handler方法設(shè)為ngx_http_init_connection方法,該方法執(zhí)行流程如下圖:
其中定時(shí)器中的超時(shí)時(shí)間是nginx.conf配置文件中指定的client_header_timeout,后面的超時(shí)時(shí)間設(shè)定也是這個(gè)值。
?
?
第一次可讀事件的處理
當(dāng)TCP連接上第一次出現(xiàn)可讀事件時(shí),將會(huì)調(diào)用ngx_http_init_request方法初始化這個(gè)HTTP請(qǐng)求
該方法主要做了3件事情:
1.對(duì)請(qǐng)求構(gòu)造ngx_http_request_t結(jié)構(gòu)體并初始化部分參數(shù);
2.修改讀事件的回調(diào)方法為ngx_http_process_request_line
3.調(diào)用上面的回調(diào)方法解析HTTP請(qǐng)求行
6.讀事件被觸發(fā),這是需要在用戶態(tài)的進(jìn)程空間分配內(nèi)存,用來(lái)把內(nèi)核緩沖區(qū)上的TCP流復(fù)制到用戶態(tài)的內(nèi)存中。
? ?這一步將在ngx_connection_t的內(nèi)存池中分配一塊內(nèi)存,內(nèi)存塊的大小與nginx.conf文件中的client_header_buffer_size配置項(xiàng)參數(shù)一致。
? ?ngx_connection_t結(jié)構(gòu)體的buffer指針以及ngx_http_request_t結(jié)構(gòu)體的header_in指針共同指向這塊內(nèi)存緩沖區(qū)。
?
?
?
接收HTTP請(qǐng)求行
在初始化請(qǐng)求之后,將調(diào)用ngx_http_process_request_line方法接收HTTP請(qǐng)求行。
因?yàn)檎?qǐng)求行的長(zhǎng)度是不定的,這意味著在讀事件被觸發(fā)時(shí),內(nèi)核套接字緩沖區(qū)的大小未必足夠接收到全部的HTTP請(qǐng)求行。
因此調(diào)用一次ngx_http_process_request_line方法不一定能夠接收完完整的HTTP請(qǐng)求行,該方法會(huì)被多次調(diào)度。下圖展示了該方法的流程
該方法會(huì)調(diào)用recv方法把Linux內(nèi)核套接字緩沖區(qū)中的TCP流復(fù)制到header_in緩沖區(qū)中。
header_in的類型是ngx_buf_t,它的pos成員和last成員指向的地址之間的內(nèi)存就是收到的未解析的字符流。
?
4.在本次沒有接收到TCP流的時(shí)候,告訴事件驅(qū)動(dòng)程序繼續(xù)檢測(cè)這個(gè)讀事件,然后該方法就結(jié)束。在該讀事件準(zhǔn)備好的時(shí)候,該方法將被再次調(diào)度。
5.在接收到TCP流后,用狀態(tài)機(jī)(ngx_http_parse_request_line方法)解析已經(jīng)接收到的TCP字符流,確認(rèn)其是否構(gòu)成完整的HTTP請(qǐng)求行。
7.如果ngx_http_parse_request_line方法返回NGX_OK,表示已經(jīng)成功地接收到完整的請(qǐng)求行。這一步將把請(qǐng)求行的的信息設(shè)置到ngx_http_request_t結(jié)構(gòu)體的相應(yīng)成員中
? ?(request_line、uri、method_name、http_protocol、args等)。
11.接收完HTTP請(qǐng)求行后,把讀事件的回調(diào)方法更改為ngx_http_request_headers準(zhǔn)備接收HTTP頭部。
?
?
接收HTTP頭部
跟HTTP請(qǐng)求行一樣,HTTP頭部也屬于可變長(zhǎng)度的字符串,它與HTTP請(qǐng)求行和包體間都是通過(guò)換行符來(lái)區(qū)分的。
下圖展示了HTTP框架使用ngx_http_process_request_headers方法接收、解析HTTP頭部的流程
6.調(diào)用ngx_http_parse_header_line方法解析緩沖區(qū)的字符流。這個(gè)方法有3個(gè)返回值:
? ?返回NGX_OK時(shí),表示解析出一行HTTP頭部;返回NGX_HTTP_PARSE_HEADER_DONE時(shí),表示已經(jīng)解析出了完整的HTTP頭部;
? ?返回NGX_AGAIN時(shí),表示還需要接收到更多的字符流才能繼續(xù)解析;除此之外的錯(cuò)誤情況,將發(fā)送400錯(cuò)誤給客戶端。
7.解析出的HTTP頭部信息設(shè)置到ngx_http_request_t結(jié)構(gòu)體headers_in成員的headers鏈表中。
9.當(dāng)ngx_http_parse_header_line方法返回NGX_HTTP_PARSE_HEADER_DONE時(shí),將會(huì)根據(jù)HTTP頭部中的host字段情況,
? ?調(diào)用ngx_http_find_virtual_server方法找到對(duì)應(yīng)的虛擬主機(jī)配置塊。ngx_http_request_t結(jié)構(gòu)體里的srv_conf、loc_conf成員被重新設(shè)置,以指向正確的虛擬主機(jī)。
?
在接收到完整的HTTP頭部后,已經(jīng)有足夠的必要信息開始在業(yè)務(wù)上處理HTTP請(qǐng)求了。下一節(jié)將說(shuō)明HTTP框架是如何召集負(fù)責(zé)具體功能的各HTTP模塊合作處理請(qǐng)求的。
總結(jié)
以上是生活随笔為你收集整理的Nginx:HTTP框架是如何介入请求的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Nginx HTTP之请求行解析函数ng
- 下一篇: Nginx-rtmp直播之业务流程分析-