Rabbitmq的性能測試

在作系統的總體性能測試時發現常常會卡在一個較低的QPS(單機低於100)數值,並且應用服務器的負載不高,檢查MQ消費速率只有40左右。接着把目標放在消息發送端上,發現消息發送速率很低,大約40條/s。java

果斷搭建一個最小化工程單測Rabbitmq發送性能,發如今啓用發送端事務後性能降低很是明顯。spring

消息數量 開啓事務 未開啓事務
10w 320796ms 10246ms

本機SSD硬盤測試結果10w條消息未開啓事務,大約10s發送完畢;而開啓了事務後,須要將近320s,差了30多倍。數據庫

接着翻閱Rabbitmq官網,發現開啓事務性能最大損失超過250倍。性能優化

Using standard AMQP 0-9-1, the only way to guarantee that a message isn't lost is by using transactions -- make the channel transactional then for each message or set of messages publish, commit. In this case, transactions are unnecessarily heavyweight and decrease throughput by a factor of 250. To remedy this, a confirmation mechanism was introduced. It mimics the consumer acknowledgements mechanism already present in the protocol.

事務爲何會慢

rabbitTemplate.setChannelTransacted(true);

該標誌位開啓後表示Rabbitmq的發送統一被spring事務管理。當一段代碼被@Transactional包裹,那麼只有當事務結束後,消息纔會正在的發送到Rabbitmq的exchange中。具體代碼詳見rabbitTemplate.java中的doSend()服務器

事務機制是Rabbitmq自身支持的,原理是channel.txSelect()開啓事務,channel.txRollback()回滾事務。channel.txCommit()提交事務。當事務開啓後,經過抓包會發現網絡交互增多。網絡

圖片描述

是否能夠去掉事務呢?

實踐證實,不行併發

由於某些消息,特別是實體的新增或者更新消息發出後,消費者有可能會經過API反查,這時若是生產者本地事務未提交。消費者就有可能消費到空數據或者舊數據。因此生產者必須將發送消息的事務包裹在本地數據庫事務當中。異步

在過去的實踐中,有一種解法能夠在不開啓事務的狀況下解決這個問題,就是利用本地消息表,即生產者調用後不發送,而是將消息寫入到本地消息表,當事務失敗那麼這次寫入操做也會回滾。真正發送消息到MQ就開啓另外一個定時線程輪詢該本地消息表異步發送消息。這種方法理論上可行,但實際操做很是複雜,當有多個生產者實例時,定時發送線程也會有多個,那麼就會遇到各類併發問題。性能

最大限度改善性能

既然沒法去除事務,而且也不但願代碼異常複雜。那麼能夠將消息分爲兩類,一類是changlog即實體的變化,一類是command,即通知消費者能夠開始作某事,一般用在同步轉異步的場景。對於第一類消息仍然保留事務,對於第二類消息關閉發送事務,採用PublisherConfirm的方式保證消息發送成功。測試

再次測試,性能明顯提升,可是並未達到預期,經過innotop命令查看MySQL壓力,發現只有10K/s上下。檢查Rabbitmq所在機器的負載。

圖片描述

iowait很是高,因爲該機器上還裝有es,一樣是io密集型的應用,因此實際性能瓶頸都在磁盤io上了。

跟Devops確認了機器狀況,這臺機器剛好是Rabbitmq的磁盤節點。爲了快速驗證,新增了一塊SSD硬盤並將Rabbitmq消息文件都掛載到新加的磁盤上。再次測試,iowait降低明顯。

圖片描述

經過innotop命令查看MySQL壓力,發現上升了一倍,達到20K/s。基本上把壓力都轉到了數據庫一側。系統總體性能提高了一個數量級。也許該Rabbitmq節點獨佔一臺機器效果會更好。

總結

性能優化有時候就像破案,看了jstat沒問題,gc沒問題,機器負載也不高,就是抓不到「元兇」。須要一點一點的扣,每每一個短板就形成了木桶效應。另外還有一點就是若是硬件可以解決的事情,就不要過分優化軟件了,代碼複雜度上升每每意味着更多的BUG,在資源有限的狀況下多花點錢省點時間仍是值得的。

相關文章
相關標籤/搜索