标签:
前段时间重新对消息总线的通信模型进行设计&重构,这篇文章谈谈其中的一些想法。
消息总线对RabbitMQ的官方java client进行了定制、简化。这里首先谈谈RabbitMQ作为一个Message Broker的两个核心概念:
Exchange(交换器),其实你可以简单得将它看成是Router(路由器),路由就是它的主要职责。它支持非常灵活的 Exchange-Exchange、Exchange-Queue 之间的绑定,并提供了4种Exchange类型。正是由于Exchange的存在,才使得RabbitMQ可以构建出多种路由拓扑模型出来,消息总线沿用的是经典的树形(Proxy)拓扑。
Queue(队列)是RabbitMQ最终保存消息的地方。生产者将消息发送给Exchange,RabbitMQ根据Exchange的绑定关系,将消息路由到Queue中,消费者通过连接到Queue来进行消息消费。
消息总线最初的设计目标之一是简化(或者说屏蔽)第三方对Exchange的感知(包括Exchange的创建、绑定策略等等)。他们只需要了解Queue的存在:
使用者只需要了解Queue,在背后消息总线做了其他的事情,包括:按照RabbitMQ的模型构建队列,完成绑定等技术细节。所以一切设计都围绕Queue展开,看起来顺其自然。
从上面的表述可知,对于消费者而言,它必须在RabbitMQ中对应一个真实的Queue。但对于生产者而言却不是,生产者只需要知晓它需要将消息发往哪个Queue,而不需要在RabbitMQ中真实存在一个与它对应的Queue,它只需要跟Exchange打交道。
消息总线除了简化使用方基于RabbitMQ的通信这一目标之外,还有一个主要目标,就是对通信权限进行管控。那么这边就产生一个不对等的问题:消费者是对应队列的,而生产者不需要对应一个真实存在的队列。很容易想到解决这个问题的方案:生产者也拥有一个队列不就好了么(而且生产方拥有队列也顺便能收到系统广播/事件之类的消息)。生产者也拥有一个队列
倒是很容易想到,而且也不难,但从资源利用率的角度来看,显然有些资源浪费。因为对生产者而言,它拥有队列的目的只是为了在授权模型上与消费方对等,或者只是收取广播事件。
这段时间,工作重心放到日志系统上,日志收集我们采用的是flume-ng 作为agent,它的source
,channel
,sink
的抽象模型,让我觉得它在消息总线上也同样适用。其实都是生产者-消费者的模型,都是去耦合的实现。
在消息总线中我进行了这样的对等映射:
形象化得理解就是下图:
针对source,sink的抽象,你可以类比为一个水龙头作为source,一个水槽作为sink,如何将水龙头跟水槽联系起来?很简单,拧开水龙头,使得水源流向水槽。这里就是通过水流(stream)来建立水龙头(source)和水槽(sink)的单向关系。而这个关系以流来进行抽象表述也同样适合于消息总线中生产者要跟消费者之间建立的通信关系。而对这个通信关系的管理、建立与维护,就是消息总线的授权功能。
自此,在消息总线新的模型中将队列的概念对外界隐藏(但就内部结构而言,它还是跟消费者并存)。外界对消息总线的感知,仅限于:source
,sink
,stream
。
并且在使用时仅需要回答几个问题:
消息总线在以生产者和消费者为基础模型之上,封装并简化了一些通信场景,其中就有Pub/Sub模型。这个模型有何特别之处需要单独拿出来讲呢?它的特殊性在于:它的依赖关系是反向的。
这会使得原来的机制有一些变动,但总得来说,都是朝着合理的方向转变。
虽然在内部实现上并没有大改,这样的模型变动主要反映在管控台上。
消息源:
消息槽:
消息流:
但这使得消息总线的模型更简单&清晰,也更容易被理解。
更多内容请访问:http://vinoyang.com
标签:
原文地址:http://blog.csdn.net/yanghua_kobe/article/details/50263857