以下是最近某個項目的一次經歷,最終並沒有按照這樣的方案來優化,但對思路確實是一個提高,所以記錄在此。
-------------------------------------------------------------------------------------------------------------------
項目D為單機服務器,據說在線達到1500后,會很卡,於是想仔細分析了其中的原因。
整體來說:C++服務器+mysql數據庫,多線程。但是是單服。
請教了前同事,在他的一步步詢問下,理清了服務器的當前架構。
同事指導,對於服務器性能分析,要從內存分配和多線程兩個方面入手。
修改內存分配策略不但能降低內存,還能減少碎片,最終勢必會提高游戲性能(分配阻塞導致性能低)。
使用多線程,將復雜的邏輯異步到不同的線程去計算,減少了主邏輯的等待,也必然提高了流暢性。
線程方面:
1 一個socket負責監聽所有客戶端的session。使用了完成端口的概念,起了3個線程,負責收消息,收到后,將消息放入一個全局的隊列revQueue中,這個隊列包含所有玩家的所有消息。
2 全局的session管理類,管理所有客戶端的session。每個玩家發送消息時,寫入自己的發消息隊列ownSendQueue中。
3 一個單獨的數據庫操作隊列dbQueue,負責所有對數據庫的讀取。
4 啟動游戲時,開了一個線程,專門負責內部邏輯的刷新。包括各種timer,數據庫隊列dbQueue的分發,全局收消息隊列revQueue的分發(每次輪詢到時,會將隊列中的所有消息都分發出去),每個session的發消息隊列ownSendQueue,其它游戲內的各種update(血量體力等各種恢復)。
內存分配:
1 會頻繁使用到標准庫的map,vector,string對象。
2 對自定義的類,有內存池的管理策略。當前策略:
- 每種自定義對象,第一次請求內存時,由內存池多分配32個可保存此類型的地址。
- 以后每次請求,從預先分配的地址中直接獲取。
- 直到預分配的地址使用完,再重新分配32個,依次循環。
- 對象銷毀時,不真正將對象交還操作系統,而是插入到可用的預分配表中。
再來看下服務器當前的配置,在線人比較多的一個區服務器,包括三組服務器:
32G內存+10個CPU,每組在線約300人。cpu使用7%左右,內存使用17%左右。
這一系列的分析出來,問題相對而言就明顯了。
內存沒有達到有效的使用。
邏輯全在一個線程,應該就是整個瓶頸所在了。
針對問題提出的優化策略:
1.內存分配方法
對於已有的內存池策略:
- 是否可在程序啟動時,直接分配固定的內存數(比如3000,可根據在線人數確定)。占用一定內存開銷,提高運行時效率。需要數據驗證可行性。
- 每次增長的數(目前32)是否可優化為更大,或者修改為梯形增長方式,或者以每次2倍的速度增長?需要數據驗證可行性。
徹底的接管內存分配
- 使用gperftools的tcmalloc組件徹底接管內存分配。配置很方便,編譯時增加一個鏈接選項即可。(https://my.oschina.net/u/877348/blog/272066)
初步測試,接入tcmalloc后,內存占用由原來的107448降為67108,提高大約40%,可驗證對在線的影響。
2.並行計算
- 修改完成端口啟動的線程數目,提高CPU 使用率,可有效提高網絡通信的吞吐量。目前為3,一般設置為CPU數*2。
- 目前所有玩家的邏輯都在一個線程處理,考慮使用多線程的可行性。
以上是在不改變當前單服的狀態下,可做出的優化。畢竟單服總有上限,如果以上的優化都不能達到想要的效果,就要拆分服務器了。
-
增加LoginServer
Gate和Master之間增加LoginServer,或者Gate本身增加LoginServer的功能。
負責:登陸驗證、創角、角色列表、刪角、 封禁IP過濾等處理,其它邏輯交給Master。
- 增加LogServer
增加LogServer,監聽Master傳送的消息,專門負責和logDB的交互。
- 其它
上述修改完成后,如果在線依然無法滿足,可根據統計數據,逐步拆分出GameGate,GMGate,ChatServer,TaskServer,DBServer。
-------------------------------------------------------------------------------------------------------------------
再次重復下,以上只是某個項目的一次經歷,最終並沒有按照這樣的方案來優化,但對思路確實是一個提高,所以記錄在此。