高性能C++網絡庫libtnet實踐:comet單機百萬連接掛載測試

最近在用go語言做一個掛載大量長連接的推送服務器,雖然已經完成,但是內存佔用情況讓我不怎麼滿意,於是考慮使用libtnet來重新實現一個。後續我會使用comet來表明推送服務器。

對於comet來說,單機能支撐大量的併發連接,是最優先考慮的事項。雖然現在業界已經有了很多數據,說單機支撐200w,300w,但我還是先把目標定在100w上面,主要的原因在於實際運行中,comet還會有少量邏輯功能,我得保證在單機掛載100w的基礎上,完全能無壓力的處理這些邏輯。

CometServer Test

首先我用libtnet簡單寫了一個comet server。它接受http請求,並將其掛起,過一段隨機時間之後在返回200。

void onTimeout(const TimingWheelPtr_t& wheel, const WeakHttpConnectionPtr_t& conn)
{
    HttpConnectionPtr_t c = conn.lock();
    if(c)
    {
        c->send(200);
    } 
}

void onHandler(const HttpConnectionPtr_t& conn, const HttpRequest& request)
{
    int timeout = random() % 60 + 30;
    comet.wheel->add(std::bind(&onTimeout, _1, WeakHttpConnectionPtr_t(conn)), timeout * 1000);
}

int main()
{
    TcpServer s;        
    s.setRunCallback(std::bind(&onServerRun, _1));
    HttpServer httpd(&s);
    httpd.setHttpCallback("/", std::bind(&onHandler, _1, _2));
    httpd.listen(Address(11181));
    s.start(8);
    return 0; 
}

可以看到comet server只是負責了掛載長連接的事情,而沒有消息的推送。在實際項目中,我已經將掛載連接和推送消息分開到兩個服務去完成。所以這裏comet僅僅是掛載連接測試。

測試機器準備

因爲linux系統上面一個網卡tcp連接端口數量是有限制的,我們調整ip_local_port_range使其能支撐60000個tcp連接:

net.ipv4.ip_local_port_range = 1024 65535

對於100w連接來說,我們至少需要16臺機器,但實際我只有可憐的3臺4G內存的虛擬機。所以就要運維的童鞋在每臺機器上面裝了6塊網卡。這樣我就能建立100w的連接了。

測試客戶端也非常簡單,每秒向服務器請求1000個連接,但是需要注意的是,因爲一臺機器上面有多塊網卡,所以在創建socket之後,我們需要將socket綁定到某一塊網卡上面。

實際測試中,因爲內存問題,每臺機器頂多能支撐34w左右的tcp連接,對我來說已經足夠,所以也懶得去調優了。

CometServer Linux調優

首先我們需要調整最大打開文件數,在我的機器上面,nr_open最大的值爲1048576,對我來說已經足夠,所以我將最大文件描述符數量調整爲1040000。

fs.file-max = 1040000

然後就是對tcp一些系統參數的調優:

net.core.somaxconn = 60000
net.core.rmem_default = 4096
net.core.wmem_default = 4096
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
net.ipv4.tcp_mem = 786432 2097152 3145728
net.core.netdev_max_backlog = 60000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_syn_backlog = 60000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_orphans = 131072
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_tw_buckets = 60000
net.netfilter.nf_conntrack_max = 1000000
net.netfilter.nf_conntrack_tcp_timeout_established = 1200

對於如何調節這些值,網上都是各有各的說法,建議直接man 7 tcp。我在實踐中也會通過查看dmesg輸出的tcp錯誤來動態調節。這裏單獨需要說明的是tcp buffer的設置,我最小和默認都是4k,這主要是考慮到推送服務器不需要太多太頻繁的數據交互,所以需要儘可能的減少tcp的內存消耗。

測試結果

實際的測試比較讓我滿意。

cometserver test8個進程cpu消耗都比較低,因爲有輪訓timing wheel然後再發送200的邏輯,所以鐵定有cpu消耗,如果只是掛載,cpu應該會更低。

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                                       
 4685 root      20   0  187m 171m  652 S 19.9  1.1   2:19.20 cometserver_test                                                                                                
 4691 root      20   0  191m 175m  656 S 16.6  1.1   2:17.80 cometserver_test                                                                                                
 4686 root      20   0  170m 155m  652 S 16.3  1.0   2:09.54 cometserver_test                                                                                                
 4690 root      20   0  183m 167m  652 S 16.3  1.1   2:11.44 cometserver_test                                                                                                
 4692 root      20   0  167m 152m  652 S 16.3  1.0   2:11.29 cometserver_test                                                                                                
 4689 root      20   0  167m 152m  652 S 15.3  1.0   2:03.08 cometserver_test                                                                                                
 4687 root      20   0  173m 158m  652 S 14.3  1.0   2:07.34 cometserver_test                                                                                                
 4688 root      20   0  129m 114m  652 S 12.3  0.7   1:35.77 cometserver_test

socket的統計情況:

[root@localhost ~]# cat /proc/net/sockstat
sockets: used 1017305
TCP: inuse 1017147 orphan 0 tw 0 alloc 1017167 mem 404824
UDP: inuse 0 mem 0
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0

可以看到,總共有1017147個tcp鏈接,同時佔用了將近4G(1017167是頁數,需要乘以4096)的內存。

系統內存的情況:

[root@localhost ~]# free
             total       used       free     shared    buffers     cached
Mem:      16334412   11210224    5124188          0     179424    1609300
-/+ buffers/cache:    9421500    6912912
Swap:      4194296          0    4194296

系統有16G內存,還有5G可用,所以不出意外單機應該還能承載更多的tcp連接。

總結

使用libtnet開發的一個簡單的comet server支撐了百萬級的連接,加深了我對其應用的信心。

libnet地址https://github.com/siddontang/libtnet,歡迎圍觀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章