C++服務器設計(零):總體設計


  這個系列把畢業論文的部分貼了出來,以作保存留念。整個系列分為三大部分,其中第一章到第三章是介紹服務器的系統層設計,設計思路參考了libevent和muduo等開源代碼的實現;第四章到第六章是介紹服務器的服務層設計,設計思路參考了自己的Khala實現;第七章介紹了如何利用該服務器框架實現一款類似於QQ的聊天系統。全文主要參考了陳碩的《Linux多線程服務端編程》、《Unix網絡編程卷1》。

系統簡介

  本系統是用C++設計實現的TCP網絡服務器框架。該系統底層I/O部分采用基於Reactor模式的非阻塞模型,線程部分采用event loop+ 線程池模型。系統的服務層具有超時檢測管理、多類型設備和多事件消息管理,以及連接生命周期管理的功能。同時整個系統通過封裝實現了網絡實現與業務邏輯相分離的多線程網絡框架。

  該框架面向網絡業務開發者,對整個網絡服務端的開發過程提供了更高層的封裝,能夠大大降低業務開發難度。通過該框架,開發者無需關心底層網絡I/O及多線程下並發連接的實現,只需根據具體業務所屬設備類型編寫業務邏輯作為消息響應事件,並可以選擇使用服務層提供的相關服務作為業務支持。

  該框架適用於局域網內基於TCP長連接的業務場景。比如智能家居下的多設備管理,局域網內的多用戶聊天工具等。在這些場景中,通過使用本系統框架,開發者只需編寫少數業務相關的代碼即可完成服務端功能的實現。

系統需求分析

  本服務器系統主要從高效和易用兩個方面作為設計的核心。

  高效是指合理設計系統底層的I/O和線程模型,充分利用CPU和內存資源,達到盡可能的高連接和高並發的要求。此處只考慮服務器硬件性能的利用,並不考慮以太網帶寬的限制。

  易用是從兩個方面出發進行考慮。第一是將網絡細節與業務邏輯相分離,使該框架的開發者從底層瑣碎細節中解脫出來,只需專注於業務邏輯的編寫;第二是提供一層服務層,能為對於連接超時、多設備及生命周期支持等特定場合下有特殊需求的開發者提供服務支持。

  本服務器系統的基本需求分析如下:

  • l  環境需求:本系統只考慮在安全可控的局域網內部使用,網絡數據采用明文傳輸,局域網內客戶機可能會因為網絡故障或死機等異常導致與服務器斷開。但是並不考慮服務器可能被某些用戶惡意攻擊的場景。因此本系統並不為安全性做特別的增強。在公網環境上使用本服務器系統是不安全的。
  • l  操作系統需求:由於服務器系統涉及大量與底層操作系統交互的過程,如I/O及網絡的處理,而不同操作系統的系統調用並不完全相同。比如在I/O復用上,雖然大多數系統均提供了select作為系統調用,但是該調用有着O(n)的時間復雜度,效率低下。因此不同系統各自實現了不同的類似系統調用作為優化,比如Linux系統下提供了epoll,BSD系統下提供了kqueue,Windows下提供了IOCP。雖然可以通過適配器模式對這些系統特性進行封裝,抽象出一層跨平台的統一接口,但這將大大增加整個設計實現的工作量,同時這也不是本系統設計的重點。因此本服務器系統只支持X86-64下的Linux,不考慮可移植性,不支持Windows等其他平台。
  • l  網絡需求:由於服務器系統涉及基於TCP/IP協議的網絡編程過程,而在TCP/IP中又存在多種選擇。在傳輸層中,存在TCP和UDP兩種協議,其中TCP是可靠的字節流協議,而UDP是不可靠的數據報協議。由於本服務器系統大多運用於局域網內,且原生業務中並無類似音頻和多媒體應用等大數據量的傳輸要求,同時由於對於連接的生命周期的設計將是系統設計的重點。因此我們優先采用基於可靠性和有序性、面向連接的TCP協議,並不支持UDP協議。同時由於開發時間及成本的關系,本系統只目前支持IPv4,不考慮兼容IPv6。
  • l  性能需求:作為服務器系統應該能夠充分利用CPU和內存資源。同時能夠發揮多核處理器的效能,原生支持多核多線程,並保證線程安全,而不像libevent本身只支持單線程,需要后期進行多線程修改。
  • l  數據傳輸需求:TCP協議是一個無邊界的字節流協議,但讀寫數據處理是以一條完整消息為單位的。因此服務器需要能夠區分消息邊界,能夠解決“目前收到的數據暫時不能構成一條完整消息”和“一次收到多條消息的數據”等情況。同時在多線程的環境下需要保證消息的正確和有序,即不能出現一條消息內混入另一條消息的數據,並且每條消息按照接收順序等待被處理。同時以上細節不能暴露給用戶,用戶應該只需關注收到一條完整消息的處理,及每次發送出去一條完整的消息。

 

  同時服務器框架提供了一些特定服務,可以為某些特殊業務的開發提供便利。開發者可以選擇使用其中某些框架服務支持,也可以選擇自己另行實現。這些服務相關的需求分析如下:

  • l  超時檢測:服務器應該能夠檢測長期未發送有效數據的空閑連接。這些連接很有可能是由於機器斷電、網線故障或防火牆導致的的僵死連接。大量的僵死連接將占用服務器資源,影響服務器性能。因此當服務器檢測到這些可能的僵死連接后,應該嘗試通知這些連接,並最終斷開這些僵死連接,釋放它們占用的系統資源。
  • l  多設備管理:不同於傳統服務器僅對消息進行解析回復,而不對消息事件進行歸類管理。本服務器系統應該能夠管理不同連接客戶的設備類型,並能夠對來自不同類型設備的不同消息消息進行權限管理。即某個設備類型的連接無權限請求屬於另一個設備類型的消息事件。而如果某個消息事件同時屬於多個設備類型,每種設備類型的連接請求該消息事件的消息處理機制也不盡相同。用戶可以為系統創建新的設備類型,為該設備類型注冊不同的消息事件,並針對每種消息事件編寫具體的業務處理代碼。
  • l  連接生命周期管理:每個連接的過程存在多個階段,比如連接的建立,登錄的驗證,退出前的處理等。服務器為每個連接的不同階段中預留了不同的接口,並保證這些接口內代碼的線程安全。這些接口由不同設備類型進行管理,用戶可以根據不同的設備類型重寫這些接口,完成對不同設備類型的每個連接在不同階段的下的操作管理。

