消息总线之模型重构

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

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 : 消息的消费者

形象化得理解就是下图:

授权模型映射为消息流

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

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

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

Pub/Sub模型的特殊性

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

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

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

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

消息源:

消息槽:

消息流: 

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

更多内容请访问:http://vinoyang.com

原文发布时间为:2015-12-11

本文作者:vinoYang

时间: 2024-10-28 16:05:05

消息总线之模型重构的相关文章

消息总线重构之EventBus

最近花了不少时间对消息总线进行了重构.重构的重点是在消息总线中加入了Guava的EventBus,并应用于以下两个场景: (1)改进广播通知 (2)业务逻辑串联,用事件驱动替代责任链模式 EventBus简介 EventBus是Google的开源项目Guava里的一个组件,有兴趣的人可以看我前不久的一篇博文解读.总得来说,EventBus是观察者模型的实现,利用它你既可以实现观察者模型的业务场景,还可以基于它的事件驱动机制来实现应用程序内组件之间的解耦与通信. 改进广播通知 广播通知是消息总线提

谈消息总线的路由模型

最近在写一个基于RabbitMQ的消息总线.虽然RabbitMQ提供了plugin的机制可以实现对其进行扩展,但是由于对erlang语言不熟,考虑到上手成本高,因此放弃实现plugin,转而基于Smart client + 树形拓扑路由的模型.当然这也大大降低了我们实现功能的灵活性,后面我会找个时间开篇新文章,谈谈Smart Client的限制. 预备知识 RabbitMQ对于消息的通信只提供了几个非常简单的API:Channel#basicPublish:Channel#basicConsum

消息总线重构之简化客户端

这段时间对消息总线进行了再次重构.本次重构主要针对消息总线的pubsub组件以及对client的简化,同时谈谈对消息总线的一些想法. 简化client的复杂度 之前的client需要同时连接两个分布式组件.消息总线的访问需要用户提供pubsuberHost,pubsuberPort参数,因此它首先连接的就是pubsuber.而消息总线是基于RabbitMQ构建的,因此它必然还需要连接RabbitMQ.而之所以没有需要用户程序提供RabbitMQ Server的地址信息,是因为它是通过pubsub

消息总线扩展之主动转发

问题简述 消息总线目前为Java编程语言提供了SDK,同时针对其他语言提供了一个称之为httpBridge的http代理.这基本可以满足大部分主流编程语言对消息总线的使用需求,但这也仅仅是对技术层面上的需求的满足.在业务层面上,尤其是面对老的业务系统的适配一直都是个难题,这篇文章谈谈面对一个在线上运行的业务系统,如何使得引入消息总线的总体成本尽可能得低. 就消息总线的两种使用方式而言,无论是SDK的方式还是httpBridge的方式,都需要往第三方系统引入对消息总线的依赖,这些依赖包括但不仅限于

消息总线优化之PubSub

近段时间,暂缓了消息总线feature的开发,花了部分时间对原先的pubsub机制进行了针对性的优化与重构.这里记录一下优化的过程以及相比原来的设计有哪些改观. PubSub在消息总线内部的作用 PubSub在消息总线内部主要用于对所有在线客户端进行实时管控的作用.每个客户端在使用消息总线时,都"被迫"注册到PubSub上,并"被迫"订阅了一些Channel,以便消息总线管控台实时下发一些管控指令及时生效. 之前的设计回顾 这里有必要回顾一下之前的设计.消息总线内部

消息总线扩展之集成Thrift-RPC

本文主要探讨了消息总线支持Thrift RPC的实现过程.鉴于RabbitMQ官方的Java Client提供了基于RabbitMQ的JSON-RPC,消息总线也顺道提供了JSON-RPC的API.然后也尝试了为消息总线增加对Thrift-RPC的扩展支持,希望此举能让消息总线同时为SOA提供基础设施. Thrift简介 Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目.Thrift通过一个中间语言(IDL, 接口定义语言)来定义

再谈消息总线客户端的多线程实现

上次我谈了最近在写的一个基于RabbitMQ的消息总线的客户端在面对并发问题时的一些思考以及最终的实现方案.那是一种简单并且不容易产生并发问题的方案,如果你看过那篇文章,我曾在最终的实现方案之后给出了其利弊分析. 核心的问题是Client建立的跟RabbitMQ Server的connection是共享还是独占.对于这个问题可以举一个通俗一点的例子:如果你想要租间房子,每个人会有不同的想法.比如有人喜欢简单.安静的生活并且在意个人隐私,那么这个时候你最好的选择就是去租个单室套:里面什么都有,并且

谈消息总线客户端的多线程实现

最近在实现一个基于RabbitMQ的消息总线.因为它提供了Client(客户端),这里就牵扯到凡是技术组件的client都无法回避的并发问题.本文借实现消息总线的client谈谈在实现过程中的想法以及最终的处理方式,当然这些都不仅仅适用于消息总线的client,其他通用组件的client也同样适用. 并发问题的分类 其实上面所提到的并发问题,从大的层面上可以划分为两类问题: 自身固有的并发问题:这个存在的前提条件是client自身内部使用了多线程技术,并且本身就存在线程安全的缺陷. 被动调用的并

消息总线授权设计

我曾在之前的一篇文章中对比过消息队列跟消息总线.它们其中的一个不同点就是:消息总线更关注通信安全,消息总线可以管控通信双方,对通信的管控是建立在授权的基础上.因此授权模型的设计是消息总线必须考虑的问题.所谓的授权,就是校验通信双方有没有建立可信任的通信关系.这篇文章我们来谈谈消息总线的权限设计. 消息总线使用场景及RabbitMQ通信简介 在介绍授权设计之前,我们先了解一些必要信息.通常我们将消息总线应用于以下这些场景: 缓冲类--自生产自消费 解耦类.异步类--生产者消费者模型 服务调用类(R