从RabbitMQ Channel设计看连接复用

2020-09-11 11:23:55 浏览数 (1)

今天公司有同事在做RabbitMQ的分享的时候,讲到了Connection和Channel的设计,有同学有疑惑,为什么不用连接池实现,而要通过Channel的方式实现呢?

先脑补下Connection和Channel的关系

即可以在一个连接上同时发送不同Channel的数据;

看下RabbitMQ官网对于Channel的解读:

Some applications need multiple connections to the broker. However, it is undesirable to keep many TCP connections open at the same time because doing so consumes system resources and makes it more difficult to configure firewalls. AMQP 0-9-1 connections are multiplexed withchannels that can be thought of as "lightweight connections that share a single TCP connection".

Every protocol operation performed by a client happens on a channel. Communication on a particular channel is completely separate from communication on another channel, therefore every protocol method also carries a channel ID (a.k.a. channel number), an integer that both the broker and clients use to figure out which channel the method is for.

A channel only exists in the context of a connection and never on its own. When a connection is closed, so are all channels on it.

For applications that use multiple threads/processes for processing, it is very common to open a new channel per thread/process and not share channels between them.

大概的意思就是:一些应用需要同时创建多个连接到broker也就是RabbitMQ服务器上。然而因为防火墙的存在,很难同时创建多个连接。AMQP 0-9-1连接使用多个channel连接实现对单一Connection的复用。 客户端的每一个协议操作都发送在channel上。每个协议方法携带channel IDbrokerclient使用channel ID来确定方法对应的channel。因此实现channel之间的数据隔离。 channel不能单独存在,仅存在connection上下文中。当connection关闭时,channel也会关闭。 多线程/进程之间打开一个channel但不共享channels是很普遍的。

回到问题本身,为什么要用Channel,因为在某些场景创建连接,服务器的负载会比较高:

设想如果RabbitMQ只有3个Broker,而客户端可能有100台Java机器,如果用连接池的方式,假设并发是50,则Broker需要承载5000个连接,而如果采用Channel的方式,则只要500个连接,当然了,前提是带宽够,程序执行快,这个后面再讲。

既然这样,Channel为什么不可以用在所有连接上,而只限定在一个连接上?

理论上是行得通的,不过这不是一个好的设计。

主要是因为成本的问题,每创建一个 Channel,Broker都要保存相应的数据结构,如果Channel没有生命周期,则服务端的内存会一增加;跟连接绑定后,连接关闭这个事件是可以捕捉到的,然后就可以释放Channel占用的资源;

还有就是并发的问题,如果可以在多个Connection上使用同一个Channel,如果客户端同时在多个Connection上往同一个Channel发数据,顺序怎么保证?

抛开RabbitMQ,所有场景都适用Channel吗,不一定,前提是带宽够,每一次发送的消息量小,并且程序调度非常快;打个比方单机网卡是1G bit,也就是128M bytes,每次发送消息体只有1K左右,则理论上每秒可以发送12.8万条消息,程序设计的好,发送时不加锁,本来需要1K个连接支持,使用Channel之后,只要一个连接了,这种场景下使用Channel,优势就会发挥出来,服务端压力很小。

不过如今机器内网带宽一般都比较大,总体来说使用Channel是一个不错的选择。

当然像Channel这样的设计也有它的弊端,就是增加了程序的复杂性,引入了新的概念Channel,服务端要增加另外的结构存储,另外还要考虑单个连接上同时多Channel数据发送不会串掉,另外还要管理Channel的生命周期。不过这也给我们如何最大程度使用单个连接设计一些参考。

当然如果服务端承受并发能力高,客户端TPS可控,使用连接池也可以解决连接复用的问题,相对来说就简单些,还是得看具体业务场景。

总体来说多线程连接复用是一个趋势,lettuce就有这样的设计,多个线程可以共享一个连接,而且tps还蛮高。

0 人点赞