概述
在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
了。当流量继续增大的时候,可能会出现吞吐量上不去的情况,如下图: