概述
在AMQP
協議中,有channel
的概念,在RabbitMq
中,channel
表示邏輯連接或者叫虛擬連接,是棣屬於TCP
連接的。一個TCP
連接裏可以創建多個channel
,在Rabbit MQ
裏,消息的發送和接收都是基於channel
的。
有了TCP
連接後,還需要channel
的原因如下:
- 創建和銷燬
TCP
連接很耗時; - 打開太多
TCP
連接,耗操作系統資源,併發量大到一定程度,系統的吞吐量會降低; - 使用一個
collection
多channel
的方式,可以提升連接的利用率。
因此採用多個channel
多路複用一個TCP
連接的方式才比較合理。
channel線程不安全
channel
不是線程安全的,線程併發的去訪問同一個channel
會出問題。這裏有幾種處理方式:
- 全局公用一個
channel
且使用全局鎖,讓操作channel
排隊.這種明顯性能是不行的; - 一個線程對應創建一個新的
channel
,但是要處理好一個連接能支撐的最大channel
數量; - 一個線程對應一個
channel
,但是是從channel
池子拿的,不是每次都創建新的.一旦一個線程完成了一個channel
的使用,它將返回到池中,從而使該channel
可用於另一個線程。
量不大的話,使用第二種方式就可以了。量大的話,建議使用第三種方式,畢竟創建和銷燬channel
也是耗時耗資源的.在spring amqp
中,提供了一個緩存channel
的方案。可以在創建CachingConnectionFactory
時指定緩存的模式。
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
connectionFactory.setChannelCacheSize(25);
上面的兩行代碼,表示channel
共用唯一的一個連接,且緩存了25個channel
,注意這裏的25個並不是說,這個連接裏只能最多創建25個channel
,而是說最多緩存25個channel
。舉個例子,假設併發發送100條消息,在CachingConnectionFactory.CacheMode.CHANNEL
模式下,瞬間會創建100個channel
的,然後往緩存裏放25個channel
,當流量下去了,剛剛創建的多餘的channel
會自動關閉掉的,緩存裏只保留25個。
使用這種方式的話,要注意緩存的channel
數量,不能太小,不然流量一大,仍然會造成頻繁關閉channel
的情況。當然我們也不能說有多少併發,就創建多少個channel
,還是要限制一下,這個時候可以使用:
connectionFactory.setChannelCheckoutTimeout(1000);
當ChannelCheckoutTimeout
的值大於0的時候,ChannelCacheSize
的值就是最大的channel
數量了,一旦從緩存中獲取不到channel
,等待ChannelCheckoutTimeout
毫秒後,如果還是獲取不到的,就會拋AmqpTimeoutException
了。
我們也可以自己實現channel pool
,但是不太建議怎麼做,畢竟spring amqp
還是相當成熟的,直接使用就可以了。
CacheMode.CHANNEL模式性能
如上文所述,採用了CacheMode.CHANNEL
的模式的話,就是一線程一channel
形式,且這些channel
共享了同一個連接,也即是共享同一個socket
。當併發量一大的時候,可能導致同一時刻,多個線程都想往這個socket
上寫數據。爲了避免這種情況,只能加鎖,讓拿不到鎖的線程block
住。在老外寫的Using spring-rabbit under high throughput一文中,也提到了這點,並做了壓力測試,併發10個線程發送1000000條消息,結果線程被block
住了,如下圖:
作者也提到,當流量很大的時候,使用CacheMode.CONNECTION
的模式,可以提高發送效率。關於這兩種模式的性能問題,也可以看一下在stackoverflow
的討論,Spring CachingConnectionFactory limiting channels & causing Thread Blocking;
channel的監控
RabbitMQ Admin UI
提供了一個監控channel
的界面,我們主要關注兩點:
- channel有沒有可能泄露,打開了channel,卻沒有關閉channel;
- 打開channel和關閉channel的速率。
如果通道打開操作的速率始終高於通道關閉操作的速率,那就可能發生channel
泄露了。如下圖:
如果打開和關閉channel
的速率都很高,也值得觀察一下。因爲可能是沒有緩存channel
了。當流量繼續增大的時候,可能會出現吞吐量上不去的情況,如下圖: