前言
最近用了幾個月的時間,一直在對EQueue做性能優化。到現在總算告一段落了,現在把一些優化的結果分享給大家。EQueue是一個分布式的消息隊列,設計思路基本和阿里的RocketMQ一致,只是是用純C#寫的,這點大家應該都知道了。
- EQueue開源地址:https://github.com/tangxuehua/equeue
- EQueue相關文檔:http://www.cnblogs.com/netfocus/category/598000.html
- EQueue Nuget地址:http://www.nuget.org/packages/equeue
之前EQueue 1.*版本,消息持久化是使用SQLServer的,之前也想做性能測試,但發現效果不理想。所以放棄了測試,轉為專心對消息持久化這塊進行優化。之前的持久化設計是通過Sql Bulk Copy的方式異步批量持久化消息。但是當數據庫服務器和Broker本身部署在不同的服務器上時,持久化消息也會走網卡,消耗帶寬,影響消息的發送和消費的TPS。而如果數據庫服務器部署在Broker同一台服務器上,則因為SQLServer本身也會消耗CPU以及內存,也會影響Broker的消息發送和消費的TPS。所以,經過考慮后,最終決定狠下心來,采用終極方案來持久化消息,就是通過本地順序寫文件的方式來持久化消息。支持異步刷盤和同步刷盤兩種方式。采用文件存儲后,EQueue可以輕易的支持大量消息的堆積,你的硬盤有多大,就能堆積多少量的消息。
到現在為止,經過不斷的測試,功能上我認為已經比較穩定了,性能也基本滿足我的要求。另外,EQueue單純的消息持久化模塊(MessageStore)以及TCP通信層的性能是非常高的。MessageStore我設計時,盡量獨立一些,因為我發現寫文件和讀文件的設計是通用的,以后可以被復用到其他項目,所以我設計時,確保持久化模塊的通用性;而TCP通信層,也是也是一樣,通用和高效。目前我自己寫的通信層(在ECommon類庫中)可以輕松把網卡壓滿。
關於EQueue 2.0文件持久化版本的具體設計,我下一篇文章會具體介紹,本文主要是想分享一下EQueue 2.0的性能測試結果。為大家在使用到線上環境前做架構選型給出性能方面的參考。
測試目的
測試單個EQueue Broker服務器的發送消息、消費消息的性能。
硬件環境
全部采用UCloud雲主機進行測試。一台Broker,4台Client機器。Broker的配置為8核16G內存,120G的SSD硬盤,Client的配置為4核8G內存,普通硬盤。所有服務器操作系統均為Windows Server 2012。具體如下圖所示:

