消息总线VS消息队列

前段时间实现了一个基于RabbitMQ的消息总线,实现的过程中自己也在不断得思考、总结以及修正。需要考虑各个维度:效率、性能、网络、吞吐量、甚至需要自己去设想API可能的使用场景、模式。不过能有一件事情,自己愿意去做,在走路、吃饭、坐公交的时候都在思考如何去改进它,然后在实践的过程中,促使去思考并挖掘自己知识面的空白,也是一件让人开心的事情。

借此记录下自己在实现的过程中,以及平时的一些想法。

这是第一篇,先谈谈消息总线跟消息队列的区别,以及对于企业级应用需要将消息队列封装成消息总线的必要性。

消息总线跟消息队列有何区别?如果有人问你这个问题,你的答案是什么?如果你的消息总线是基于一个已经相当成熟的消息队列或者消息系统做二次封装。为什么需要用你的客户端,而不直接用原始的(这是一个大家都相信权威的时代,请注意这里用的是相信,而不是迷信,你确实应该相信权威,至少比相信一个新手来得靠谱,当然我这里指的权威,是正面的意思)?

那么我从以下几点来谈谈我对这个问题的思考:

  • 消息队列clientAPI权限太大,clientAPI信任级别太高
  • 消息队列clientAPI面向技术,消息总线clientAPI面向技术+业务
  • 消息队列无法隐藏通信细节
  • 消息队列无法实施实时管控
  • 总线的优势:统一入口,简化拦截成本

这里为了理解简单,你就暂且先把RabbitMQ当做是个消息队列,其实它不只是个消息队列,其他的一些基于JMS的消息队列对于回答这个问题而言,也能成立。

(1)消息队列clientAPI权限太大,信任级别太高

这一点不仅仅是哪一个服务端组件的客户端driver的实现是这样,绝大部分其实都是这样的,它们的client其实是对服务端组件(或者称之为服务器)协议的翻译。这些服务器大都带有commandline interface(这几乎是标配)。其实,CLI跟在程序中使用的各种语言的client库没有区别本质区别,它们相对于server而言都是client——都是对server实现的protocol的翻译或者转换,而这些API都是对这些包装过的协议的调用。因此它们都存在一些“management”形式的接口:比如create,delete,remove某个component之类的。没错,你去看所有带client的组件的实现,它们都包含了这些API(这不是对错的问题,这些client本身就没有也不应该假设你的使用场景)。比如你看看redis的client:jedis——它甚至具备了flushAll,flushDB的功能(清空所有redis数据),除了能关闭server还有什么事它不能做?而就RabbitMQ而言,它的officialnative java client,可以创建/删除其通信的核心组件:exchange,queue。你能直接将这些client散布到各个业务系统里去而不加阻拦?你当然有必要做二次封装以移除这些高危的managementAPI。

(2)消息队列clientAPI面向技术而消息总线clientAPI面向技术+业务

消息队列的clientAPI大都面向协议、通信实现,面向可用性以及高性能,如果归类一下那就是面向技术,除了通信场景它不会去模拟业务场景。而消息总线需要带着业务场景去实现需要支持的机制。

当你去搜索任何一个消息队列的时候,它的advantage里都有一条:生产者与消费者解耦,就像下面这样:

就生产者跟消费者模型而言,这确实是消息队列的优势。不过这种优势也被限制在一些特定的使用场景下,比如:单一业务的消息排队处理。因此通用消息队列的场景更适用于单一职责的生产者跟消费者模型;而我们期待的消息总线却是企业里各个系统中消息的通信,侧重点在于通信上。消息队列只是提供了一种非常适合于消息通信的实现机制(消息有序,消息缓存等),因此消息总线是在消息队列提供的技术支撑上封装出适合消息交互的业务场景。

(3)消息队列无法隐藏通信细节

对于企业内的系统交互,我们希望它尽可能保证数据的安全性。而数据通常都暂存在队列中,因此保证数据的安全性就顺其自然得转变成保证消息队列访问的安全性:你总是不应该让没有经过授权的客户端去访问本不应该访问到的队列。可惜的是RabbitMQ官方的客户端达不到这种要求,它要访问一个队列,需要知道真实队列的名称,需要知道其路由路径。而就连接一个队列而言,我们认为它提供了太多的信息,但这是没办法的事情,因为它的exchange以及queue的混搭机制非常灵活,所以你得提供一个称之为routingkey的路由路径。而不管怎样,如果你把这个信息开放给调用端去填写,几乎肯定会暴露你服务端exchange以及queue的路由机制以及拓扑结构。因此我们需要做什么?我们需要找到一种通信机制,让它对外只需要知道有个proxy节点,而不需要去关注真实的queue的名称;然后想一个办法把其routingkey隐藏在消息总线内部。

(4)消息队列无法实施实时管控

如果你在企业内各个系统之间引入消息总线,很显然访问控制是必须提供的。比如对某个队列实施消息大小限制,激活/禁用某个队列等。

之前我们提到过消息队列不是面向业务的,它自身没有过多得考虑数据的安全性以及对访问的安全控制机制。而且我们也几乎很难去改造一个消息队列的服务端实现,除非它是基于拦截器/插件模式的。即便RabbitMQ是支持插件的,但对于erlang这样一个受众不是特别广泛的语言,你去给它写插件一不小心就会走到坑里去,并且RabbitMQ官方也已经申明了它们十分不建议你自己去编写插件。考虑到诸多不便,我们只能在客户端上做文章。毫无疑问,我们的实时管控信息还是必须存储在服务端(只是它是一个独立的服务端),但原生的client很显然是不支持这种机制的,因此我们需要在原生client外部封装订阅实时管控信息以及实施访问控制的逻辑代码。

(5)总线的优势:统一入口,简化拦截成本

