點 擊 關 注 上?方"兩猿社"
設 為"置 頂 或 星 標",干 貨 第 一?時 間 送 達。
互 聯 網 猿 | 兩 猿 社
TineyWebServer
Linux下C++輕量級Web服務器,助力初學者快速實踐網絡編程,搭建屬于自己的服務器.
使用線程池 + epoll(ET和LT均實現) + 模擬Proactor模式并發模型
使用狀態機解析HTTP請求報文,支持解析GET和POST請求
通過訪問服務器數據庫實現web端用戶注冊、登錄功能,可以請求服務器圖片和視頻文件
實現同步/異步日志系統,記錄服務器運行狀態
經Webbench壓力測試可以實現上萬的并發連接數據交換
框架
項目框架主要分為I/O處理單元、邏輯處理單元和存儲單元三個模塊
I/O處理單元和邏輯處理單元對應半同步/半反應堆線程池
邏輯處理單元和存儲單元對應數據庫連接池和日志系統
半同步/半反應堆線程池將web端和服務器端建立通信
實現http請求報文的處理與響應
定時器完成非活動連接的處理
數據庫連接池避免頻繁訪問數據庫,實現登錄和校驗功能
日志系統實現同步和異步兩種方式記錄服務器運行狀態
工作流程
以一個請求到來具體的處理過程介紹項目工作流程,具體包括web端和服務器建立連接,訪問服務器數據庫完成登錄和注冊,并通過定時器完成非活動連接的處理,最后服務器運行狀態通過日志系統進行記錄。
web端和服務器端建立連接
采用epoll的邊緣觸發模式同時監聽多個文件描述符,采用同步I/O模擬proactor模式處理事件,主線程負責監聽客戶端是否發起請求
當web端發起http請求時,主線程接收請求報文,然后將任務插入請求隊列,由工作線程通過競爭從請求隊列中獲取任務
通過http類中的主從狀態機對請求報文進行分析,根據請求報文對客戶端進行http響應,然后由主線程給客戶端發送響應報文。
連接數據庫
單例模式創建數據庫連接池,避免頻繁建立連接,用于后續web端登錄和注冊校驗訪問服務器數據庫
實現web端的登錄和注冊
web訪問的歡迎界面為GET請求,登錄和注冊界面是POST請求。
歡迎界面有新用戶(0)和已有賬號(1)兩個選項,若選擇新用戶,會跳轉注冊(3)界面,注冊成功或選擇已有賬號,跳轉登錄(2)界面,注冊或登錄失敗會提示失敗,成功和失敗為0,1
同步/異步日志系統,記錄服務器運行狀態
同步的方式下,工作線程直接寫入日志文件
異步會另外創建一個寫線程,工作線程將要寫的內容push進請求隊列,通過寫線程寫入文件
日志文件支持按日期分類,和超過最大行數自動創建新文件
非活動連接的處理
由于非活躍連接占用了連接資源,嚴重影響服務器的性能,通過實現一個服務器定時器,處理這種非活躍連接,釋放連接資源。
利用alarm函數周期性地觸發SIGALRM信號,該信號的信號處理函數利用管道通知主循環執行定時器鏈表上的定時任務.
Demo
注冊演示
登錄演示
請求圖片文件演示(6M)
請求視頻文件演示(39M)
壓力測試
Webbench對服務器進行壓力測試,在ET非阻塞和LT阻塞模式下均可實現上萬的并發連接.
ET非阻塞
LT阻塞
并發連接總數:10500
訪問服務器時間:5s
所有訪問均成功
注意:?使用本項目的webbench進行壓測時,若報錯顯示webbench命令找不到,將可執行文件webbench刪除后,重新編譯即可。
基礎測試
服務器測試環境
Ubuntu版本16.04
MySQL版本5.7.29
測試前確認已安裝MySQL數據庫
1//建立yourdb庫
2create?database?yourdb?set?utf8;
3
4//創建user表
5USE?yourdb;
6CREATE?TABLE?user( 7????username?char(50)?NULL, 8????passwd?char(50)?NULL 9)ENGINE=InnoDB;
10
11//添加數據
12INSERT?INTO?user(username,?passwd)?VALUES('name',?'passwd');修改main.c中的數據庫初始化信息
1//root?root為服務器數據庫的登錄名和密碼
2connection_pool?*connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5);修改http_conn.cpp中的root路徑
1const?char*?doc_root="/home/qgy/TinyWebServer/root";
生成server
1make?server
啟動server
1./server?port
瀏覽器端
1ip:port
個性化測試
個性化測試分為三種情況,分別是校驗方式、I/O復用方式、日志寫入方式。
校驗方式
選擇任一校驗方式,代碼中使用同步校驗,可以修改為CGI.
同步線程數據庫校驗
關閉main.c中CGISQLPOOL,打開SYNSQL
123?#define?SYNSQL????//同步數據庫校驗
224?//#define?CGISQLPOOL??//CGI數據庫校驗關閉http_conn.cpp中兩種CGI,打開SYNSQL
17?//同步校驗
28?#define?SYNSQL
3
410?//CGI多進程使用鏈接池
511?//#define?CGISQLPOOL
6
713?//CGI多進程不用連接池
814?//#define?CGISQL
CGI多進程數據庫校驗,不使用連接池
關閉main.c中SYNSQL和CGISQLPOOL
123?//#define?SYNSQL????//同步數據庫校驗
224?//#define?CGISQLPOOL??//CGI數據庫校驗關閉http_conn.cpp中SYNSQL和CGISQLPOOL,打開CGISQL
17?//同步校驗
28?//#define?SYNSQL
3
410?//CGI多進程使用鏈接池
511?//#define?CGISQLPOOL
6
713?//CGI多進程不用連接池
814?#define?CGISQL關閉sign.cpp中的CGISQLPOOL,打開CGISQL
112?#define?CGISQL????//不使用連接池
213?//#define?CGISQLPOOL??//使用連接池修改sign.cpp中的數據庫初始化信息
1//root?root為服務器數據庫的登錄名和密碼
2connection_pool?*connPool=connection_pool::GetInstance("localhost","root","root","yourdb",3306,5);生成CGISQL.cgi
1make?CGISQL.cgi
CGI多進程數據庫校驗,使用連接池
關閉main.c中SYNSQL,打開CGISQLPOOL
123?//#define?SYNSQL????//同步數據庫校驗
224?#define?CGISQLPOOL??//CGI數據庫校驗關閉http_conn.cpp中SYNSQL和CGISQL,打開CGISQLPOOL
17?//同步校驗
28?//#define?SYNSQL
3
410?//CGI多進程使用鏈接池
511?#define?CGISQLPOOL
6
713?//CGI多進程不用連接池
814?//#define?CGISQL關閉sign.cpp中的CGISQL,打開CGISQLPOOL
112?//#define?CGISQL????//不使用連接池
213?#define?CGISQLPOOL??//使用連接池生成CGISQL.cgi
1make?CGISQL.cgi
I/O復用方式
選擇任一I/O復用方式,代碼中使用LT阻塞,可以修改為ET非阻塞.
LT阻塞
關閉main.c中ET,打開LT
128?//#define?ET???????//邊緣觸發非阻塞
229?#define?LT?????????//水平觸發阻塞關閉http_conn.cpp中ET,打開LT
116?//#define?ET???????//邊緣觸發非阻塞
217?#define?LT?????????//水平觸發阻塞
ET非阻塞
關閉main.c中LT,打開ET
128?#define?ET?????????//邊緣觸發非阻塞
229?//#define?LT???????//水平觸發阻塞關閉http_conn.cpp中LT,打開ET
116?#define?ET???????//邊緣觸發非阻塞
217?//#define?LT?????????//水平觸發阻塞
日志寫入方式
選擇任一日志方式,代碼中使用同步日志,可以修改為異步寫入.
同步寫入日志
關閉main.c中ASYNLOG,打開同步寫入SYNLOG
125?#define?SYNLOG?//同步寫日志
226?//#define?ASYNLOG???/異步寫日志
異步寫入日志
關閉main.c中SYNLOG,打開異步寫入ASYNLOG
125?//#define?SYNLOG?//同步寫日志
226?#define?ASYNLOG???/異步寫日志
選擇數據庫訪問、I/O復用方式或日志寫入方式后,按照前述生成server,啟動server,即可進行測試.
致謝
Linux高性能服務器編程,游雙著.
如果本文對你有幫助,閱讀原文
star一下服務器項目,我們需要你的星星^_^.
完。