nginx是個高性能web server,很多時候我們會把它當成reverse proxy或者web server container使用,但有時我們也會開發它的第三方module,因為module才能完全使用nginx的全事件驅動、無阻塞調用機制,充分使用系統資源,達到SERVER最大處理吞吐量。
在開發nginx module時,我們最有可能遇到的一件事就是,在處理一個請求時,我們需要訪問其他多個backend server網絡資源,拉取到結果后分析整理成一個response,再發給用戶。這個過程是無法使用nginx upstream機制的,因為upstream被設計為用來支持nginx reverse proxy功能,所以呢,upstream默認是把其他server的http response body全部返回給client。這與我們的要求不符,這個時候,我們可以考慮subrequest了,nginx http模塊提供的這個功能能夠幫我們搞定它。
先看看subrequest調用函數長什么樣:
挨個分析下參數吧。r是我們的module handler中,nginx調用時傳給我們的請求,這時我們直接傳給subrequest即可。uri和args是我們需要訪問backend server的URL,而psr是subrequest函數執行完后返回給我們的新請求,即將要訪問backend server的請求指針。ps指明了回調函數,就是說,如果這個請求執行完畢,接收到了backend server的響應后,就會回調這個函數。flags會指定這個子請求的一些特征。
看看ngx_http_post_subrequest的結構:
這里的handler你可以指向一個函數,這個函數會在這個子請求結束以后被nginx調用,這時傳給函數的request是子請求,不是原始的父請求哈。
而flag我們一般只會感興趣下面這個NGX_HTTP_SUBREQUEST_IN_MEMORY,flag設為這個宏時,表示發起的子請求,訪問的網絡資源返回的響應將全部放在內存中,我們可以從upstream->buffer里取到響應內容。所以這里如果用了這個flag,一定要確保返回內容不可以很大,例如不能去下載一個大文件。
所以,當我們寫nginx的module時,需要去拉取某個網絡資源,就可以這么寫:
這樣,在這個subrequest執行完后,將會調用ngx_http_my_post_subrequest方法,再次注意,此時傳給你的ngx_http_request_t上下文是子請求的,不是原始的父請求,所以,如果你需要在父請求的上下文中處理這個請求,可以在ngx_http_my_post_subrequest中找到父請求的handler,設置為一個處理函數即可。比如:
這樣,這個ngx_http_my_post_subrequest執行完畢后,nginx開始換醒父請求,奧斯卡這時ngx_http_parent_handler將會被調用。
最后我們要注意,一個請求中,我們只能調用一次subrequest,不能一次生成多個subrequest。我們可以在兒子請求中再創建孫子請求,一直下去都行,但是不能一次創建多個子請求。為什么呢?因為nginx本身的設計就是,每處理完一個事件后,將會檢查有沒有它對應的一個post事件(一對一),如果有則處理。上面的subrequest就是用這個流程的。如果一個請求中我們想同時生成多個子請求,就不能用subrequest了,我們必須自己創建nginx事件來處理了。