陶輝 著
《深入理解Nginx:模塊開(kāi)發(fā)與架構(gòu)解析》由阿里巴巴資深Nginx專(zhuān)家撰寫(xiě),透徹解析Nginx架構(gòu),詳解Nginx模塊開(kāi)發(fā)方法和技巧。
《深入理解Nginx:模塊開(kāi)發(fā)與架構(gòu)解析》是阿里巴巴資深Nginx技術(shù)專(zhuān)家嘔心瀝血之作,是作者多年的經(jīng)驗(yàn)結(jié)晶,也是目前市場(chǎng)上一本通過(guò)還原Nginx設(shè)計(jì)思想,剖析Nginx架構(gòu)來(lái)幫助讀者快速高效開(kāi)發(fā)HTTP模塊的圖書(shū)。
《深入理解Nginx:模塊開(kāi)發(fā)與架構(gòu)解析》首先通過(guò)介紹官方Nginx的基本用法和配置規(guī)則,幫助讀者了解一般Nginx模塊的用法,然后重點(diǎn)介紹如何開(kāi)發(fā)HTTP模塊(含HTTP過(guò)濾模塊)來(lái)得到定制的Nginx,其中包括開(kāi)發(fā)一個(gè)功能復(fù)雜的模塊所需要了解的各種知識(shí),如Nginx的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)、配置項(xiàng)的解析、記錄日志的工具以及upstream、subrequest的使用方法等。在此基礎(chǔ)上,綜合Nginx框架代碼分析Nginx的架構(gòu),介紹其設(shè)計(jì)理念和技巧,進(jìn)一步幫助讀者自由、有效地開(kāi)發(fā)出功能豐富、性能一流的Nginx模塊。
陶輝,思科后臺(tái)工程師,從事服務(wù)端開(kāi)發(fā)近十年,擅長(zhǎng)Linux分布式架構(gòu)下的海量數(shù)據(jù)處理,擅長(zhǎng)C/C++開(kāi)發(fā)的高性能高吞吐量網(wǎng)絡(luò)服務(wù),曾任思科DMS后臺(tái)架構(gòu)設(shè)計(jì)工作,曾在騰訊QQ空間后臺(tái)個(gè)人信息中心、個(gè)人檔、漂流瓶、空間日志、花藤、好友買(mǎi)賣(mài)等項(xiàng)目中擔(dān)任服務(wù)器設(shè)計(jì)與開(kāi)發(fā)工作,曾在華為中央軟件部綜合網(wǎng)管平臺(tái)擔(dān)任北向接口設(shè)計(jì)開(kāi)發(fā)工作。擁有豐富的Linux高性能服務(wù)器開(kāi)發(fā)經(jīng)驗(yàn),豐富的云存儲(chǔ)系統(tǒng)開(kāi)發(fā)經(jīng)驗(yàn),目前關(guān)注云文檔管理系統(tǒng)和Nginx的再開(kāi)發(fā)。
前 言
第一部分 Nginx能幫我們做什么
第1章 研究Nginx前的準(zhǔn)備工作
1.1 Nginx是什么
1.2 為什么選擇Nginx
1.3 準(zhǔn)備工作
1.3.1 Linux操作系統(tǒng)
1.3.2 使用Nginx的必備軟件
1.3.3 磁盤(pán)目錄
1.3.4 Linux內(nèi)核參數(shù)的優(yōu)化
1.3.5 獲取Nginx源碼
1.4 編譯安裝Nginx
1.5 configure詳解
1.5.1 configure的命令參數(shù)
1.5.2 configure執(zhí)行流程
1.5.3 configure生成的文件
1.6 Nginx的命令行控制
1.7 小結(jié)
第2章 Nginx的配置
2.1 運(yùn)行中的Nginx進(jìn)程間的關(guān)系
2.2 Nginx配置的通用語(yǔ)法
2.2.1 塊配置項(xiàng)
2.2.2 配置項(xiàng)的語(yǔ)法格式
2.2.3 配置項(xiàng)的注釋
2.2.4 配置項(xiàng)的單位
2.2.5 在配置中使用變量
2.3 Nginx服務(wù)的基本配置
2.3.1 用于調(diào)試進(jìn)程和定位問(wèn)題的配置項(xiàng)
2.3.2 正常運(yùn)行的配置項(xiàng)
2.3.3 優(yōu)化性能的配置項(xiàng)
2.3.4 事件類(lèi)配置項(xiàng)
2.4 用HTTP核心模塊配置一個(gè)靜態(tài)Web服務(wù)器
2.4.1 虛擬主機(jī)與請(qǐng)求的分發(fā)
2.4.2 文件路徑的定義
2.4.3 內(nèi)存及磁盤(pán)資源的分配
2.4.4 網(wǎng)絡(luò)連接的設(shè)置
2.4.5 MIME類(lèi)型的設(shè)置
2.4.6 對(duì)客戶(hù)端請(qǐng)求的限制
2.4.7 文件操作的優(yōu)化
2.4.8 對(duì)客戶(hù)端請(qǐng)求的特殊處理
2.4.9 ngx_http_core_module模塊提供的變量
2.5 用HTTP proxy module配置一個(gè)反向代理服務(wù)器
2.5.1 負(fù)載均衡的基本配置
2.5.2 反向代理的基本配置
2.6 小結(jié)
第二部分 如何編寫(xiě)HTTP模塊
第3章 開(kāi)發(fā)一個(gè)簡(jiǎn)單的HTTP模塊
3.1 如何調(diào)用HTTP模塊
3.2 準(zhǔn)備工作
3.2.1 整型的封裝
3.2.2 ngx_str_t數(shù)據(jù)結(jié)構(gòu)
3.2.3 ngx_list_t數(shù)據(jù)結(jié)構(gòu)
3.2.4 ngx_table_elt_t數(shù)據(jù)結(jié)構(gòu)
3.2.5 ngx_buf_t數(shù)據(jù)結(jié)構(gòu)
3.2.6 ngx_chain_t數(shù)據(jù)結(jié)構(gòu)
3.3 如何將自己的HTTP模塊編譯進(jìn)Nginx
3.3.1 config文件的寫(xiě)法
3.3.2 利用configure腳本將定制的模塊加入到Nginx中
3.3.3 直接修改Makefile文件
3.4 HTTP模塊的數(shù)據(jù)結(jié)構(gòu)
3.5 定義自己的HTTP模塊
3.6 處理用戶(hù)請(qǐng)求
3.6.1 處理方法的返回值
3.6.2 獲取URI和參數(shù)
3.6.3 獲取HTTP頭部
3.6.4 獲取HTTP包體
3.7 發(fā)送響應(yīng)
3.7.1 發(fā)送HTTP頭部
3.7.2 將內(nèi)存中的字符串作為包體發(fā)送
3.7.3 經(jīng)典的"Hello World"示例
3.8 將磁盤(pán)文件作為包體發(fā)送
3.8.1 如何發(fā)送磁盤(pán)中的文件
3.8.2 清理文件句柄
3.8.3 支持用戶(hù)多線(xiàn)程下載和斷點(diǎn)續(xù)傳
3.9 用C++語(yǔ)言編寫(xiě)HTTP模塊
3.9.1 編譯方式的修改
3.9.2 程序中的符號(hào)轉(zhuǎn)換
3.10 小結(jié)
第4章 配置、error日志和請(qǐng)求上下文
4.1 http配置項(xiàng)的使用場(chǎng)景
4.2 怎樣使用http配置
4.2.1 分配用于保存配置參數(shù)的數(shù)據(jù)結(jié)構(gòu)
4.2.2 設(shè)定配置項(xiàng)的解析方式
4.2.3 使用14種預(yù)設(shè)方法解析配置項(xiàng)
4.2.4 自定義配置項(xiàng)處理方法
4.2.5 合并配置項(xiàng)
4.3 HTTP配置模型
4.3.1 解析HTTP配置的流程
4.3.2 HTTP配置模型的內(nèi)存布局
4.3.3 如何合并配置項(xiàng)
4.3.4 預(yù)設(shè)配置項(xiàng)處理方法的工作原理
4.4 error日志的用法
4.5 請(qǐng)求的上下文
4.5.1 上下文與全異步Web服務(wù)器的關(guān)系
4.5.2 如何使用HTTP上下文
4.5.3 HTTP框架如何維護(hù)上下文結(jié)構(gòu)
4.6 小結(jié)
第5章 訪問(wèn)第三方服務(wù)
5.1 upstream的使用方式
5.1.1 ngx_http_upstream_t結(jié)構(gòu)體
5.1.2 設(shè)置upstream的限制性參數(shù)
5.1.3 設(shè)置需要訪問(wèn)的第三方服務(wù)器地址
5.1.4 設(shè)置回調(diào)方法
5.1.5 如何啟動(dòng)upstream機(jī)制
5.2 回調(diào)方法的執(zhí)行場(chǎng)景
5.2.1 create_request回調(diào)方法
5.2.2 reinit_request回調(diào)方法
5.2.3 finalize_request回調(diào)方法
5.2.4 process_header回調(diào)方法
5.2.5 rewrite_redirect回調(diào)方法
5.2.6 input_filter_init與input_filter回調(diào)方法
5.3 使用upstream的示例
5.3.1 upstream的各種配置參數(shù)
5.3.2 請(qǐng)求上下文
5.3.3 在create_request方法中構(gòu)造請(qǐng)求
5.3.4 在process_header方法中解析包頭
5.3.5 在finalize_request方法中釋放資源
5.3.6 在ngx_http_mytest_handler方法中啟動(dòng)upstream
5.4 subrequest的使用方式
5.4.1 配置子請(qǐng)求的處理方式
5.4.2 實(shí)現(xiàn)子請(qǐng)求處理完畢時(shí)的回調(diào)方法
5.4.3 處理父請(qǐng)求被重新激活后的回調(diào)方法
5.4.4 啟動(dòng)subrequest子請(qǐng)求
5.5 subrequest執(zhí)行過(guò)程中的主要場(chǎng)景
5.5.1 如何啟動(dòng)subrequest
5.5.2 如何轉(zhuǎn)發(fā)多個(gè)子請(qǐng)求的響應(yīng)包體
5.5.3 子請(qǐng)求如何激活父請(qǐng)求
5.6 subrequest使用的例子
5.6.1 配置文件中子請(qǐng)求的設(shè)置
5.6.2 請(qǐng)求上下文
5.6.3 子請(qǐng)求結(jié)束時(shí)的處理方法
5.6.4 父請(qǐng)求的回調(diào)方法
5.6.5 啟動(dòng)subrequest
5.7 小結(jié)
第6章 開(kāi)發(fā)一個(gè)簡(jiǎn)單的HTTP過(guò)濾模塊
6.1 過(guò)濾模塊的意義
6.2 過(guò)濾模塊的調(diào)用順序
6.2.1 過(guò)濾鏈表是如何構(gòu)成的
6.2.2 過(guò)濾鏈表的順序
6.2.3 官方默認(rèn)HTTP過(guò)濾模塊的功能簡(jiǎn)介
6.3 HTTP過(guò)濾模塊的開(kāi)發(fā)步驟
6.4 HTTP過(guò)濾模塊的簡(jiǎn)單例子
6.4.1 如何編寫(xiě)config文件
6.4.2 配置項(xiàng)和上下文
6.4.3 定義HTTP過(guò)濾模塊
6.4.4 初始化HTTP過(guò)濾模塊
6.4.5 處理請(qǐng)求中的HTTP頭部
6.4.6 處理請(qǐng)求中的HTTP包體
6.5 小結(jié)
第7章 Nginx提供的高級(jí)數(shù)據(jù)結(jié)構(gòu)
7.1 Nginx提供的高級(jí)數(shù)據(jù)結(jié)構(gòu)概述
7.2 ngx_queue_t雙向鏈表
7.2.1 為什么設(shè)計(jì)ngx_queue_t雙向鏈表
7.2.2 雙向鏈表的使用方法
7.2.3 使用雙向鏈表排序的例子
7.2.4 雙向鏈表是如何實(shí)現(xiàn)的
7.3 ngx_array_t動(dòng)態(tài)數(shù)組
7.3.1 為什么設(shè)計(jì)ngx_array_t動(dòng)態(tài)數(shù)組
7.3.2 動(dòng)態(tài)數(shù)組的使用方法
7.3.3 使用動(dòng)態(tài)數(shù)組的例子
7.3.4 動(dòng)態(tài)數(shù)組的擴(kuò)容方式
7.4 ngx_list_t單向鏈表
7.5 ngx_rbtree_t紅黑樹(shù)
7.5.1 為什么設(shè)計(jì)ngx_rbtree_t紅黑樹(shù)
7.5.2 紅黑樹(shù)的特性
7.5.3 紅黑樹(shù)的使用方法
7.5.4 使用紅黑樹(shù)的簡(jiǎn)單例子
7.5.5 如何自定義添加成員方法
7.6 ngx_radix_tree_t基數(shù)樹(shù)
7.6.1 ngx_radix_tree_t基數(shù)樹(shù)的原理
7.6.2 基數(shù)樹(shù)的使用方法
7.6.3 使用基數(shù)樹(shù)的例子
7.7 支持通配符的散列表
7.7.1 ngx_hash_t基本散列表
7.7.2 支持通配符的散列表
7.7.3 帶通配符散列表的使用例子
7.8 小結(jié)
第三部分 深入Nginx
第8章 Nginx基礎(chǔ)架構(gòu)
8.1 Web服務(wù)器設(shè)計(jì)中的關(guān)鍵約束
8.2 Nginx的架構(gòu)設(shè)計(jì)
8.2.1 優(yōu)秀的模塊化設(shè)計(jì)
8.2.2 事件驅(qū)動(dòng)架構(gòu)
8.2.3 請(qǐng)求的多階段異步處理
8.2.4 管理進(jìn)程、多工作進(jìn)程設(shè)計(jì)
8.2.5 平臺(tái)無(wú)關(guān)的代碼實(shí)現(xiàn)
8.2.6 內(nèi)存池的設(shè)計(jì)
8.2.7 使用統(tǒng)一管道過(guò)濾器模式的HTTP過(guò)濾模塊
8.2.8 其他一些用戶(hù)模塊
8.3 Nginx框架中的核心結(jié)構(gòu)體ngx_cycle_t
8.3.1 ngx_listening_t結(jié)構(gòu)體
8.3.2 ngx_cycle_t結(jié)構(gòu)體
8.3.3 ngx_cycle_t支持的方法
8.4 Nginx啟動(dòng)時(shí)框架的處理流程
8.5 worker進(jìn)程是如何工作的
8.6 master進(jìn)程是如何工作的
8.7 小結(jié)
第9章 事件模塊
9.1 事件處理框架概述
9.2 Nginx事件的定義
9.3 Nginx連接的定義
9.3.1 被動(dòng)連接
9.3.2 主動(dòng)連接
9.3.3 ngx_connection_t連接池
9.4 ngx_events_module核心模塊
9.4.1 如何管理所有事件模塊的配置項(xiàng)
9.4.2 管理事件模塊
9.5 ngx_event_core_module事件模塊
9.6 epoll事件驅(qū)動(dòng)模塊
9.6.1 epoll的原理和用法
9.6.2 如何使用epoll
9.6.3 ngx_epoll_module模塊的實(shí)現(xiàn)
9.7 定時(shí)器事件
9.7.1 緩存時(shí)間的管理
9.7.2 緩存時(shí)間的精度
9.7.3 定時(shí)器的實(shí)現(xiàn)
9.8 事件驅(qū)動(dòng)框架的處理流程
9.8.1 如何建立新連接
9.8.2 如何解決"驚群"問(wèn)題
9.8.3 如何實(shí)現(xiàn)負(fù)載均衡
9.8.4 post事件隊(duì)列
9.8.5 ngx_process_events_and_timers流程
9.9 文件的異步I/O
9.9.1 Linux內(nèi)核提供的文件異步I/O
9.9.2 ngx_epoll_module模塊中實(shí)現(xiàn)的針對(duì)文件的異步I/O
9.10 小結(jié)
第10章 HTTP框架的初始化
10.1 HTTP框架概述
10.2 管理HTTP模塊的配置項(xiàng)
10.2.1 管理main級(jí)別下的配置項(xiàng)
10.2.2 管理server級(jí)別下的配置項(xiàng)
10.2.3 管理location級(jí)別下的配置項(xiàng)
10.2.4 不同級(jí)別配置項(xiàng)的合并
10.3 監(jiān)聽(tīng)端口的管理
10.4 server的快速檢索
10.5 location的快速檢索
10.6 HTTP請(qǐng)求的11個(gè)處理階段
10.6.1 HTTP處理階段的普適規(guī)則
10.6.2 NGX_HTTP_POST_READ_PHASE階段
10.6.3 NGX_HTTP_SERVER_REWRITE_PHASE階段
10.6.4 NGX_HTTP_FIND_CONFIG_PHASE階段
10.6.5 NGX_HTTP_REWRITE_PHASE階段
10.6.6 NGX_HTTP_POST_REWRITE_PHASE階段
10.6.7 NGX_HTTP_PREACCESS_PHASE階段
10.6.8 NGX_HTTP_ACCESS_PHASE階段
10.6.9 NGX_HTTP_POST_ACCESS_PHASE階段
10.6.10 NGX_HTTP_TRY_FILES_PHASE階段
10.6.11 NGX_HTTP_CONTENT_PHASE階段
10.6.12 NGX_HTTP_LOG_PHASE階段
10.7 HTTP框架的初始化流程
10.8 小結(jié)
第11章 HTTP框架的執(zhí)行流程
11.1 HTTP框架執(zhí)行流程概述
11.2 新連接建立時(shí)的行為
11.3 第一次可讀事件的處理
11.4 接收HTTP請(qǐng)求行
11.5 接收HTTP頭部
11.6 處理HTTP請(qǐng)求
11.6.1 ngx_http_core_generic_phase
11.6.2 ngx_http_core_rewrite_phase
11.6.3 ngx_http_core_access_phase
11.6.4 ngx_http_core_content_phase
11.7 subrequest與post請(qǐng)求
11.8 處理HTTP包體
11.8.1 接收包體
11.8.2 放棄接收包體
11.9 發(fā)送HTTP響應(yīng)
11.9.1 ngx_http_send_header
11.9.2 ngx_http_output_filter
11.9.3 ngx_http_writer
11.10 結(jié)束HTTP請(qǐng)求
11.10.1 ngx_http_close_connection
11.10.2 ngx_http_free_request
11.10.3 ngx_http_close_request
11.10.4 ngx_http_finalize_connection
11.10.5 ngx_http_terminate_request
11.10.6 ngx_http_finalize_request
11.11 小結(jié)
第12章 upstream機(jī)制的設(shè)計(jì)與實(shí)現(xiàn)
12.1 upstream機(jī)制概述
12.1.1 設(shè)計(jì)目的
12.1.2 ngx_http_upstream_t數(shù)據(jù)結(jié)構(gòu)的意義
12.1.3 ngx_http_upstream_conf_t配置結(jié)構(gòu)體
12.2 啟動(dòng)upstream
12.3 與上游服務(wù)器建立連接
12.4 發(fā)送請(qǐng)求到上游服務(wù)器
12.5 接收上游服務(wù)器的響應(yīng)頭部
12.5.1 應(yīng)用層協(xié)議的兩段劃分方式
12.5.2 處理包體的3種方式
12.5.3 接收響應(yīng)頭部的流程
12.6 不轉(zhuǎn)發(fā)響應(yīng)時(shí)的處理流程
12.6.1 input_filter方法的設(shè)計(jì)
12.6.2 默認(rèn)的input_filter方法
12.6.3 接收包體的流程
12.7 以下游網(wǎng)速優(yōu)先來(lái)轉(zhuǎn)發(fā)響應(yīng)
12.7.1 轉(zhuǎn)發(fā)響應(yīng)的包頭
12.7.2 轉(zhuǎn)發(fā)響應(yīng)的包體
12.8 以上游網(wǎng)速優(yōu)先來(lái)轉(zhuǎn)發(fā)響應(yīng)
12.8.1 ngx_event_pipe_t結(jié)構(gòu)體的意義
12.8.2 轉(zhuǎn)發(fā)響應(yīng)的包頭
12.8.3 轉(zhuǎn)發(fā)響應(yīng)的包體
12.8.4 ngx_event_pipe_read_upstream方法
12.8.5 ngx_event_pipe_write_to_downstream方法
12.9 結(jié)束upstream請(qǐng)求
12.10 小結(jié)
第13章 郵件代理模塊
13.1 郵件代理服務(wù)器的功能
13.2 郵件模塊的處理框架
13.2.1 一個(gè)請(qǐng)求的8個(gè)獨(dú)立處理階段
13.2.2 郵件類(lèi)模塊的定義
13.2.3 郵件框架的初始化
13.3 初始化請(qǐng)求
13.3.1 描述郵件請(qǐng)求的ngx_mail_session_t結(jié)構(gòu)體
13.3.2 初始化郵件請(qǐng)求的流程
13.4 接收并解析客戶(hù)端請(qǐng)求
13.5 郵件認(rèn)證
13.5.1 ngx_mail_auth_http_ctx_t結(jié)構(gòu)體
13.5.2 與認(rèn)證服務(wù)器建立連接
13.5.3 發(fā)送請(qǐng)求到認(rèn)證服務(wù)器
13.5.4 接收并解析響應(yīng)
13.6 與上游郵件服務(wù)器間的認(rèn)證交互
13.6.1 ngx_mail_proxy_ctx_t結(jié)構(gòu)體
13.6.2 向上游郵件服務(wù)器發(fā)起連接
13.6.3 與郵件服務(wù)器認(rèn)證交互的過(guò)程
13.7 透?jìng)魃嫌梧]件服務(wù)器與客戶(hù)端間的流
13.8 小結(jié)
第14章 進(jìn)程間的通信機(jī)制
14.1 概述
14.2 共享內(nèi)存
14.3 原子操作
14.3.1 不支持原子庫(kù)下的原子操作
14.3.2 x86架構(gòu)下的原子操作
14.3.3 自旋鎖
14.4 Nginx頻道
14.5 信號(hào)
14.6 信號(hào)量
14.7 文件鎖
14.8 互斥鎖
14.8.1 文件鎖實(shí)現(xiàn)的ngx_shmtx_t鎖
14.8.2 原子變量實(shí)現(xiàn)的ngx_shmtx_t鎖
14.9 小結(jié)
3)如果handler方法返回NGX_DONE,則意味著剛才的handler方法無(wú)法在這一次調(diào)度中處理完這一個(gè)階段,它需要多次的調(diào)度才能完成。注意,此時(shí)返回NGX—OK,它會(huì)使得HTTP框架立刻把控制權(quán)交還給epoll等事件模塊,不再處理當(dāng)前請(qǐng)求,唯有這個(gè)請(qǐng)求上的事件再次被觸發(fā)時(shí)才會(huì)繼續(xù)執(zhí)行。
4)如果handler方法返回除去NGX_DECLINED或者NGX_DONE以外的其他值,則調(diào)用ngx_http_finalize_request結(jié)束請(qǐng)求,其參數(shù)為handler方法的返回值。
可以注意到,ngx_http_core_rewrite_phase方法與ngx_http_core_generic_phase方法有一個(gè)顯著的不同點(diǎn):前者永遠(yuǎn)不會(huì)導(dǎo)致跨過(guò)同一個(gè)HTTP階段的其他處理方法,就直接跳到下一個(gè)階段來(lái)處理請(qǐng)求。原因其實(shí)很簡(jiǎn)單,可能有許多HTTP模塊在NGX HTTP SERVERREWRITE_PHASE和NGX_HTTP_REWRITE_PHASE階段同時(shí)處理重寫(xiě)URL這樣的業(yè)務(wù),HTTP框架認(rèn)為這兩個(gè)階段的HTTP模塊是完全平等的,序號(hào)靠前的HTTP模塊優(yōu)先級(jí)并不會(huì)更高,它不能決定序號(hào)靠后的HTTP模塊是否可以再次重寫(xiě)URL。因此,ngx http corerewrite_phase方法絕對(duì)不會(huì)把phase_handler直接設(shè)置到下一個(gè)階段處理方法的流程中,即不可能存在類(lèi)似下面的代碼。
11.6.3 ngx http core access phase
ngx_http_core_access—phase方法是僅用于NGX_HTTP_ACCESS_PHASE階段的處理方法,這一階段用于控制用戶(hù)發(fā)起的請(qǐng)求是否合法,如檢測(cè)客戶(hù)端的IP地址是否允許訪問(wèn)。它涉及nginx.conf配置文件中satisfy配置項(xiàng)的參數(shù)值,見(jiàn)表11—2。
對(duì)于表11—2的any配置項(xiàng),是通過(guò)ngx_http_request_t結(jié)構(gòu)體中的access_code成員來(lái)傳遞handler方法的返回值的,因此,ngx_http_core_access_phase方法會(huì)比較復(fù)雜,如圖11—10所示。
為什么要寫(xiě)這本書(shū)
當(dāng)我試圖在產(chǎn)品的關(guān)鍵位置設(shè)計(jì)一個(gè)高性能Web服務(wù)器時(shí),我選擇使用成熟的Nginx。選擇它的理由為:首先,它對(duì)服務(wù)器性能上的挖掘已經(jīng)達(dá)到了很高水平,它能盡量使不同的硬件(包括網(wǎng)卡、硬盤(pán)、不同的CPU核心)并發(fā)運(yùn)行,同時(shí)軟件中又沒(méi)有阻塞進(jìn)程使之睡眠的代碼,從性能上來(lái)說(shuō),它可以挑戰(zhàn)任何服務(wù)器。其次,完全基于事件驅(qū)動(dòng)的服務(wù)器開(kāi)發(fā)效率往往很不理想,它們要處理的事件過(guò)于底層化、細(xì)節(jié)化,這使得各功能模塊無(wú)法聚焦于業(yè)務(wù),最終產(chǎn)品的功能都較為單一,不會(huì)有豐富的可選功能。但Nginx卻不然,由于它在軟件架構(gòu)上具有優(yōu)秀的設(shè)計(jì),使得Nginx完全由許多簡(jiǎn)單的模塊構(gòu)成,各模塊(特別是HTTP模塊)不用介入底層細(xì)節(jié),在盡享分階段、無(wú)阻塞的事件驅(qū)動(dòng)架構(gòu)下,可以專(zhuān)注于業(yè)務(wù)功能的實(shí)現(xiàn),這樣最終為Nginx帶來(lái)了大量的官方、第三方的功能模塊,使得功能同樣強(qiáng)大的Nginx在產(chǎn)品核心位置上足以擔(dān)當(dāng)重任,經(jīng)受住海量請(qǐng)求的考驗(yàn)。
當(dāng)Nginx已有模塊提供的功能不能完全實(shí)現(xiàn)我的所有業(yè)務(wù)需求時(shí),我可以在Nginx的后端再搭建一個(gè)實(shí)現(xiàn)了缺失功能的非Nginx服務(wù)器,將Nginx無(wú)法實(shí)現(xiàn)的請(qǐng)求反向代理到這臺(tái)服務(wù)器上處理。但這樣也有一定的弊端,首先增大了處理請(qǐng)求的開(kāi)銷(xiāo),其次后端服務(wù)器的設(shè)計(jì)仍然制約著總體性能(它依然需要解決Nginx解決過(guò)的無(wú)阻塞問(wèn)題,那樣才能像Nginx一樣高效),這樣做僅適用于對(duì)性能要求不高的場(chǎng)景。唯有開(kāi)發(fā)一個(gè)實(shí)現(xiàn)了所需功能的自定義Nginx模塊嵌入到Nginx代碼中,才能讓自己的業(yè)務(wù)像Nginx一樣充分挖掘服務(wù)器的硬件資源,及時(shí)地響應(yīng)百萬(wàn)級(jí)別的并發(fā)TCP連接。
當(dāng)我在開(kāi)發(fā)Nginx模塊之前,試圖在市面上找到一本關(guān)于Nginx模塊開(kāi)發(fā)的書(shū)籍(無(wú)論是中文還是英文)時(shí)卻一無(wú)所獲。我只能找到如何使用Nginx及其已有模塊的書(shū)籍。為了開(kāi)發(fā)Nginx模塊,我只能通過(guò)閱讀Nginx極度缺少注釋的源代碼,并分析各種官方Nginx模塊來(lái)逐步還原其設(shè)計(jì)思想,反復(fù)嘗試、驗(yàn)證著怎樣的模塊能夠使用Nginx的基礎(chǔ)架構(gòu),和豐富的跨平臺(tái)工具方法,同時(shí)符合Nginx設(shè)計(jì)思想,使Nginx擁有媲美Linux內(nèi)核的一流效率。這個(gè)過(guò)程耗費(fèi)了我很多的精力,因此,我希望今后的Nginx使用者、開(kāi)發(fā)者在遇到同樣的問(wèn)題時(shí),不至于還要很痛苦地閱讀源代碼來(lái)找到模塊開(kāi)發(fā)方法,而是簡(jiǎn)單地按照章節(jié)查閱本書(shū),就可以快速找到怎樣簡(jiǎn)單、高效地開(kāi)發(fā)Nginx模塊,把精力放在業(yè)務(wù)的實(shí)現(xiàn)上。這是我寫(xiě)這本書(shū)的第一個(gè)目的。
當(dāng)我們產(chǎn)品中運(yùn)行的Nginx出現(xiàn)了問(wèn)題時(shí),往往是通過(guò)找到錯(cuò)誤的配置項(xiàng)、使用方式來(lái)解決的,這樣也的確能夠修復(fù)大部分問(wèn)題。但是更深層次的問(wèn)題,或者是使用場(chǎng)景比較偏僻,抑或是Nginx自身代碼考慮得不夠全面時(shí),這些問(wèn)題往往只能由那些花費(fèi)大量精力研究Nginx源代碼的工程師來(lái)解決。我寫(xiě)作本書(shū)的第二個(gè)目的是希望通過(guò)透徹地解析Nginx架構(gòu),幫助讀者深入理解Nginx,既能夠正確地使用它,也能在它出現(xiàn)任何問(wèn)題時(shí)找到根本原因,進(jìn)而用最合適的方法修復(fù)或者回避問(wèn)題。
Nginx是一個(gè)優(yōu)秀的事件驅(qū)動(dòng)框架,雖然它在HTTP的處理上非常出色,但它絕不僅僅用于Web服務(wù)器。Nginx非常適合開(kāi)發(fā)在傳輸層以TCP對(duì)外提供服務(wù)的服務(wù)器程序?;贜ginx框架開(kāi)發(fā)程序有5個(gè)優(yōu)勢(shì):
1)Nginx將網(wǎng)絡(luò)、磁盤(pán)及定時(shí)器等異步事件的驅(qū)動(dòng)都做了非常好的封裝,基于它開(kāi)發(fā)將可以忽略這些事件處理的細(xì)節(jié)。
2)Nginx封裝了許多平臺(tái)無(wú)關(guān)的接口、容器,適用于跨平臺(tái)開(kāi)發(fā)。
3)優(yōu)秀的模塊化設(shè)計(jì),使得開(kāi)發(fā)者可以輕易地復(fù)用各種已有的模塊,其中既包括基本的讀取配置、記錄日志等模塊,也包括處理請(qǐng)求的諸如HTTP、mail等高級(jí)功能模塊。
4)Nginx是作為服務(wù)器來(lái)設(shè)計(jì)其框架的,因此,它在服務(wù)器進(jìn)程的管理上相當(dāng)出色,基于它開(kāi)發(fā)服務(wù)器程序可以輕松地實(shí)現(xiàn)程序的動(dòng)態(tài)升級(jí),子進(jìn)程的監(jiān)控、管理,配置項(xiàng)的動(dòng)態(tài)修改生效等。
5)Nginx充分考慮到各操作系統(tǒng)所擅長(zhǎng)的“絕活”,能夠使用特殊的系統(tǒng)調(diào)用更高效地完成任務(wù)時(shí),絕不會(huì)去使用低效的通用接口。尤其對(duì)于Linux操作系統(tǒng),Nginx不遺余力地做了大量?jī)?yōu)化。
當(dāng)我們期望編寫(xiě)一款能夠以低負(fù)載處理高并發(fā)請(qǐng)求并且主要處理基于TCP的服務(wù)器程序時(shí),推薦選擇Nginx,它可能會(huì)帶給我們意外的驚喜。這本書(shū)的第三部分,將通過(guò)分析Nginx的內(nèi)部架構(gòu),幫助讀者了解怎樣基于Nginx開(kāi)發(fā)高效的TCP服務(wù)器程序:通過(guò)開(kāi)發(fā)一種新的模塊類(lèi)型,實(shí)現(xiàn)一種新的功能框架來(lái)提供極佳的擴(kuò)展性,使得功能子模塊僅關(guān)注于業(yè)務(wù)的開(kāi)發(fā),忽視底層事件的處理。這是我寫(xiě)作本書(shū)的第三個(gè)目的。
除了這3個(gè)主要目的外,我還希望通過(guò)這本書(shū)向大家展示Nginx在服務(wù)器開(kāi)發(fā)上的許多巧妙設(shè)計(jì),它們或在抽象設(shè)計(jì)上精妙,或通過(guò)操作系統(tǒng)精確、節(jié)省地使用硬件資源,這些細(xì)節(jié)之處的設(shè)計(jì)都體現(xiàn)了Igor Sysoev的不凡功底。即使我們完全不使用Nginx,學(xué)習(xí)這些技巧也將有助于我們服務(wù)器編程水平的提升。
讀者對(duì)象
本書(shū)適合以下讀者閱讀。
對(duì)Nginx及如何將它搭建成一個(gè)高性能的Web服務(wù)器感興趣的讀者。
希望通過(guò)開(kāi)發(fā)特定的HTTP模塊實(shí)現(xiàn)高性能Web服務(wù)器的讀者。
希望了解Nginx的架構(gòu)設(shè)計(jì),學(xué)習(xí)怎樣充分使用服務(wù)器上的硬件資源的讀者。
了解如何快速定位、修復(fù)Nginx中深層次Bug的讀者。
希望利用Nginx提供的框架,設(shè)計(jì)出任何基于TCP的、無(wú)阻塞的、易于擴(kuò)展的服務(wù)器的讀者。
背景知識(shí)
如果僅希望了解怎樣使用已有的Nginx功能搭建服務(wù)器,那么閱讀本書(shū)不需要什么先決條件。但如果希望通過(guò)閱讀本書(shū)的第二、第三部分,來(lái)學(xué)習(xí)Nginx的模塊開(kāi)發(fā)和架構(gòu)設(shè)計(jì)技巧,則必須了解C語(yǔ)言的基本語(yǔ)法。在閱讀本書(shū)第三部分時(shí),需要讀者對(duì)TCP有一個(gè)基本的了解,同時(shí)對(duì)Linux操作系統(tǒng)也應(yīng)該有簡(jiǎn)單的了解。
如何閱讀本書(shū)
我很希望將本書(shū)寫(xiě)成一本“step by step”式(循序漸進(jìn)式)的書(shū)籍,因?yàn)檫@樣最能節(jié)省讀者的時(shí)間,然而,由于3個(gè)主要寫(xiě)作目的想解決的問(wèn)題都不是那么簡(jiǎn)單,所以這本書(shū)只能做一個(gè)折中的處理。
在第一部分的前兩章中,將只探討如何使用Nginx這一問(wèn)題。閱讀這一部分的讀者不需要了解C語(yǔ)言,就可以學(xué)習(xí)如何部署Nginx,學(xué)習(xí)如何向其中添加各種官方、第三方的功能模塊,如何通過(guò)修改配置文件來(lái)更改Nginx及各模塊的功能,如何修改Linux操作系統(tǒng)上的參數(shù)來(lái)優(yōu)化服務(wù)器性能,最終向用戶(hù)提供企業(yè)級(jí)的Web服務(wù)器。這一部分介紹配置項(xiàng)的方式,更偏重于帶領(lǐng)對(duì)Nginx還比較陌生的讀者熟悉它,通過(guò)了解幾個(gè)基本Nginx模塊的配置修改方式,進(jìn)而使讀者可以通過(guò)查詢(xún)官網(wǎng)、第三方網(wǎng)站來(lái)了解如何使用所有Nginx模塊的用法。
在第二部分的第3章~第7章中,都是以例子來(lái)介紹HTTP模塊的開(kāi)發(fā)方式的,這里有些接近于“step by step”的學(xué)習(xí)方式,我在寫(xiě)作這一部分時(shí),會(huì)通過(guò)循序漸進(jìn)的方式使讀者能夠快速上手,同時(shí)會(huì)穿插著介紹其常見(jiàn)用法的基本原理。
在第三部分,將開(kāi)始介紹Nginx的完整框架,閱讀到這里時(shí)將會(huì)了解第二部分中HTTP模塊為何以此種方式開(kāi)發(fā),同時(shí)將可以輕易地開(kāi)發(fā)出Nginx模塊。這一部分并不僅僅滿(mǎn)足于闡述Nginx架構(gòu),而是會(huì)探討其為何如此設(shè)計(jì),只有這樣才能拋開(kāi)HTTP框架、郵件代理框架,實(shí)現(xiàn)一種新的業(yè)務(wù)框架、一種新的模塊類(lèi)型。
對(duì)于Nginx的使用還不熟悉的讀者應(yīng)當(dāng)從第1章開(kāi)始學(xué)習(xí),前兩章將幫助你快速了解Nginx。
使用過(guò)Nginx,但對(duì)如何開(kāi)發(fā)Nginx的HTTP模塊不太了解的讀者可以直接從第3章開(kāi)始學(xué)習(xí),在這一章閱讀完后,即可編寫(xiě)一個(gè)功能大致完整的HTTP模塊。然而,編寫(xiě)企業(yè)級(jí)的模塊必須閱讀完第4章才能做到,這一章將會(huì)介紹編寫(xiě)產(chǎn)品線(xiàn)上服務(wù)器程序時(shí)必備的3個(gè)手段。第5章舉例說(shuō)明了兩種編寫(xiě)復(fù)雜HTTP模塊的方式,在第三部分會(huì)對(duì)這兩種方式有進(jìn)一步的說(shuō)明。第6章介紹一種特殊的HTTP模塊-HTTP過(guò)濾模塊的編寫(xiě)方法。第7章探討基礎(chǔ)容器的用法,這同樣是復(fù)雜模塊的必備工具。
如果讀者對(duì)于普通HTTP模塊的編寫(xiě)已經(jīng)很熟悉,想深入地實(shí)現(xiàn)更為復(fù)雜的HTTP模塊,或者想了解郵件代理服務(wù)器的設(shè)計(jì)與實(shí)現(xiàn),或者希望編寫(xiě)一種新的處理其他協(xié)議的模塊,或者僅僅想了解Nginx的架構(gòu)設(shè)計(jì),都可以直接從第8章開(kāi)始學(xué)習(xí),這一章會(huì)從整體上系統(tǒng)介紹Nginx的模塊式設(shè)計(jì)。第9章的事件框架是Nginx處理TCP的基礎(chǔ),這一章無(wú)法跳過(guò)。閱讀第8、第9章時(shí)可能會(huì)遇到許多第7章介紹過(guò)的容器,這時(shí)可以回到第7章查詢(xún)其用法和意義。第10章~第12章介紹HTTP框架,通過(guò)這3章的學(xué)習(xí)會(huì)對(duì)HTTP模塊的開(kāi)發(fā)有深入的了解,同時(shí)可以學(xué)習(xí)HTTP框架的優(yōu)秀設(shè)計(jì)。第13章簡(jiǎn)單地介紹了郵件代理服務(wù)器的設(shè)計(jì),它近似于簡(jiǎn)化版的HTTP框架。第14章介紹了進(jìn)程間同步的工具。
為了不讓讀者陷入代碼的“汪洋大海”中,在本書(shū)中大量使用了圖表,這樣可以使讀者快速、大體地了解流程和原理。關(guān)鍵地方會(huì)直接給出代碼,并添加注釋加以說(shuō)明。希望這種方式能夠幫助讀者減少閱讀花費(fèi)的時(shí)間,更快、更好地把握Nginx,同時(shí)深入到細(xì)節(jié)中。
在本書(shū)開(kāi)始寫(xiě)作時(shí),由于Nginx的最新穩(wěn)定版本是1.0.14,所以本書(shū)是基于此版本來(lái)編寫(xiě)的。截止到本書(shū)編寫(xiě)完成時(shí),Nginx的穩(wěn)定版本已經(jīng)上升到了1.2.4。但這不會(huì)對(duì)本書(shū)的閱讀造成困擾,因?yàn)楸緯?shū)主要是在介紹Nginx的基本框架代碼,以及怎樣使用這些框架代碼開(kāi)發(fā)新的Nginx模塊,而不是介紹Nginx的某些功能。在這些基本框架代碼中,Nginx一般不會(huì)做任何改變,否則已有的大量Nginx模塊將無(wú)法工作,這種損失也是不可承受的。而且,Nginx框架為具體的功能模塊提供了足夠的靈活性,修改功能時(shí)很少需要修改框架代碼。
Nginx是跨平臺(tái)的服務(wù)器,然而這本書(shū)將只針對(duì)最常見(jiàn)的Linux操作系統(tǒng)進(jìn)行分析,這樣做一方面是篇幅所限,另一方面則是本書(shū)的寫(xiě)作目的主要在于告訴讀者如何基于Nginx編寫(xiě)代碼,而不是怎樣在一個(gè)具體的操作系統(tǒng)上修改配置來(lái)使用Nginx。因此,即使本書(shū)以Linux系統(tǒng)為代表講述Nginx,也不會(huì)影響使用其他操作系統(tǒng)的讀者閱讀,因?yàn)椴僮飨到y(tǒng)的差別對(duì)閱讀本書(shū)的影響實(shí)在是非常小。
勘誤和支持
由于作者的水平有限,加之編寫(xiě)的時(shí)間也很倉(cāng)促,書(shū)中難免會(huì)出現(xiàn)一些錯(cuò)誤或者不準(zhǔn)確的地方,懇請(qǐng)讀者批評(píng)指正。為此,我特意創(chuàng)建了一個(gè)在線(xiàn)支持與應(yīng)急方案的二級(jí)站點(diǎn)。讀者可以將書(shū)中的錯(cuò)誤發(fā)布在Bug勘誤表頁(yè)面中,同時(shí)如果你遇到任何問(wèn)題,也可以訪問(wèn)Q&A頁(yè)面,我將盡量在線(xiàn)上為讀者提供最滿(mǎn)意的解答。書(shū)中的全部源文件都將發(fā)布在這個(gè)網(wǎng)站上,我也會(huì)將相應(yīng)的功能更新及時(shí)發(fā)布出來(lái)。如果你有更多的寶貴意見(jiàn),也歡迎你發(fā)送郵件至我的郵箱,期待能夠聽(tīng)到讀者的真摯反饋。
致謝
我首先要感謝Igor Sysoev,他在Nginx設(shè)計(jì)上展現(xiàn)的功力令人折服,正是他的工作成果才讓本書(shū)的誕生有了意義。
lisa是機(jī)械工業(yè)出版社華章公司的優(yōu)秀編輯,非常值得信任。在這半年的寫(xiě)作過(guò)程中,她花費(fèi)了很多時(shí)間、精力來(lái)閱讀我的書(shū)稿,指出了許多文字和格式上的錯(cuò)誤,她提出的建議大大提高了本書(shū)的可讀性。
在這半年時(shí)間內(nèi),一邊工作一邊寫(xiě)作給我?guī)?lái)了很大的壓力,所以我要感謝我的父母在生活上對(duì)我無(wú)微不至的照顧,使我可以全力投入到寫(xiě)作中。繁忙的工作之余,寫(xiě)作又占用了休息時(shí)間的絕大部分,感謝我的太太對(duì)我的體諒和鼓勵(lì),讓我始終以高昂的斗志投入到本書(shū)的寫(xiě)作中。
感謝我工作中的同事們,正是在與他們一起工作的日子里,我才不斷地對(duì)技術(shù)有新的感悟;正是那些充滿(mǎn)激情的歲月,才使得我越來(lái)越熱愛(ài)服務(wù)器技術(shù)的開(kāi)發(fā)。
謹(jǐn)以此書(shū),獻(xiàn)給我最親愛(ài)的家人,以及眾多熱愛(ài)Nginx的朋友。
陶輝
更多建議: