lingering_close,字面意思就是延遲關閉,也就是說,當nginx要關閉連接時,并非立即關閉連接,而是先關閉tcp連接的寫,再等待一段時間后再關掉連接的讀。為什么要這樣呢?我們先來看看這樣一個場景。nginx在接收客戶端的請求時,可能由于客戶端或服務端出錯了,要立即響應錯誤信息給客戶端,而nginx在響應錯誤信息后,大分部情況下是需要關閉當前連接。nginx執行完write()系統調用把錯誤信息發送給客戶端,write()系統調用返回成功并不表示數據已經發送到客戶端,有可能還在tcp連接的write buffer里。接著如果直接執行close()系統調用關閉tcp連接,內核會首先檢查tcp的read buffer里有沒有客戶端發送過來的數據留在內核態沒有被用戶態進程讀取,如果有則發送給客戶端RST報文來關閉tcp連接丟棄write buffer里的數據,如果沒有則等待write buffer里的數據發送完畢,然后再經過正常的4次分手報文斷開連接。所以,當在某些場景下出現tcp write buffer里的數據在write()系統調用之后到close()系統調用執行之前沒有發送完畢,且tcp read buffer里面還有數據沒有讀,close()系統調用會導致客戶端收到RST報文且不會拿到服務端發送過來的錯誤信息數據。那客戶端肯定會想,這服務器好霸道,動不動就reset我的連接,連個錯誤信息都沒有。
在上面這個場景中,我們可以看到,關鍵點是服務端給客戶端發送了RST包,導致自己發送的數據在客戶端忽略掉了。所以,解決問題的重點是,讓服務端別發RST包。再想想,我們發送RST是因為我們關掉了連接,關掉連接是因為我們不想再處理此連接了,也不會有任何數據產生了。對于全雙工的TCP連接來說,我們只需要關掉寫就行了,讀可以繼續進行,我們只需要丟掉讀到的任何數據就行了,這樣的話,當我們關掉連接后,客戶端再發過來的數據,就不會再收到RST了。當然最終我們還是需要關掉這個讀端的,所以我們會設置一個超時時間,在這個時間過后,就關掉讀,客戶端再發送數據來就不管了,作為服務端我會認為,都這么長時間了,發給你的錯誤信息也應該讀到了,再慢就不關我事了,要怪就怪你RP不好了。當然,正常的客戶端,在讀取到數據后,會關掉連接,此時服務端就會在超時時間內關掉讀端。這些正是lingering_close所做的事情。協議棧提供 SO_LINGER 這個選項,它的一種配置情況就是來處理lingering_close的情況的,不過nginx是自己實現的lingering_close。lingering_close存在的意義就是來讀取剩下的客戶端發來的數據,所以nginx會有一個讀超時時間,通過lingering_timeout選項來設置,如果在lingering_timeout時間內還沒有收到數據,則直接關掉連接。nginx還支持設置一個總的讀取時間,通過lingering_time來設置,這個時間也就是nginx在關閉寫之后,保留socket的時間,客戶端需要在這個時間內發送完所有的數據,否則nginx在這個時間過后,會直接關掉連接。當然,nginx是支持配置是否打開lingering_close選項的,通過lingering_close選項來配置。 那么,我們在實際應用中,是否應該打開lingering_close呢?這個就沒有固定的推薦值了,如Maxim Dounin所說,lingering_close的主要作用是保持更好的客戶端兼容性,但是卻需要消耗更多的額外資源(比如連接會一直占著)。
這節,我們介紹了nginx中,連接與請求的基本概念,下節,我們講基本的數據結構。
- 上篇:nginx模塊開發篇
- nginx平臺初探
- 初探nginx架構
- nginx基礎概念
- connection
- request
- keepalive
- pipe
- lingering_close
- 基本數據結構
- ngx_str_t
- ngx_pool_t
- ngx_array_t
- ngx_hash_t
- ngx_hash_wildcard_t
- ngx_hash_combined_t
- ngx_hash_keys_arrays_t
- ngx_chain_t
- ngx_buf_t
- ngx_list_t
- ngx_queue_t
- nginx的配置系統
- 指令參數
- 指令上下文
- nginx的模塊化體系結構
- 模塊的分類
- nginx的請求處理
- handler模塊
- handler模塊簡介
- 模塊的基本結構
- 模塊配置結構
- 模塊配置指令
- 模塊上下文結構
- 模塊的定義
- handler模塊的基本結構
- handler模塊的掛載
- handler的編寫步驟
- 示例: hello handler 模塊
- handler模塊的編譯和使用
- 更多handler模塊示例分析
- http access module
- http static module
- http log module
- 過濾模塊
- 過濾模塊簡介
- 過濾模塊的分析
- upstream模塊
- upstream模塊
- upstream模塊接口
- memcached模塊分析
- 本節回顧
- 負載均衡模塊
- 配置
- 指令
- 鉤子
- 初始化配置
- 初始化請求
- peer.get和peer.free回調函數
- 本節回顧
- 其他模塊
- core模塊
- event模塊
- 模塊開發高級篇
- 變量
- 下篇:nginx原理解析篇
- nginx架構詳解
- nginx的源碼目錄結構
- nginx的configure原理
- 模塊編譯順序
- nginx基礎設施
- 內存池
- nginx的啟動階段
- 概述
- 共有流程
- 配置解析
- nginx的請求處理階段
- 接收請求流程
- http請求格式簡介
- 請求頭讀取
- 解析請求行
- 解析請求頭
- 請求體讀取
- 讀取請求體
- 丟棄請求體
- 多階段處理請求
- 多階段執行鏈
- POST_READ階段
- SERVER_REWRITE階段
- FIND_CONFIG階段
- REWRITE階段
- POST_REWRITE階段
- PREACCESS階段
- ACCESS階段
- POST_ACCESS階段
- TRY_FILES階段
- CONTENT階段
- LOG階段
- Nginx filter
- header filter分析
- body filter分析
- ngx_http_copy_filter_module分析
- ngx_http_write_filter_module分析
- subrequest原理解析
- https請求處理解析
- 附錄A 編碼風格
- 附錄B 常用API
- 附錄C 模塊編譯,調試與測試