_______. _______.___ ___ .______ ____ ____ _______ .______
/ | / | / / | _ / / | ____|| _
| (----` | (----` V / | |_) | / / | |__ | |_) |
> < | / / | __| | /
.----) | .----) | / . | | ----. / | |____ | | ----.
|_______/ |_______/ /__/ __ | _| `._____| __/ |_______|| _| `._____|
ssxrver 是一個運行於Linux 平台下的高性能高並發網絡庫,使用C++17 進行編寫,支持TCP 和UDP 協議。
請盡量匹配與我相同的開發環境,如果不需要數據庫模塊請對應修改CMakeLists.txt .
cmake 安裝
# debian/ubuntu
sudo apt-get install cmakeboost 庫安裝
wget http://sourceforge.net/projects/boost/files/boost/1.72.0/boost_1_72_0.tar.bz2
tar -xvf boost_1_72_0.tar.bz2
cd ./boost_1_72_0
./bootstrap.sh --prefix=/usr/local
sudo ./b2 install --with=all在ssxrver 目錄下運行./build.sh , 可以修改build.sh 選擇生成Debug 版本還是Release 版本(默認Release 版本)
./build.sh編譯成功會生成build/ 目錄,可執行文件在對應版本的目錄下,比如當你選擇Release 版本,可執行文件就在/build/Release/ssxrver.
模仿conf/ssxrver.json.example 的格式去創建你的配置文件(注意配置文件中不能加註釋,不能加註釋,不能加註釋),以下我對各個配置文件選項做一下解釋,很多參數實際上我設定了默認值.如果不配置的話也不會有影響.
{
" port " : 4507, # 端口号,不填的话默认4507
" address " : " 127.0.0.1 " , # 绑定的地址
" worker_processes " : 4, # IO 线程数量,不填默认为 4 个
" worker_connections " : -1, # 一个 IO 线程最多支持多少连接, -1 表示最多能创建多少就创建多少,不做限制
" task_processes " : 0, # 任务线程,不填的话默认为 0
" cpu_affinity " : " off " , # cpu 亲和度 ,默认关闭
" http " : { # http 模块
" max_body_size " : 67108864, # 单个 http 包最大支持大小
" root_path " : " /home/randylambert/sunshouxun/ssxrver/html/ " # 文件访问根路径
},
" log " : { # log 模块
" level " : " INFO " , # 输出等级,可填三种等级, DEBUG,INFO,WARN 不填默认为 INFO 等级
" ansync_started " : " off " , # 是否打开异步日志线程,不填默认关闭
" flush_second " : 3, # 异步线程每隔多久持久化一次
" roll_size " : 67108864, # 日志文件滚动大小
" path " : " /home/randylambert/sunshouxun/ssxrver/logs/ " , # 日志文件存放路径
" base_name " : " ssxrver " # 日志文件基础名
},
" mysql " : { # 数据库模块
" mysql_started " : " off " , # 是否打开数据库模块,默认关闭
" address " : " 127.0.0.1 " , # 以下是对应数据库连接信息
" user " : " root " ,
" password " : " 123456 " ,
" database_name " : " ttms " ,
" port " : 0,
" unix_socket " : null,
" client_flag " : 0
},
" blocks_ip " : [ " 122.0.0.2 " , " 198.1.2.33 " ] # 可屏蔽部分恶意 IP
}運行可執行文件.
./ssxrver -f /配置文件的路径
# 例如
./build/Release/ssxrver -f ./conf/ssxrver.json| 測試環境 | 數值 |
|---|---|
| 操作系統髮型版本 | deepin v20.1 社區版(1030) |
| 內核版本 | 5.4.70-amd64-desktop (64位) |
| 編譯器版本 | gcc 8.3 |
| boost庫版本 | 1.72 |
| 處理器 | Intel(R) Core(TM) i7-8750H CPU @2.20GHz |
| L1 Cache 大小 | 32K |
| L2 Cache 大小 | 256K |
| L3 Cache 大小 | 9216K |
| 硬盤轉速 | 1.8 TiB 機械硬盤5400轉 |
| 硬盤讀寫速度 | 370 MB in 3.03 seconds = 122.27 MB/sec |
| 內存 | 7.6GB |
| Swap分區 | 4.7GB |
| 邏輯核數 | 12核 |
為控制變量,測試前重啟電腦,保證測試環境沒有高CPU 負載和高IO 負載的其他應用.
測試工具為webbench1.5 ,去掉第一次熱身數據,測試命令如下(100個客戶端持續訪問15秒).
./webbench -c 100 -t 15 http://127.0.0.1:8081/測試對象為Apache/2.4.38 , nginx/1.14.2 , ssxrver.
注: 無論是使用webbench 還是ab ,這種壓測工具測出來的數據只能做一個簡單參考,壓測是一個需要全方位多角度的測試,而不是簡單的運行一條命令而已,甚至在壓測時數據根本沒有經過網絡傳輸,只是在內核裡轉了一圈.
| 網絡庫 | Speed(pages/min) | Requests成功率 |
|---|---|---|
| ssxrver 返回在內存中生成的response | 7107414 | 100% |
| ssxrver 返回靜態文件 | 5114376 | 100% |
| Apache/2.4.28 | 2884072 | 100% |
| nginx/1.14.2 | 4728748 | 100% |
ssxrver 的測試結果還不錯,但是奇怪的是,我本以為數據會更高的,因為在我早期開發的時候,當時我很多優化還並沒有做,返回直接在內存中生成的response 時,測出來最多有接近8000000 pages/min (接近8000000 pages/min 的測試結果沒截圖,留下來一個7550778). 而當時nginx/1.14.2 最多也超過5000000 pages/min , 不過無論是ssxrver 還是nginx/1.14.2 ,現在我怎麼測也測不出來那麼高的值了,我也不清楚是什麼原因,導致最終結果能出現這麼大的差距,(難道是我電腦老化了? ̄□ ̄||)
目前我個人如果有時間的話會修改ssxrver 的Buffer 模塊和Log 模塊.
首先, Buffer 模塊最簡單的改法是將其改成循環buffer ,從而有效的減少Buffer 將數據前移的次數,或者直接放棄這種Buffer 實現,重新實現一種高性能Buffer .
其次,目前的Log 模塊是模擬C++ 的流形式寫的,雖然在性能上肯定比直接用C++ 的iostream 要高,但是重載<< 符號形式的Log 還是會出現格式控制不方便的問題和函數調用鏈引起的性能問題,這兩個問題都可以通過實現printf 形式的Log 來解決.
由於時間原因, ssxrver 並沒有實現內存管理模塊,寫一個通用的高性能內存管理模塊是幾乎不可能的(不如直接上jemalloc 或者tcmalloc),但是,通過分析網絡庫這種場景,寫出一個在這種場景下性能更高的內存管理模塊還是有一點機會的,如果之有時間,我會去看看nginx 中的實現,學習一下.
在我查詢資料的時候我得到了一個結論是在C++ 17 中,可以使用std::string_view 替換const string& ,會有一定的效率提升,因此我嘗試將我項目中全部使用const string& 的地方更換為std::string_view ,但是當我在最終使用perf -top去查看更改後的負載時,意外的發現了部分函數在我使用了std::string_view 替換之後,負載居然提高了,我很疑惑為什麼會出現這種情況,由於時間原因我暫時不去追究這個問題的具體成因了,有機會去看看底層實現去查一下具體原因.
在實現http 解析模塊時,第一版我採用的是直接匹配字符串的手寫狀態機,之後我替換為了Ragel 實現的狀態機,但是最近測試的時候我發現http 解析函數的負載十分誇張,達到了10% , 難道說使用Ragel 之後反而導致了性能下降?(如果說解析header 會出現如此高的系統負載,那麼看來HTTP/2.0 對性能的提高還是很可觀的) 遺憾的是在之前我手寫狀態機的時候,我並沒有測試對應解析函數的負載情況,現在我一下子我拿不出來兩者的數據比較,有機會寫一個BenchMark 測測.
ssxrver 支持簡單的UDP 傳輸,但是我個人認為一個沒有擁塞控制,流量控制,丟包重傳功能的UDP 框架基本可以說是沒辦法正常應用的,以後我有時間去學習學習QUIC, KCP 這些協議,在補充補充UDP 相關知識,相信更高效更靈活的UDP 協議在未來的應用會越來越廣泛的!
實際上,我其實我認為目前最好的網絡框架應該是,端口復用地址復用加多線程(多進程)綁定同一地址和端口,內核自動去做accept 負載均衡, 同時在通過協程框架+ hook 阻塞系統調用,這個框架使用之後可以在保證高性能的前提之下,同時不用主線程分發連接,還不用陷入異步回調地獄.
初此之外,如果能使用上在Linux kernel 5.1 版本之後加入的異步IO 機制io_uring , 相信服務器的性能會更上一層樓,不過我目前對io_uring 的了解並不多,暫時還沒有功力去設計一個基於io_uring 的異步IO 網絡庫.