系統總體架構

  如圖2-1所示,整個服務器框架由系統層、服務層和用戶層組成,運行於Linux操作系統之上。其中系統層和服務層構成框架主體,並通過相關接口提供給用戶層使用。用戶層由框架的使用者依據具體業務進行實現。每層的具體介紹如下。

圖2-1 服務框架軟件架構圖

  系統層是整個服務器系統框架的核心。它與操作系統交互,通過I/O和線程等機制,保證了整個系統的正常高效運行,並提供了對底層系統及網絡等細節的封裝,保證了與上層具體業務邏輯的分離。

  系統層由Reactor模式、多線程模型、連接對象和應用層I/O緩沖四個部分組成。在Reactor模式中,通過Linux下的epoll系統調用實現的非阻塞I/O機制,實現了對不同連接的I/O事件的管理,構成了系統層的主體。同時在多線程模型中,采用了event loop + 線程池機制[39],同時線程間通過任務隊列的形式進行通信,並且保證對於每個連接的整個連接周期中都在一個線程中進行管理。在連接對象部分,對每個連接的描述符等信息和連接相關操作進行了封裝處理。在應用層I/O緩沖中,通過緩沖機制,封裝了非阻塞I/O下的數據收發處理,保證了數據收發下的完整性。

  服務層是在系統層的基礎上進行了進一步封裝,並提供了更多的服務功能。理論上整個框架可以剝離服務層而單獨在系統層上運行,但框架的易用性將大打折扣,同時業務邏輯也將很容易侵入到系統層的代碼中。使用者可以根據具體業務需求選擇使用服務層的某些功能,對不需要的服務進行關閉。因此在實際使用中應該基於服務層提供的接口進行開發工作。

  服務層主要由連接超時管理、多設備類型管理和連接生命周期管理三個部分組成。

  在連接超時管理中,服務層通過心跳機制對每個連接進行計時,客戶端需要每隔一段時間向服務器發送有效數據,通知服務器連接仍然在線。當服務器檢測到某個連接超時時,將會執行超時接口中實現的處理機制,並強制將該連接斷開連接並移除系統。用戶可以設置超時時間,並通過超時接口為不同設備制定不同的超時處理機制。

  在多設備類型管理中,服務層提供了以設備類型為單位制定不同的消息事件,並為不同消息事件實現具體處理的機制。服務層默認實現了臨時設備類型和登錄設備類型這兩種設備類型,並為其制定了和登錄相關的消息事件及處理。

  連接的生命周期管理主要是提供一組和連接的生命周期狀態變化和錯誤處理相關的接口,並以設備類型為單位進行管理。這些生命周期狀態包括連接的建立、登錄、超時及退出等。用戶可以為不同的設備類型的連接制定不同的生命周期接口實現,比如統計每一個新建連接信息,或對不同類型的登錄連接進行驗證等。

圖2-2 用戶創建新的設備類型

  用戶層位於整個服務器系統框架之上,由該框架的使用者根據具體的業務需求進行實現。使用者主要工作為根據連接類型創建新的設備類型對象,如圖2-2所示,並為新的設備類型注冊業務相關的消息事件和處理機制,並根據需求實現該設備類型對應連接的生命周期接口。

  整個服務器框架設計中並不涉及磁盤文件I/O和數據庫操作,而在具體服務端業務編程中通常會涉及數據庫操作。如果需要連接使用數據庫,服務端開發者應該在系統用戶層自行實現或導入第三方的數據庫操作類庫,比如Mysql++庫等,並通過該庫的API實現與后台數據庫的交互操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM