消息总线之模型重构

文章目录
  1. 1. RabbitMQ简介
    1. 1.1. Exchange
    2. 1.2. Queue
  2. 2. 以队列为中心的设计
    1. 2.1. 授权模型衍生虚拟队列
  3. 3. 去队列化的设计
    1. 3.1. 授权模型映射为消息流
    2. 3.2. Pub/Sub模型的特殊性

前段时间重新对消息总线的通信模型进行设计&重构,这篇文章谈谈其中的一些想法。

RabbitMQ简介

消息总线对RabbitMQ的官方java client进行了定制、简化。这里首先谈谈RabbitMQ作为一个Message Broker的两个核心概念:

Exchange

Exchange(交换器),其实你可以简单得将它看成是Router(路由器),路由就是它的主要职责。它支持非常灵活的 Exchange-Exchange、Exchange-Queue 之间的绑定,并提供了4种Exchange类型。正是由于Exchange的存在,才使得RabbitMQ可以构建出多种路由拓扑模型出来,消息总线沿用的是经典的树形(Proxy)拓扑。

Queue

Queue(队列)是RabbitMQ最终保存消息的地方。生产者将消息发送给Exchange,RabbitMQ根据Exchange的绑定关系,将消息路由到Queue中,消费者通过连接到Queue来进行消息消费。

以队列为中心的设计

消息总线最初的设计目标之一是简化(或者说屏蔽)第三方对Exchange的感知(包括Exchange的创建、绑定策略等等)。他们只需要了解Queue的存在:

  • 生产者:我要将消息发送到某个Queue
  • 消费者:我要申请一个Queue以从中消费消息

使用者只需要了解Queue,在背后消息总线做了其他的事情,包括:按照RabbitMQ的模型构建队列,完成绑定等技术细节。所以一切设计都围绕Queue展开,看起来顺其自然。

授权模型衍生虚拟队列

从上面的表述可知,对于消费者而言,它必须在RabbitMQ中对应一个真实的Queue。但对于生产者而言却不是,生产者只需要知晓它需要将消息发往哪个Queue,而不需要在RabbitMQ中真实存在一个与它对应的Queue,它只需要跟Exchange打交道。

消息总线除了简化使用方基于RabbitMQ的通信这一目标之外,还有一个主要目标,就是对通信权限进行管控。那么这边就产生一个不对等的问题:消费者是对应队列的,而生产者不需要对应一个真实存在的队列。很容易想到解决这个问题的方案:生产者也拥有一个队列不就好了么(而且生产方拥有队列也顺便能收到系统广播/事件之类的消息)。生产者也拥有一个队列 倒是很容易想到,而且也不难,但从资源利用率的角度来看,显然有些资源浪费。因为对生产者而言,它拥有队列的目的只是为了在授权模型上与消费方对等,或者只是收取广播事件。

去队列化的设计

这段时间,工作重心放到日志系统上,日志收集我们采用的是flume-ng 作为agent,它的source,channel,sink的抽象模型,让我觉得它在消息总线上也同样适用。其实都是生产者-消费者的模型,都是去耦合的实现。
在消息总线中我进行了这样的对等映射:

  • source : 消息的生产者
  • channel : 可以将RabbitMQ这样的整个Message Broker抽象成一个Channel,注意跟RabbitMQ官方的java client里的Channel对象进行区别
  • sink : 消息的消费者

形象化得理解就是下图:

model

授权模型映射为消息流

针对source,sink的抽象,你可以类比为一个水龙头作为source,一个水槽作为sink,如何将水龙头跟水槽联系起来?很简单,拧开水龙头,使得水源流向水槽。这里就是通过水流(stream)来建立水龙头(source)和水槽(sink)的单向关系。而这个关系以流来进行抽象表述也同样适合于消息总线中生产者要跟消费者之间建立的通信关系。而对这个通信关系的管理、建立与维护,就是消息总线的授权功能。

自此,在消息总线新的模型中将队列的概念对外界隐藏(但就内部结构而言,它还是跟消费者并存)。外界对消息总线的感知,仅限于:source,sink,stream
并且在使用时仅需要回答几个问题:

  • 我需要申请的是消息源还是消息槽?
  • 我需要跟谁或者谁需要跟我建立消息流才能使用消息服务

Pub/Sub模型的特殊性

消息总线在以生产者和消费者为基础模型之上,封装并简化了一些通信场景,其中就有Pub/Sub模型。这个模型有何特别之处需要单独拿出来讲呢?它的特殊性在于:它的依赖关系是反向的。

  • 感知关系:Produce/Consume模型中,Producer需要感知到Consumer的存在,而Pub/Sub模型中是Subscriber需要感知到Publisher得存在
  • 授权关系:虽然消息流向并没有变,但授权关系也是反向的。这里需要鉴别Subscriber 订阅 Publisher是否合法

这会使得原来的机制有一些变动,但总得来说,都是朝着合理的方向转变。

虽然在内部实现上并没有大改,这样的模型变动主要反映在管控台上。

消息源:

source

消息槽:

sink

消息流:
stream

但这使得消息总线的模型更简单&清晰,也更容易被理解。