消息总线之模型重构

简介: 前段时间重新对消息总线的通信模型进行设计&重构,这篇文章谈谈其中的一些想法。 RabbitMQ简介 消息总线对RabbitMQ的官方java client进行了定制、简化。这里首先谈谈RabbitMQ作为一个Message Broker的两个核心概念: Exchange Exchange(交换器),其实你可以简单得将它看成是Router(路由器),路由就是它的主要职责。

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

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

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

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


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

本文作者:vinoYang

本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
3月前
|
存储 缓存 前端开发
构建前端防腐策略问题之防腐层帮助前端实现稳定性兜底难的问题如何解决
构建前端防腐策略问题之防腐层帮助前端实现稳定性兜底难的问题如何解决
|
3月前
|
缓存 负载均衡 监控
Dubbo框架整体认知
该文章主要介绍了Dubbo框架的整体认知,包括Dubbo的概念、产生的背景、解决的问题、架构以及功能特性等。
Dubbo框架整体认知
|
4月前
|
架构师 存储
软件交付问题之在设计领域模型和状态机时,模型和状态机,如何解决
软件交付问题之在设计领域模型和状态机时,模型和状态机,如何解决
|
4月前
|
测试技术 程序员
W模型和瀑布模型与“V”模式开发模型有何异同?
W模型和瀑布模型与“V”模式开发模型有何异同?
130 1
|
4月前
|
存储 Java 数据库连接
业务系统架构实践问题之充血模型在实现上可能会带来问题如何解决
业务系统架构实践问题之充血模型在实现上可能会带来问题如何解决
|
数据采集 网络协议 算法
RPC框架整体架构
RPC就是把拦截到的方法参数,转成可以在网络中传输的二进制,并保证在服务提供方能正确地还原出语义,最终实现像调用本地一样地调用远程的目的。
238 0
|
消息中间件 缓存 负载均衡
架构重构的技巧
对软件代码做任何改动以增加可读性或者简化结构而不影响输出结果。
155 0
|
缓存 Dubbo 应用服务中间件
生产故障|Dubbo泛化调用引发的“血案”
生产故障|Dubbo泛化调用引发的“血案”
生产故障|Dubbo泛化调用引发的“血案”
|
存储 消息中间件 监控
复杂任务中,流程的解耦设计
在系统开发的过程中,必然存在耗时极高的动作,是基于请求响应模式无法解决的问题,通常会采用解耦的思维,并基于异步或者事件驱动的方式去调度整个流程的完整执行。
449 0
复杂任务中,流程的解耦设计
|
设计模式 Java API
策略模式,从防腐层改造聊到Nacos插件的应用
策略模式就像一个工具箱,当我们遇到不同的场景,拿出不同工具。它的好处是 符合开闭原则还有单一原则,当我们需要对另外一种场景进行处理的时候,只需要去打造另一款工具,而不是在之前的工具去修改
279 0
策略模式,从防腐层改造聊到Nacos插件的应用