測試場景1
1台生產者、1台消費者、1台Broker,異步刷盤,每隔100ms刷一次盤。
| 消息大小(字節) | 發送TPS | 消費TPS | 發送網絡吞吐量 | 消費網絡吞吐量 | Broker磁盤寫入吞吐量 | Broker CPU | Broker內存 | Producer CPU | Producer內存 | Consumer CPU | Consumer內存 |
| 128 | 56400 | 56400 | 10MB | 13MB | 13MB | 35% | 35% | 30% | 12% | 30% | 13% |
| 512 | 41000 | 41000 | 22MB | 25MB | 25MB | 32% | 35% | 30% | 13% | 30% | 13% |
| 1024 | 29000 | 29000 | 30MB | 34MB | 34MB | 32% | 35% | 27% | 12% | 25% | 13% |
| 2048 | 20000 | 20000 | 41MB | 43MB | 43MB | 30% | 35% | 27% | 12% | 25% | 13% |
測試場景2
2台生產者、2台消費者、1台Broker,異步刷盤,每隔100ms刷一次盤。
| 消息大小(字節) | 發送總TPS | 消費總TPS | 發送總網絡吞吐量 | 消費總網絡吞吐量 | Broker磁盤寫入吞吐量 | Broker CPU | Broker內存 | Producer CPU | Producer內存 | Consumer CPU | Consumer內存 |
| 128 | 50000 | 50000 | 8.6MB | 11MB | 11MB | 46% | 35% | 19% | 13% | 19% | 13% |
| 512 | 40000 | 40000 | 21MB | 24MB | 24MB | 48% | 35% | 19% | 13% | 19% | 13% |
| 1024 | 31200 | 31200 | 32MB | 35MB | 35MB | 47% | 35% | 19% | 13% | 19% | 13% |
| 2048 | 23000 | 23000 | 48MB | 51MB | 51MB | 45% | 35% | 19% | 13% | 19% | 13% |
結束語
一些說明
- 因為EQueue的Producer發送消息時,本地會有緩存隊列。所以Producer發送消息采用的線程數對發送TPS並無什么影響。所以,我上面的測試中並沒有考慮發送線程數;
- Broker的內存使用我們是可以控制的,我測試時配置為當內存使用到達40%時,開始自動清理內存,所以上面的測試結果,內存使用都不會超過40%;
- 因為消費者從Broker拉取消息時,EQueue的設計在內存足夠的情況下,總是會將需要消費的消息緩存在非托管內存,所以消費者拉取消息時,總是從內存獲取消息的。所以Broker沒有磁盤的讀取;且由於這個設計,消費者消費消息不會成為瓶頸,假設當前Broker上存在大量消息可以被消費,然后我們開一個Consumer,那消費速度是非常快的,1K大小的消息,10W TPS沒有問題。所以,消息的消費不會成為瓶頸,除非消費者自己內部處理消息的代碼太慢。這種情況下,消費者需要增加Consumer機器,確保消費速度跟得上發送速度。
- 上面的測試結果是基於UCloud的虛擬機,由於我沒有物理機,所以不知道物理機上的性能。有條件的朋友我非常希望能幫忙測試下哦。像這種重要的分布式消息隊列服務器,是應該部署在物理機上的。另外,我測試時,Producer和Consumer服務器使用的是普通的虛擬機,盡量真實的模仿一般應用的虛擬機配置。
測試結論
從上面的測試結果可知:
- 隨着消息體的不斷增大,發送消息的TPS不斷降低,一般我們的消息大小為1KB,在有兩個發送者客戶端時,EQueue的發送消息TPS大概為31000。我覺得性能上應該滿足大部分需求場景了。如果一個系統平均每秒產生31200個消息,一天是86400秒。那一天的消息量是27億。這個數字已經很大了,呵呵。相信國內大部分網站都沒有這么大的規模。當然我們不能這么簡單的來算,實際的系統肯定有高峰期的,實際大家要根據自己的系統的高峰期發送消息的TPS來衡量具體要部署多少台EQueue Broker服務器。線上環境使用時,我們不能把Broker壓滿的,要給其有足夠的處理余量,比如高峰期發送TPS為10W,每台Broker最大能承受3W,那我們至少要分配6台機器,確保應付可能出現的高峰。這樣才能應付特別高的高峰。
- 大家可以看到上面的數據中,開兩個生產者,兩個消費者,消息大小為2KB時,Broker的進出總網絡流量為99MB,這個數據還沒有到達千兆網卡的總流量(125MB)。目前,我只申請了5台機器,4個客戶端機器。但實際我們的應用的集群,客戶端機器應該會更多,我不知道當客戶端機器增加時,能否把Broker的網卡壓滿,如果壓不滿,則說明Broker本身的設計實現已經到達瓶頸了,也就是說EQueue Broker本身設計上可能還有性能優化的空間。
- 在上面的性能數據情況下,所有服務器的CPU,內存使用穩定,符合預期。在穩定性方面,我在個人筆記本上同時發送和消費消息,連續運行1天沒有問題;也有其他網友幫忙測試了穩定性,運行幾天也沒有問題。各項指標均正常。所以我認為目前EQueue的穩定性應該問題不大了。
- Broker的消息持久化,采用的是順序寫文件的方式;目前的測試結果來看,完全還沒達到寫文件的瓶頸;如果我們單獨測試寫文件模塊的性能,可以輕松達到300MB每秒,而我們的千兆網卡最大流量也只有125MB。所以,消息持久化不會再成為很大的瓶頸了,但寫文件的響應時間本身也會影響Broker對外的TPS,目前的響應時間為60ms,我感覺是比較慢的,不知道是否是虛擬機的原因,不知道物理機上會如何。我覺得SSD硬盤,寫入文件的響應時間不會這么慢的。
- 最后一點,當Broker的客戶端增加時,Broker的CPU的使用也會相應增加一點。這個可以理解,因為TCP長連接的數量多了一倍。而生產者和消費者服務器的CPU內存的使用都非常穩定,符合預期。
