前段时间实现了一个基于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

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

  1. EBS 消息总线

    http://www.ibm.com/developerworks/cn/webservices/ws-whyesb/ 开发人员为何需要企业服务总线? 本文不仅仅是为架构师准备的:使用企业服务总线 ( ...

  2. Spring Cloud(十一)高可用的分布式配置中心 Spring Cloud Bus 消息总线集成(RabbitMQ)

    详见:https://www.w3cschool.cn/spring_cloud/spring_cloud-jl8a2ixp.html 上一篇文章,留了一个悬念,Config Client 实现配置的 ...

  3. SpringCloud全家桶学习之消息总线---SpringCloud Bus

    一.概述 ConfigClient(微服务)从ConfigServer端获取自己对应的配置文件,但是目前的问题是:当远程git仓库配置文件发生改变时,每次都是需要重启ConfigCient(微服务), ...

  4. 八. SpringCloud消息总线

    1. 消息总线概述 1.1 分布式配置的动态刷新问题 Linux运维修改Github上的配置文件内容做调整 刷新3344,发现ConfigServer配置中心立刻响应 刷新3355,发现ConfigC ...

  5. 使用SignalR为FineUI/Webform打造消息总线

    第一次写博客,语言组织能力不好,请大家多多包涵! 效果图如下: 图片的右下角即为SignalR消息总线的消息框. 一.建立SignalR服务端 第一步:打开一个空的FineUI 4.5空项目文件,在空 ...

  6. 使用SignalR打造消息总线

    使用SignalR为FineUI/Webform打造消息总线 第一次写博客,语言组织能力不好,请大家多多包涵! 效果图如下: 图片的右下角即为SignalR消息总线的消息框. 一.建立SignalR服 ...

  7. (转发)一个通用的C++ 消息总线框架

    注:转自https://www.cnblogs.com/qicosmos/archive/2013/04/28/3048919.html 应用开发过程中经常会处理对象间通信的问题,一般都是对象或接口的 ...

  8. Linux进程内消息总线设计

    文章目录 Windows平台进程内消息总线 如果没有消息总线,会产生什么问题 死循环包含关系 高耦合.低内聚 消息总线 结构图 原理 生产者与总线的关系 总线与消费者的关系 Linux进程内消息总线设 ...

  9. [从源码学设计]蚂蚁金服SOFARegistry之消息总线

    [从源码学设计]蚂蚁金服SOFARegistry之消息总线 目录 [从源码学设计]蚂蚁金服SOFARegistry之消息总线 0x00 摘要 0x01 相关概念 1.1 事件驱动模型 1.1.1 概念 ...

随机推荐

  1. centos 出现的问题

    1.DNS问题,导致yum没得源 echo "nameserver 8.8.8.8">>/etc/resolv.conf 2.CentOS 7最小化安装后找不到‘ifc ...

  2. mybatis的sql语句导致索引失效,使得查询超时

    mybaitis书写sql需要特别注意where条件中的语句,否则将会导致索引失效,使得查询总是超时.如下语句会出现导致索引失效的情况: with test1 as (select count(C_F ...

  3. iOS开发 小知识点

    1/ iOS汉字百分号互相转换. //汉字 NSString * name = @"时间终于将我对你的爱消耗殆尽"; //汉字转为百分比 NSString * encodeStri ...

  4. POJ 1328 Radar Installation 【贪心 区间选点】

    解题思路:给出n个岛屿,n个岛屿的坐标分别为(a1,b1),(a2,b2)-----(an,bn),雷达的覆盖半径为r 求所有的岛屿都被覆盖所需要的最少的雷达数目. 首先将岛屿坐标进行处理,因为雷达的 ...

  5. Android 7.0 Gallery图库源码分析1 - 初识Gallery源码

    分析一个项目的源代码时,第一件事就是查看清单文件,找到程序入口,我们从Gallery2源码的清单文件中可以看到GalleryActivity是此应用的启动Activity. <activity ...

  6. Django Views Decorator

    Django的试图函数的装饰器主要有: HTTP请求方法 条件视图处理 GZip压缩 改变页眉 缓存 官网文档 HTTP请求方法 该装饰器是设置允许访问HTTP协议的方法,装饰器在django.vie ...

  7. Python笔记(28)-----继承

    来自https://blog.csdn.net/sunwukong_hadoop/article/details/80175292 1.Python的继承以及调用父类成员 python子类调用父类成员 ...

  8. IOS - 退出程序

    - (void)exitApplication { OAAppDelegate *app = [UIApplication sharedApplication].delegate; UIWindow ...

  9. BZOJ 4372/3370 烁烁的游戏/震波 (动态点分治+线段树)

    烁烁的游戏 题目大意: 给你一棵$n$个节点的树,有$m$次操作,询问某个节点的权值,或者将与某个点$x$距离不超过$d$的所有节点的权值都增加$w$ 动态点分裸题 每个节点开一棵权值线段树 对于修改 ...

  10. 二、frps 完整配置文件

    # [common] is integral section [common] # A literal address or host name for IPv6 must be enclosed # ...