无论是消息总线还是服务总线,其实所谓的总线就是进行先收拢再发散的过程。先收拢,从统一的入口进去,完成必要的统一处理逻辑;再发散,按照路由规则,路由到各个组件去处理。事实上这就是代理的作用:屏蔽内部细节,对外统一入口。在基于代理的基础上,我们可以对消息总线上所有的消息做日志记录(因为所有消息的通信都必须经过代理),并且还是在不切断RabbitMQ自身Channel的基础上,而如果想在路由上实现一个Proxy,那基本上离不开一个树形拓扑结构。

写在最后

这篇主要谈了消息总线跟消息队列的区别。其实市面上已经有一些成熟的消息队列可以开箱即用,如果你针对消息队列来封装出一个消息总线,总有人会认为是否有这个必要性。如果没有这些开源的消息队列,那么完全有你自己来实现消息总线的话,你还是需要实现出一个跟市面上类似的MQ或者MessageBroker(见POSA卷4),因此消息队列只是实现消息总线的基础,或者是它的消息通信方式;而选择基于一些成熟的MessageBroker来进行开发,既能省去很多的工作量,又能享有它们提供的稳定性以及社区的贡献。

如果你现在就希望看到它的实现机制,可以移步到github

时间: 2024-12-20 17:55:51

消息总线VS消息队列的相关文章

消息总线扩展之主动转发

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

消息总线的应用场景

应用场景 分布式事务 分布式系统组件相互通信 数据复制 日志同步 delay queue 广播通知 介绍 消息总线是一种通信工具,可以在机器之间互相传输消息.文件等. 消息总线扮演着一种消息路由的角色,拥有一套完备的路由机制来决定消息传输方向.发送段只需要向消息总线发出消息而不用管消息被如何转发,为了避免消息丢失,部分消息总线提供了一定的持久化存储和灾备的机制. [多机房同步方案] 通过消息广播方式将数据多点分布 数据提交给一个代理,这个代理帮我们把这些数据同步到多个机房,那我们应用不需要关心这

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

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

微服务实战(二):落地微服务架构到直销系统(构建消息总线框架接口)

从上一篇文章大家可以看出,实现一个自己的消息总线框架是非常重要的内容,消息总线可以将界限上下文之间进行解耦,也可以为大并发访问提供必要的支持. 消息总线的作用: 1.界限上下文解耦:在DDD第一波文章中,当更新了订单信息后,我们通过调用经销商界限上下文的领域模型和仓储,进行了经销商信息的更新,这造成了耦合.通过一个消息总线,可以在订单界限上下文的WebApi服务(来源微服务-生产者)更新了订单信息后,发布一个事件消息到消息总线的某个队列中,经销商界限上下文的WebApi服务(消费者)订阅这个事件

分布式消息总线

消息总线是一种通信工具,可以在机器之间互相传输消息.文件等. 消息总线扮演着一种消息路由的角色,拥有一套完备的路由机制来决定消息传输方向.发送段只需要向消息总线发出消息而不用管消息被如何转发,为了避免消息丢失,部分消息总线提供了一定的持久化存储和灾备的机制. 分布式消息总线比较 开源消息总线ActiveMQ

一个通用的C++ 消息总线框架

应用开发过程中经常会处理对象间通信的问题,一般都是对象或接口的依赖和引用去实现对象间的通信,这在一般情况下是没问题的,但是如果相互通信的对象很多,可能会造成对象间的引用关系像蜘蛛网一样,这样会导致对象关系很复杂,难以维护的问题,解决这个问题的一个好方法是通过消息总线去解耦对象间大量相互引用的紧耦合的关系. 设计思路:被通信对象向消息总线发布一个主题,这个主题包含消息主题.消息类型和消息处理函数,消息主题标示某个特定的主题,消息类型用来区分标示这个主题会响应某个特定的消息,消息处理函数用来响应该主

微服务实战(四):落地微服务架构到直销系统(将生产者与消费者接入消息总线)

前一篇文章我们已经完成了基于RabbitMq实现的的消息总线,这篇文章就来看看生产者(订单微服务)与消费者(经销商微服务)如何接入消息总线实现消息的发送与消息的接收处理. 定义需要发送的消息: 下单消息要被发送到消息总线,并被经销商微服务的处理器处理.经销商微服务处理时,需要知道要对哪个经销商处理多少的PV值与电子币余额.这些信息就是事件消息需要承载的重要信息. public class OrderCreatedProcessDealerEvent:BaseEvent { public deci

Linux进程内消息总线设计

文章目录 Windows平台进程内消息总线 如果没有消息总线,会产生什么问题 死循环包含关系 高耦合.低内聚 消息总线 结构图 原理 生产者与总线的关系 总线与消费者的关系 Linux进程内消息总线设计 使用进程间实时信号来实现进程内消息总线 参考文档 整体流程 主线程注册总线消息处理函数 生产者线程产生并发送消息到总线 接收并处理消息 总线接收到消息 总线消息处理函数 消费者总线消息处理虚函数 消费者消息处理实函数 核心原理 sigqueue发送消息 sigaction处理消息 存在的问题 消

SpringCloud(Greenwich版)Bus消息总线

一.Bus简介 是什么: Spring Cloud Bus 使用轻量级的消息代理(目前只支持两种,RabbitMQ 和 Kafka)连接分布式系统的节点,这样就可以广播传播状态的更改(例如配置的更新)或者其他的管理指令.可将 Spring Cloud Bus 想象成一个分布式 Spring Boot Actuator,也可以当做微服务间的通信通道. 为什么被称为消息总线: 在微服务系统架构中,通常会使用轻量级的消息代理来构建一个共用的消息主题(订阅某学校的微信公众号 topic),并让系统中所有