Linux環(huán)境下高并發(fā)web服務器 的設計與實現(xiàn).docx
《Linux環(huán)境下高并發(fā)web服務器 的設計與實現(xiàn).docx》由會員分享,可在線閱讀,更多相關《Linux環(huán)境下高并發(fā)web服務器 的設計與實現(xiàn).docx(31頁珍藏版)》請在裝配圖網(wǎng)上搜索。
1、Linux環(huán)境下高并發(fā)web服務器的設計與實現(xiàn) 摘要 隨著信息技術的飛速開展,互聯(lián)網(wǎng)已經(jīng)成為社會生產(chǎn)生活中不可缺少的一局部。特別是近十年來,由于移動技 術的進步和智能移動設備的普及,互聯(lián)網(wǎng)用戶數(shù)量呈爆發(fā)式增長,越來越多的行業(yè)正在通過互聯(lián)網(wǎng)進行變革。用戶 群體的開展改變了互聯(lián)網(wǎng)服務的形式,越來越多的服務器需要同時為大量客戶提供服務。因此,如何設計服務器的 通信框架,如何有效利用服務器的系統(tǒng)資源,成為服務器開發(fā)人員關注的焦點。 Web應用具有連接間隔短、突發(fā)性強、響應時間短等特點,它是一種對并發(fā)性要求很高的服務。本文在服務器系 統(tǒng)中選擇主流的Linux操作系統(tǒng),采用C++,結合Socket、
2、I/O多路復用技術epoll、無阻塞I/O、線程池、定時器和 Reactor事件處理模式,設計并實現(xiàn)了一個穩(wěn)定支持高并發(fā)的Web服務器。 本文首先介紹和分析了Wob服務器的相關技術,包括Socket、epolK Reactor事件處理模式、線程池和數(shù)據(jù)庫連 接池;重點討論了epoll與其他I/O模型的區(qū)別,以及epoll模型的特點。其次,根據(jù)Reactor模式分析了服務器的整 圖3. 4事件處理器與存儲隊列存儲單元從阻塞隊列獲取獲取數(shù) 據(jù)進行存儲。如圖3. 5所示。 圖3. 5存儲隊列與存儲單元 3. 2事件別離器流程事件別離器主要負責接收新連接、偵聽到達事件。HTTP長連
3、接可以減少TCP連接的創(chuàng)立次數(shù),使客戶端和服務器能 夠有效復用TCP連接。但是,“在長連接應用 中,服務器與客戶端實例都保持一個持久的連接,這將大量消耗服務器資源,特別是在一些大型應用系統(tǒng)中更是如 此,大量并發(fā)的長連接有可能導致新的請求被阻塞甚至系統(tǒng)崩潰[18]?!? 為了對長連接進行管控,服務器可以通過實現(xiàn)定時器,對每個連接記錄超時時間點來解決這個問題。每當某個 連接有新請求到達時重置該連接的定時器,如果連接超時那么服務器主動釋放連接。判斷超時和重置定時器的工作均 由事件別離器完成,事件別離器工作流程如圖3. 6所示。 設置新連接的定時 器,把新邇接加入 設置新連接的定時 器,把新
4、邇接加入 重置龍援的定時器,把 事件和處理方法加入到 I 到檢測集合中 II 事件阻塞隊列中 I 圖3.6事件別離器流程3. 3事件處理器流程 事件處理器是服務器的核心模塊,主要負責的工作是:服務器的I/O操作、業(yè)務處理,因此對服務器的業(yè)務拓展 工作主要是對事件處理器的功能進行拓展。在本工程實現(xiàn)的Web服務器中,I/O操作主要是指對Socket進行讀寫操 作 ,業(yè)務處理主要是指對接收到的HTTP請求報文進行解析,并生成相應的HTTP響應報文。同時為了能夠支持用戶登錄 功能和用戶注冊功能,當HTTP請求為該兩種功能請求時,事件處理器還需要通過訪問數(shù)據(jù)庫對接收到的用戶名和用 戶密碼進行判斷
5、或存儲操作。 事件處理器的整體流程如圖3. 7所示。 圖3. 7事件處理器流程在本工程中事件處理器的業(yè)務處理可以分為兩個模塊:HTTP請求報文解析模塊、HTTP 響應報文生成模塊。3. 3. 1 HTTP請求報文解析模塊 本模塊主要工作是對客戶端發(fā)送來的HTTP請求報文進行解析。HTTP請求報文由請求行、請求頭部、空白行、請 求體構成,每一行都以CRLF (回車符和換行符)作為結尾標志[19]。 在IITTP/L1中,請求行由請求方法、URI、HTTP協(xié)議版本構成。請求頭部由多行構成,每行以鍵值對的形式表示 (鍵為頭部字段名)。請求頭部包含了一些該連接和報文的信息,如表示長連接狀態(tài)
6、的keep-alive、請求體的編碼 方式content-type等??瞻仔袥]有數(shù)據(jù)只有CRLF表示空白的一行,用來區(qū)分請求頭部和請求體。請求體攜帶客戶端 發(fā)送的數(shù)據(jù),如通過POST請求報文發(fā)送的表單。 HTTP請求解析模塊主要有三個狀態(tài):解析請求行、解析請求頭部、解析請求體。狀態(tài)轉移如圖3. 8所示。GET請求報 文和POST請求報文是本文的主要解析對象。GET請求報文把參數(shù)存放在URL請求體數(shù)據(jù)為空,事件處理器無需解析GET請求報文的請求體。POST請求報文的參數(shù)被存放在POST請求報文的請求體中,事件處理器需要對 POST請求報文的請求體進行解析,從中獲取報文參數(shù)。 登陸和注冊通過P
7、OST報文發(fā)送請求,用戶名和用戶密碼存放在請求體中。在解析請求體過程中,需要對通過解 析得到的用戶名、用戶密碼進行處理:當報文請求為登錄時,向存儲單元查詢用戶名、用戶密碼進行校驗;當報文 請求為注冊時,向存儲單元查詢用戶名、密碼合法性,如果合法那么進行存儲。 3.3.2 HTTP響應報文生成模塊 本模塊主要工作是生成發(fā)送給客戶端的HTTP響應報文。HTTP響應報文由響應行、響應 頭部、空白行、響應體構成,每一行以CRLF表示該行結尾。響應行由HTTP協(xié)議 版本、狀態(tài)碼、狀態(tài)碼描述構成。 狀態(tài)碼由三個十進制數(shù)字組成,狀態(tài)碼的類型由第一個數(shù)字定義。狀態(tài)碼和狀態(tài)碼描述表示HTTP響應報文的報 文
8、類型。HTTP響應報文生成模塊流程如圖3. 9所示。 圖3. 8 HTTP解析模塊狀態(tài)轉移 圖3. 9 HTTP響應報文生成模塊流程“客戶端請求資源合法”是指客戶端請求的資源存在并且是一些 指定的文件,而不是某個文件夾。3.4日志系統(tǒng) 日志是記錄服務器運行狀態(tài)的文件,對于服務器開發(fā)人員和服務器運維人員而言日志是一種能夠直觀地觀察服 務器狀態(tài)的重要文件。通過日志,服務器開發(fā)人員可以查看服務器的日常運行狀態(tài),對服務器架構和功能作出調(diào)整 和拓展;服務器運維人員可以根據(jù)日志中的錯誤信息對服務器進行錯誤排查。日志系統(tǒng)的實現(xiàn)有兩種方式:同步模 式和異步模式。 同步模式的寫入操
9、作與事件處理器串行執(zhí)行。寫日志文件會涉及到I/O操作,同步模式在I/O操作時會阻塞整個 處理流程。當單條日志記錄較大時,事件處理器會被阻塞較長時間,整個服務器的并發(fā)度將有所下降。如果服務器 需要處理大量連接請求時,寫日志操作可能會成為系統(tǒng)瓶頸。 異步模式需要阻塞隊列作為事件處理器和寫日志模塊的傳輸介質(zhì)。事件處理器把日志記錄存入阻塞隊列,寫日 志模塊異步地從阻塞隊列中獲取日志記錄并寫入日志文件。異步模式把寫日志操作與業(yè)務處理流程進行別離,防止 業(yè)務處理流程被阻塞,提高了服務器業(yè)務處理的并發(fā)度。 日志記錄根據(jù)記錄信息被劃分為多個等級。例如,日志記錄被分為調(diào)試信息、運行信息、錯誤信息等幾個級別
10、,服務器正常運行時僅記錄運行信息和錯誤信息;服務器調(diào)試時記錄包括調(diào)試信息的所有信息。服務器可以根據(jù) 不 同的運行狀態(tài)選擇不同的日志等級,過濾無需記錄的日志信息,從而提高日志文件的可讀性。日志系統(tǒng)的寫入記錄 流程如圖3. 10所示。 根據(jù)日志等 級、日志參數(shù) 創(chuàng)立相應的E 志記錄 ? 1 < ? JU) 第N個日志文件 結束 同步璜式 日阻先 入業(yè)不 11 志塞處板 異步建式 寫日志模塊異步地從 阻塞隊列中茨取日志 記錄,并把日志記錄 寫入日志文件 圖3.10寫入日志記錄流程 4工程實現(xiàn)4.1服務器整體功能 本工程是在Linux環(huán)境下使用C++實現(xiàn)的Web服務器
11、。目標服務器基于Reactor模式,結合了 epoll和非阻塞 Socket,利用線程池和數(shù)據(jù)庫連接池實現(xiàn)了LT模式、ET模式下對GET請求報文、POST請求報文的解析和響應,使用定 時器實現(xiàn)了對長連接的管控。此外,本工程還實現(xiàn)了同步和異步兩種模式下的日志系統(tǒng),用于記錄系統(tǒng)運行狀態(tài)。 4.2阻塞隊列 STL向C++開發(fā)者提供了deque、queue等模板類隊列,這些模板類使用方便、接口清晰,但不具備線程平安性。 根據(jù)參考文獻[20]的第十二條: library implementation that allows multiple readers on one container and
12、multiple writers on separate containers. You can't hope for the library to eliminate the need for manual concurrency control, and you can,t rely on any thread support at all. ”可知如果要實現(xiàn)STL的線程平安性,程序員必須 自己去完成相關平安措施。 本工程通過互斥鎖、條件變量對deque重新封裝,利用“生產(chǎn)者-消費者”模式實現(xiàn)了具有線程平安性的阻塞隊 列。 力口鎖(lock mutex) 被條件兗量 consumer
13、 阻塞 元素出隊 解鎖(unlock mutex) 圖4.1阻塞隊列的入隊和出隊4.3日志系統(tǒng) 4.3.1 單例模式為了防止日志文件混亂,日志系統(tǒng)在服務器運行過程中應該只存在一個實例,即單例模式實現(xiàn)日志 系統(tǒng)。單例模式是最常用的設計模式之一,通過私有化構造函數(shù)來保證一個類無法類外實例化,并提供相應的實例訪問接口給 其它程序模塊訪問,該接口只能獲取該類的唯一實例。在C++11標準[21]中,對局部靜態(tài)變量的初始化作出以下要 求: “Otherwise such a variable is initialized the first time control passes through
14、 its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration c
15、oncurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. n 因此,在C++11 標準中,局部靜態(tài)變量的初始化 是具備線程平安性的。單例類可以通過在實例訪問接口中定義該類
16、的局部靜態(tài)對象來定義唯一實例。這種方法是單例模式中的懶漢模式,因為C++中的局部靜態(tài)對象只有在第一次訪 問時才會被初始化。 實例接口代碼如下所示。 1 .Log* Log::Instance() {. static Log minstance; 2 . return &m_instance;.) 4.3.2 日志級別日志系統(tǒng)提供四種日志級別,從低到高依次為:DEBUG、INFO、WARN、ERRORo低級別的記錄信息會 被日志系統(tǒng)的級別過濾。 DEBUG:記錄調(diào)試信息,如請求資源信息、數(shù)據(jù)庫查詢信息等。該級別的信息用于記錄調(diào)試服務器時服務器的詳 細運行過程,在服務器實際運行時不使用。
17、 INFO:記錄服務器當前狀態(tài),如當前的執(zhí)行流程、接收信息、連接數(shù)量等。該級別的信息用于記錄服務器實際 運行時的狀態(tài)。 WARN:記錄警告信息,如服務器連接數(shù)已滿。該級別的信息表示服務器可能出現(xiàn)了異常情況。ERROR:記錄系統(tǒng)出 錯信息,如服務器初始化出錯、創(chuàng)立Socket出錯、檢測到了未定義的事件等。該級別的信息用于記錄服務器的出錯信息。日志系統(tǒng)定義了四個宏:“LOG_DEBUG”、“L0GJNF0"、“LOGJVARN”、“ LOG_ERROR”。其它程序通過調(diào)用 這四個宏向日志系統(tǒng)傳輸日志信息。為了區(qū)分記錄級別,四個宏都通過調(diào)用“LOG_BASE”來進行級別檢查,不同的 接口調(diào)用“
18、LOG_BASE”時傳入不同的記錄等級參數(shù)。 “LOG—BASE”的偽代碼如圖4. 2所示。 4.3.3 分日期、分記錄數(shù)量創(chuàng)立日志文件 日志記錄寫入日志文件時日志系統(tǒng)對當前翻開的日志文件的日期、記錄數(shù)量進行檢查,日志系統(tǒng)根據(jù)日期、記 錄數(shù)量對日志文件進行分類。如果當前日期與日志系統(tǒng)翻開日志文件的日期不一致,那么日志系統(tǒng)新建日志文件,日 志名稱為當前日期;如果當前翻開的日志文件的記錄數(shù)到達單個日志文件的最大記錄數(shù),那么日志系統(tǒng)將創(chuàng)立一個日志名稱帶有當前日期和計數(shù)后綴的新日志文件[22] o 檢查日志文件日期、記錄數(shù)量的偽代碼如圖4. 3所示。 4.3.4 同步模式、異步模式日志系統(tǒng)的
19、模式在服務器初始化中決定:阻塞隊列長度等于零為同步模式,阻塞隊列長度大于零為異步模式。日志 系統(tǒng)根據(jù)不同的記錄級別、日志信息生成相應的日志記錄,并寫入日志文件。同步模式中記錄寫入日志文 件的I/O操作與處理業(yè)務的工作線程串行執(zhí)行,會阻塞工作線程;異步模式創(chuàng)立一個日志寫入線程,工作線程把日志 記錄插入阻塞隊列,由日志寫入線程異步地從阻塞隊列獲取日志記錄并執(zhí)行I/O操作。、日志寫入的偽代碼如圖4. 4 所示。 Algorithm 1: LOG_BASEInput: level, loginfo if level》Instance.level thenWirteLog(level, login
20、fo); flush;end 圖4.2 L0G_BASEAlgorithm 2: CheckLog if log. date * today or (count % maxCount) = 0 thenif log. date 盧 today then create new log, name is today;log. date = today; count = 1;else create new log, name is today,(count / maxCount);end end圖4. 3檢查日志文件 Algorithm 3: WriteLogInput: level
21、, loginfo CheckLog;logging = C r eat eLogging (level. liglnfo); if is A sync thenLogging Q uque. p ush (logging); elsewrite (logging); end圖4. 4日志寫入 4.3.5 4線程池4. 4.1線程池成員線程池主要由兒個成員組成:存儲任務(任務為回調(diào)函數(shù))的阻塞隊列、負責線程互斥訪問臨界 區(qū)的互斥鎖、 負責線程同步的條件變量。 4. 4.2工作線程的同步與競爭 根據(jù)圖3. 7可知線程池中的工作線程與主線程存在同步關系,與其它工作線程存在競爭關系,
22、即半同步/半異步模式。工作線程的執(zhí)行流程偽代碼如圖4. 5所示。 Algorithm 4: Workmutex lock; while threadpool is not closed doif BlockQueue is not empty then get task from BlockQueue;mutex unlock: processing the task;mutex lock; elsethread is blocked by condition variable; endend mutex unlock;圖4.5工作線程工作線程初始化后對線程池中的mutex進行加
23、鎖,然后訪問臨界區(qū),在線程池關閉前循環(huán)執(zhí)行。如 果存儲任務的 阻塞隊列不為空,工作線程從阻塞隊列中取出任務,然后對mutex解鎖并處理任務。任務完成后重新嘗試對mutex力口 鎖,再次參與競爭。如果存儲任務的阻塞隊列為空,工作線程被條件變量阻塞(工作線程被條件變量阻塞會釋放 mutex的所有權,即解鎖),只有主線程向阻塞隊列插入新的任務或主線程關閉線程池時才被喚醒(工作線程被喚醒 后重新嘗試對mutex加鎖,重新參與競爭)。兩種喚醒方式的區(qū)別在于新的任務插入阻塞隊列只會喚醒一個被條件變 量阻塞的工作線程,線程池關閉會喚醒所有被條件變量阻塞的線程。 4.5數(shù)據(jù)庫連接池 數(shù)據(jù)庫連接池的實現(xiàn)方法
24、比線程池更簡單,本質(zhì)是使用一個阻塞隊列存儲空閑的數(shù)據(jù)庫連接。工 作線程向數(shù)據(jù)庫連接池申請和歸還數(shù)據(jù)庫連接實際是對阻塞隊列的出隊和入隊操作。但阻塞隊列的入隊操作如果交給程序員處理 可能會存在遺漏入隊的情況,導致空閑的數(shù)據(jù)庫連接沒有重新進池,從而浪費空閑資源,甚至最后會導致數(shù)據(jù)庫連 接池無空閑的數(shù)據(jù)庫連接可用。同時,程序沒有主動釋放動態(tài)申請的資源,造成了內(nèi)存泄漏。 為了防止某個線程使用完數(shù)據(jù)庫連接后沒有釋放數(shù)據(jù)庫連接的所有權,數(shù)據(jù)庫連接的分配和回收通常采用RAH (Resource Acquisition Is Initialization,資源獲取就是初始化)機制,程序通過定義一個封裝了從數(shù)據(jù)
25、庫連 接池獲取和歸還連接的RAU類來管理連接的分配和回收[23]。RAH類在構造函數(shù)中獲取數(shù)據(jù)庫連接,在析構函數(shù)中 歸還數(shù)據(jù)庫連接。通過RAU機制,工作線程只能通過定義一個RAH實例獲取數(shù)據(jù)庫連接。同時工作線程無需主動歸 還數(shù)據(jù)庫連接,因為線程對數(shù)據(jù)庫連接的所有權周期就是RAH實例的生命周期,當線程完成數(shù)據(jù)庫訪問后退出相應 作用域,RAH實例被系統(tǒng)自動銷毀,RAH實例的析構函數(shù)釋放數(shù)據(jù)庫連接。數(shù)據(jù)庫連接RAH類的構造函數(shù)和析構函 數(shù)的偽代碼如圖4. 6所示。 Algorithm 5: Construct SqlPoolRAIIOutput: sqlConnection get sqlCon
26、nection from sqlConnectionQueue;mySqlConnection = sqlConnection; Algorithm 6: Destruct SqlPoolRAIIif mySqlConnection is not empty then sqlConnectionQueue.push(mySqlConnection): end圖4.6數(shù)據(jù)庫連接RAH類的構造函數(shù)和析構函數(shù) 4. 6解析HTTP請求報文4. 6.1 HTTP請求報文解析流程根據(jù)圖3. 8可知HTTP解析主要分為三局部:解析請求行、解析請求頭部、解析請求體 o請求行主要解析請求報文的請求方
27、法、URI、HTTP協(xié)議版本,請求頭部主要解析關于連接和報文的各種信息,請求體主要解析某些報文的參數(shù) (POST報文)。解析請求體前逐行解析行、請求頭部。本工程HTTP請求報文解析模塊從HTTP請求中解析得到的信息 ,包含:請求方法,URL協(xié)議版本,連接類型 (Connection,表示短連接或長連接),長連接信息(keep-alive)請求體編碼方式,請求體長度,表單參數(shù)(用 戶名、密碼)。 4. 6. 2解析請求行 請求行由請求方法、URI和HTTP協(xié)議版本組成,以空格分隔,整行格式為“請求方法URI HTTP/協(xié)議版本”。請 求行可以通過正那么表達式進行匹配。由于請求行的三個元
28、素由空格隔開,并且版本協(xié)議前帶有“HTTP/",所以匹配 正那么表達式為: 乂廠]*)(廠]*) HTTP/(1]*)$通過正那么表達式匹配可得到請求報文的請求方法、URI和HTTP協(xié)議版本。URI作為 唯一資源標識符,本工程可以通過URI和自定義的“URI-資源路徑”映射表得知請求資源的路徑。 4. 6. 3解析請求頭部 請求頭部包含許多連接信息,以鍵值對的形式表示,每個鍵值對占一行。STL提供了以鍵值對形式存儲的模板 類:map、unordered_mapo其中map底層由紅黑樹實現(xiàn),unorderedjnap底層由哈希表實現(xiàn)。由于HTTP請求頭部中的 信息無順序意義,因此本工程使用
29、時間復雜度更低的unorderecUnap存儲請求頭部信息。 請求頭部中的鍵值對以冒號和空格(“:”)進行分割,因此可以使用正那么表達式對鍵值對進行匹配。匹配請 求頭部信息的正那么表達式: ”[:]*): ?(.*)$如果匹配失敗,說明解析到了空白行,結束解析請求頭部。解析請求體 請求體的長度和編碼方式在解析請求首部中得知,鍵的名稱分別為"Content-Length"和"Content-Type' o 本工程對編碼方式為uapplication/x-www-form-(JRIencodeclv (大多數(shù)主流瀏覽器提交表單的默認編碼方式)的 POST請求進行解析。 “applicati
30、on/x-www-form-URIencoded”的數(shù)據(jù)參數(shù)是鍵值對,表現(xiàn)形式為“key=value”,因此可以使用 unorderedjnap存儲鍵值對參數(shù)。多個參數(shù)之間由別離。編碼規(guī)那么為:參數(shù)中的空格使用“ + ”替換,非數(shù)字、 字母的特殊字符使用三個字符“%xy”替換,其中“xy”表示該字符的十六進制表示形式。 請求體的整體解析過程:根據(jù)"app 1 i cat i on/x-w wf orm-UR I encoded v編碼方式對請求體進行反編碼,別離鍵 值對參數(shù)并存入哈希表中。判斷請求報文是否請求注冊或登錄,如果是那么根據(jù)相應的參數(shù)判斷請求是否合法,如參 數(shù)中否含有用戶名、用戶密
31、碼。 本工程的登陸與注冊校驗過程由HTTP解析模塊完成??蛻舳送ㄟ^POST請求報文提交表單,用戶名和用戶密碼存 放在請求體中。服務器解析post請求消息并獲取用戶名和密碼,然后通過數(shù)據(jù)庫連接訪問數(shù)據(jù)庫,并查詢用戶名和 密碼是否合法。 HTTP請求體解析過程如圖4. 7所示。 Algorithm 7: Parse Postif method = POST and Content-Type = application/x-www-form-urlencoded thenEncoded (Body); if Register or Login thenif username and pas
32、sword are legal then path = welcome; elsepath = error; endend end圖4.7解析HTTP請求體 如果請求報文是登錄或注冊請求,那么返回給客戶端的資源的路徑由用戶名、用戶密碼檢查結 果決定:如果信息 正確,資源路徑為成功頁面的路徑;如果信息錯誤,那么資源路徑為錯誤頁面的路徑。 4. 7生成HTTP響應報文4. 7.1 HTTP響應報文生成流程響應報文生成模塊根據(jù)解析模塊的結果,生成響應行、響應頭部、空白行和響應體。 響應行主耍生成狀態(tài)碼; 響應頭部主要生成長連接狀態(tài)、響應體長度;響應體為根據(jù)解析結果得到的資源文件。本工程的
33、HTTP響應報文生成 模塊中,因為每個連接的報文信息都可能各不相同,因此響應行、響應頭部、空白行存儲在各個連接的響應報文緩沖區(qū)中。為了加快發(fā)送報文時對資源文件的讀取速率,響應體對應的資源文件通過 內(nèi)存映射存放到內(nèi)存中。“內(nèi)存映射 文件提供了一種獨特的內(nèi)存管理特征,它允許應用程序與通過指針訪問動態(tài)內(nèi)存相同的方式訪問磁盤上的文件。因 此,可以在進程地址空間中將磁盤上的文件局部或者全部映射到特定地址范圍,然后通過指針就可以訪問內(nèi)存映射 文件中的內(nèi)容。一旦該文件被映射,就可以訪問它,就像整個文件已經(jīng)加載內(nèi)存一樣,從而可以不必對文件執(zhí)行I/O 操作,這對大數(shù)據(jù)量文件來說存取效率較高[24]。” 圖4
34、. 8響應報文存儲關系 4.7. 2生成狀態(tài)碼 根據(jù)圖3. 9可知,能夠成功請求資源的請求報文需要經(jīng)過三次檢查:成功解析請求報文、客戶端請求的資源合 法、客戶端擁有足夠權限訪問資源。每個階段出錯都會生成相應的狀態(tài)碼,只有三次檢查都通過才會生成表示成功 體框架,包括框架中的事件別離器、事件處理器;其中事件處理器是核心模塊,重點分析了其HTTP請求報文解析模 塊和HTTP響應報文生成模塊;還分析了日志系統(tǒng)的同步模式和異步模式。在完成需求分析后,對服務器的各個模塊 進行設計和實現(xiàn),包括阻塞隊列、日志系統(tǒng)、線程池、數(shù)據(jù)庫連接池、HTTP請求報文解析、HTTP響應報文生成以及 管控HTTP長連接的定
35、時器。各模塊通過面向?qū)ο蟮乃枷雽崿F(xiàn)了相應的功能接口,降低了模塊間的耦合度。最后,本 文對服務器進行了壓力測試和穩(wěn)定性測試,并對應用層面的注冊功能、登錄功能和大文件傳輸功能進行了測試。本 文涉及了Socket的監(jiān)聽與連接、網(wǎng)絡I/O管理、線程池設計和HTTP協(xié)議分析,對解決Web服務器中遇到的問題具有很 好的工程參考價值。 關鍵詞:Linux, C++,高并發(fā),epoll, Reactor Abstract With the rapid development of information technology, the Internet has become an indispensabl
36、e part of social production and life. Especially in the past decade, due to the progress of mobile technology and the popularity of intelligent mobile devices, the number of Internet users is growing explosively, and more and more industries are changing through the Internet. The development of user
37、 groups has changed the form of Internet services, more and more servers need to provide services for a large number of customers at the same time. Therefore, how to design the communication framework of the server and how to effectively use the system resources of the server have become the focus o
38、f server developers. Web application has the characteristics of short connection interval, strong burst and short response time. It is a service with high concurrency requirements. In this thesis, we choose the mainstream Linux operating system in the server system, use C++, combine socket, I/O mul
39、tiplexing technology epoll, non blocking I/O, thread pool, timer and Reactor event processing mode, design and implement a stable and high concurrent web server. This thesis first introduces and analyzes the related technologies of web server, including socket, epoll, Reactor event processing mode,
40、 thread pool and database connection pool; the differences between epoll and other I/O models and the characteristics of epoll model are discussed. Secondly, according to the Reactor mode, the overall framework of the server is analyzed, including the event separator and event processor in the frame
41、work; the event processor is the core module, focusing on the analysis of its HTTP request message parsing module and HTTP response message generation module; the synchronous mode and asynchronous mode of the log system are also analyzed. After the completion of the requirements analysis, the design
42、 and implementation of each module of the server, including blocking queue, log system, thread pool, database connection pool, HTTP request message parsing, HTTP response message generation and the timer to control the HTTP long connection. Each module realizes the corresponding function interface t
43、hrough the idea of object-oriented, which reduces the coupling degree between modules. Finally, this thesis tests the pressure and stability of the server, and tests the registration function, login function and large file transfer function of the application level. This thesis involves socket monit
44、oring and connection, network I/O management, thread pool design and HTTP protocol analysis, which has good engineering reference value to solve the problems encountered in web server. Key words: Linux, C++, high-concurrency, epoll, Reactor目錄 1緒論.12開發(fā)技術介紹 2 2. 1 Socket,22.2 I/O多路復用技術EPOLL.4 1. 2
45、.1 五種 I/O 模型 ..42. 2 EPOLL4 2. 3 Reactor事件處理模式...7的狀態(tài)碼“200”。能否成功解析請求報文是由請求報文解析模塊進行判斷的。如果HTTP請求報文格式出錯(如請 求行格式出 錯),那么狀態(tài)碼設置為“400”。該階段檢查的“出錯”僅表示請求報文格式出錯,而登錄或注冊請求中的用戶信息 出錯不屬于該階段的檢查范圍,僅屬于應用層面上的出錯。所以用戶信息錯誤的登錄或注冊請求也是一次成功的請 求,服務器發(fā)送的響應報文依然是狀態(tài)碼為“200”的成功響應報文,只是頁面資源是表示用戶信息出錯的頁面,而 不是成功頁面。 客戶端請求資源合法表示客戶端請求的資源應
46、該存在并且是有效資源,不能是某個文件夾??蛻舳藫碛凶銐驒嘞拊L 問資源表示客戶端請求的資源在服務器的權限是中是“其它用戶可讀"。Linux中的文件權限分為“可讀”、“可寫”、“可執(zhí)行”,本工程主要檢查客戶端是否有權限讀資源,在開發(fā)中使用SJR0TH (表 示其他用戶可讀的權限值,與文件的權限值進行與運算可知文件能否被其他用戶讀)對資源文件的權限進行檢查。 生成狀態(tài)碼的偽代碼如圖4. 9所示。 Algorithm 8: CreateStatusCode Output: Status Code if Parse Post = FALSE thenStatus Code = 400; endels
47、e if resource does not exist or resource is a folder then Status Code = 404;end else if resource is not readable thenStatus Code = 403; endelse Status Code = 200;end 圖4.9生成狀態(tài)碼的偽代碼生成響應行、響應頭部、空白行 本工程定義了 “狀態(tài)碼-狀態(tài)碼描述”映射表,生成響應行時向HTTP響應報文緩沖區(qū)寫入?yún)f(xié)議版本、狀態(tài)碼、狀 態(tài)條件。 本工程定義了 “文件類型-MIME類型”映射表,生成響應頭部時根據(jù)資源的文件類型向
48、HTTP響應報文緩沖區(qū)寫入 資源的MIME類型參數(shù)(Content-Type) o同時,根據(jù)連接類型向緩沖區(qū)寫入連接類型參數(shù)(Connection),如果連 接類型為長連接還需寫入長連接參數(shù)(keep-alive) o長連接類型參數(shù)的值為timeout和max,表示連接生存時間和 最大請求數(shù)。最后寫入響應體長度參數(shù)(Content-Length)和空白行。 4. 7.4生成響應體目標資源文件通過內(nèi)存映射被映射到內(nèi)存中。服務器向客戶端發(fā)送響應報時文從映射對象中讀取 數(shù)據(jù)并寫入Socketo為了防止程序影響原文件,內(nèi)存映射類型應設為MAP_PRIVATE。這種類型表示程序?qū)?nèi)存映射中的映射區(qū)進
49、行修改時會執(zhí)行“寫時拷貝”獲得一份私有的文件拷貝,對文件的修改都是對這篇私有內(nèi)存的修改,不寫回原文件 O4. 8定時器 本工程通過定時器對HTTP長連接進行管控。服務器在初始化時定義連接的生存時間,接收到請求報文后為連接 設置定時器。 定時器使用小頂堆實現(xiàn),每個節(jié)點存儲某個連接的定時器信息。定時器信息包括Socket的文件描述符、超時時 間點、回調(diào)函數(shù),其中超時時間點為定時器設置時間加上連接的生存時間,回調(diào)函數(shù)為關閉連接函數(shù)。各個信息的 作用是:主線程設置或重置連接的定時器時根據(jù)Socket的文件描述符找到指定的定時器節(jié)點;主線程根據(jù)定時器的 超時時間點判斷該連接是否超時,如果超時那么調(diào)
50、用回調(diào)函數(shù)關閉連接。 小頂堆的底層數(shù)據(jù)結構是數(shù)組,根據(jù)節(jié)點的超時時間點排序建堆。主線程在重置定時器時需要根據(jù)文件描述符 找到定時器節(jié)點,因此還需要定義一個“文件描述符-節(jié)點下標”映射表,可以使用unorderedjnap實現(xiàn)。 新增連接時判斷堆中是否存在含有該Socket文件描述符的節(jié)點。如果不存在,主線程向堆尾中插入新的定時器 節(jié)點,并根據(jù)超時時間點對節(jié)點向上調(diào)整;如果存在,說明之前使用該文件描述符的連接雖然已經(jīng)被關閉但目前事 件還到達該連接的超時時間點,主線程重置該節(jié)點的超時時間點,并對節(jié)點向下調(diào)整。 某個連接有新的事件到達,主線程重置該連接定時器的超時時間點:通過文件描述符和“文件
51、描述符-節(jié)點下 標”的映射表找到定時器節(jié)點的下標,修改定時器節(jié)點的超時時間點并對節(jié)點向下調(diào)整。 調(diào)整過程中定時器需要維護“文件描述符-節(jié)點下標”的映射表。向上調(diào)整如圖4. 10所示,向下調(diào)整如圖4. 11所/Js o 圖4.10定時器節(jié)點向上調(diào)整 國弱陸禁漁IIR延您腐 圖4.11定時器節(jié)點向下調(diào)整清除超時節(jié)點只需要判斷根節(jié)點是否超時,如果超時那么刪除根節(jié)點,調(diào)整堆:交換根 節(jié)點與最后一個節(jié)點,刪 除最后節(jié)點并對新的根節(jié)點向下調(diào)整。主線程不斷清除直到根節(jié)點不超時或堆為空。對于連接已經(jīng)關閉的節(jié)點無需 管理。因為如果有新的連接使用該節(jié)點的文件描述符,該節(jié)點會析構舊連接的信息,存儲新連
52、接的信息,并調(diào)整節(jié) 點位置;如果時間到達了舊連接的超時時間點,該節(jié)點會與其它超時節(jié)點一起被清除。 為了能夠有效地對超時連接進行關閉,當堆不為空時主線程應有限地等待到達事件,并且等待時間為定時器根 節(jié)點的超時時間點減去當前時間。在此期間內(nèi)如果沒有事件到達那么根節(jié)點對應的連接一定超時,需要關閉。 |清輻時節(jié)點|—"葉e噩;鬻|-0Y1 斐斐二處是,重置定時弱| 圖4. 12堆不為空時主線程有限等待4. 9服務器整體流程 4. 9.1服務器初始化服務器啟動階段需要初始化一些數(shù)據(jù):偵聽端口號、epoll模式、連接生存時間、優(yōu)雅退出、 數(shù)據(jù)庫信息(數(shù)據(jù)庫用戶名、密碼、數(shù)據(jù)庫名、數(shù)據(jù)庫訪問端口)、
53、工作線程數(shù)量、數(shù)據(jù)庫連接數(shù)量、日志開關、日志等級、日志模 式。 偵聽端口號:客戶端根據(jù)服務器的IP地址和偵聽端口號發(fā)起TCP連接,服務器通過偵聽端口號接收客戶端請求。 epoll模式:決定服務器使用LT模式還是ET模式。 連接生存時間:決定一個空閑的連接可以維持多長時間。 優(yōu)雅退出:決定服務器關閉連接時是否優(yōu)雅退出。數(shù)據(jù)庫信息:用于初始化數(shù)據(jù)庫連接。日志開關:決定服務器是 否使用日志系統(tǒng)。日志等級:決定服務器翻開的日志系統(tǒng) 的等級。日志模式:決定日志系統(tǒng)使用同步模式還是異步 模式。服務器運行主線程流程如圖4. 13所示。 error圖4. 13主線程流程工作線程 流程如圖4.
54、14所示。 圖4. 14工作線程流程 5服務器測試測試環(huán)境 服務器系統(tǒng): ubuntul8. 04服務器處理器內(nèi)核總數(shù):4服務器內(nèi)存:8GB 5.1 測試工具本工程使用WebBench對服務器進行壓力測試和穩(wěn) 定測試[25]。 WebBench是VeriTest公司開發(fā)的一款測試工具,其原理是通過父進程fork多個子進程,由子進程對目標URI發(fā)起 實際請求并記錄請求結果。子進程把結果返回給父進程,由父進程整理和統(tǒng)計子進程記錄的信息,反應于用戶。用 戶能夠通過WebBench向目標Web服務器發(fā)起一定數(shù)量的并發(fā)請求,并發(fā)連接數(shù)和持續(xù)時間可由用戶決定,但并發(fā)數(shù)量 不能超過三萬。 5
55、.2 壓力測試和穩(wěn)定測試的服務器參數(shù)關閉方式:優(yōu) 雅退出。 線程數(shù)量:6O日志系統(tǒng):關 閉。 epoll模式:偵聽Socket設為LT模式,連接Socket分LT模式和ET模式進行測試。 5.3 壓力測試 單次測試時長為5秒,并發(fā)連接數(shù)從1000開始,以2000為步長進行增長,直到服務器支持上萬并發(fā)。LT模式、ET 模式各進行十次測試,測試結果見附錄。結果平均值如表5.1、5.2所示。 表5. 1 LT模式壓力測試 并發(fā)連接數(shù) 數(shù)據(jù)傳輸速率bytes/s 成功次數(shù) 每秒成功次數(shù) 1000 5464886. 4 202652.2 6754. 6 3000 44812
56、09.4 214422.6 7146.8 5000 4441323.4 221291.7 7375. 9 7000 4556732 229431. 5 7647. 3 9000 4461308.4 227311 7586. 6 11000 4292933.3 222241.8 7407. 7 表5. 2 ET模式壓力測試 并發(fā)連接數(shù) 數(shù)據(jù)傳輸速率bytes/s 成功次數(shù) 每秒成功次數(shù) 1000 5368129.2 199063. 8 6635 3000 4447417. 8 213078.5 7104. 7 5000 43648
57、42. 2 215823.2 7193. 7 7000 4424853. 2 223747. 1 7457. 8 9000 4196370. 8 215830.3 7193.9 11000 4193782. 7 218356.8 7278. 1 從表中可知LT模式和ET模式均能支持上萬并發(fā),并且每秒處理的連接請求數(shù)(qps)約為7000。 5.4 穩(wěn)定測試測試時長為10分鐘,并發(fā)連接數(shù)11000。對ET模式、LT模式各進行3次測試,測試結 果如下。 11000 clients, running 600 sec. Speed=452658 pages/min,
58、 -2733954 bytes/sec. Requests: 4496798 susceed, 29787 failed. 圖5.1 LT模式第一次穩(wěn)定測試11000 clients, running 600 sec. Speed=458109 pages/min, -2694015 bytes/sec. Requests: 4551932 susceed, 29163 failed. 圖5. 2 LT模式第二次穩(wěn)定測試11000 clients, running 600 sec. Speed=443065 pages/min, -2833884 bytes/sec. Reque
59、sts: 4400922 susceed, 29737 failed. 圖5. 3 LT模式第三次穩(wěn)定測試11000 clients, running 600 sec. Speed=415562 pages/min, -3129087 bytes/sec. Requests: 4126594 susceed, 29035 failed. 圖5. 4 ET模式第一次穩(wěn)定測試11000 clients, running 600 sec. Speed=424722 pages/min, -3033192 bytes/sec. Requests: 4218901 susceed, 2832
60、2 failed. 圖5. 5 ET模式第二次穩(wěn)定測試11000 clients, running 600 sec. Speed=422485 pages/min, -3066281 bytes/sec. Requests: 4196183 susceed, 28669 failed. 圖5.6 ET模式第三次穩(wěn)定測試根據(jù)測試結果可知服務器能在處理上萬并發(fā)連接的情況下保持服務器穩(wěn)定,每秒處理 的連接請求數(shù)和壓力測試結果一樣約為7000。測試結果中的每秒字節(jié)數(shù)傳輸速率為負是因為數(shù)據(jù)過大造成了整型溢出。 5.5 注冊、登錄功能測試用戶表初始為空。 rrysql> select * fr
61、om user; Empty set (0.00 sec) 圖5.7用戶表初始內(nèi)容注冊 用戶。 C O A Not secure 192.168.3.1... ☆* y : 首頁 圖5.8首頁 圖5. 9注冊 圖5.10注冊成功,進入功能界面 mysql> select * from user;+++ | username|password|+++ | 123456|123456|+++ 1 row inset(0.00 sec)圖5.11數(shù)據(jù)庫存入用戶名、用戶密碼 登錄需要對用 戶名、用戶密碼進行校驗。 <--> C O A Not secure 192
62、.16...所 ☆★ y登錄 用戶名 用戶密碼確定 用戶名、用戶密碼錯誤=== 圖5. 12用戶信息錯誤,返回錯誤信息 注冊對用戶名 進行校驗,防止重新注冊。 圖5. 13用戶名被注冊 5. 7文件測試服務器讀取內(nèi)存映射區(qū)中的文件數(shù)據(jù),作為響應體發(fā)送給客戶端。 S 今 C 。 A Not secure 192.1683.129:2333/... d ☆. ■* n y 圖5.14視頻測試 192.168.3... R ☆ 192.168.3... R ☆ <--? C 6 A Not 圖5. 15圖片測試 6課題總結 本文主要介紹了Linux環(huán)境下關于C++服
63、務器開發(fā)的相關技術,分析了服務器的整體需求,并設計了一個結合 Reactor模式、epoll、非阻塞I/O以及線程池的高并發(fā)Web服務器。其中主要對服務器的高并發(fā)相關技術進行了詳細 探討:epoll模式選擇、阻塞隊列實現(xiàn)原理、主線程與線程池的半同步/半異步模型、日志系統(tǒng)的同步/異步模式。 通過實驗測試,可以得知本文設計的服務器在主流PC中運行可以支持上萬并發(fā),并且可以保證服務器穩(wěn)定運行。 同時,本文設計的服務器還支持用戶登錄、注冊功能。工作線程通過數(shù)據(jù)庫連接池和RAH機制完成多線程模式 下對數(shù)據(jù)庫的有序訪問,并對客戶端的用戶名、用戶密碼進行校驗。對于文件傳輸,服務器通過內(nèi)存映射把資源文 件拷
64、貝進內(nèi)存,減少了程序?qū)ξ募x取的I/O操作,加快了文件傳輸速率。對于長連接,服務器通過小頂堆實現(xiàn)了定 時器,完成了對超時連接的主動釋放。 本文設計的服務器雖然保證了高并發(fā)場景下的穩(wěn)定運行,但依然存在一些應用場景問題。本文設計的服務器處 理的請求都是無狀態(tài)的,因此登錄功能會失去實際意義。有狀態(tài)場景需要服務器生成session并向客戶端返回 cookie, cookie的生成和session的保存在高并發(fā)場景下會變成一個新的難題。我將在后續(xù)工作中繼續(xù)改進,為服務 器的可用性提供更多支持。 參考文獻 [1]中國互聯(lián)網(wǎng)絡信息中心.第46次中國互聯(lián)網(wǎng)絡開展狀況統(tǒng)計報告[R].北京:中共中央網(wǎng)絡平安
65、和信息化委員會 辦公室,中華人民共和國國家互聯(lián)網(wǎng)信息辦公室,2020. [2]周炎濤,李立明.TCP/IP協(xié)議下網(wǎng)絡編程技術及其實現(xiàn)[J].航空計算技術,2002(03):126-128+132. [3]W. RichardStevens, BillFenner, AndrewM. Rudoff. UNIX網(wǎng)絡編程,第 1 卷,套接口API [M].清華大學出版 社,2006. [4]W. RICHARDSTEVENS. TCP/IP詳解.卷1,協(xié)議機械工業(yè)出版社,2000. [5]MichaelKerrisk,凱利斯克,孫劍,等.Linux/UNIX系統(tǒng)編程手冊[M].人民郵電出版社
66、,2014. [6]胥 光輝,徐永森.同步阻塞線程的喚醒問題研究[J].計算機科學,2002, 29(012):49-50. [7] Gammo L , Brecht T , Shukla A , et al. Comparing and Evaluating epoll, select, and poll Event Mechanisms. Proceedings of Annual Linux Symposium, 2004. [8]Linux下基于EPOLL機制的海量網(wǎng)絡信息處理模型[J].強激光與粒子束,2013, 25(Ozl):46-50. [9]游 雙.Linux高性能服務器編程[加.機械工業(yè)出版社,2013. [10] Schmidt D C . Reactor - An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events. 1999. [11]李璞,張玲,胡術,等.多線程環(huán)境下Reactor模式的研究與實現(xiàn)[J].網(wǎng)絡新媒體技術
- 溫馨提示:
1: 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
2: 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
3.本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
5. 裝配圖網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。