首先,一个拥有信息化建设积累的公司内部可能同时运行着多个不同的业务系统,而这些系统可能基于不同操作系统,不同存储数据的数据库等,若需要将这些信息系统可以结合成一个协同工作的整体,实现企业跨平台、分布式应用,就需要一个中介者完成这个使命,而中间件便是天选之子,它处于操作系统和应用程序之间,它是脱离了具体的设计目标,而具备提供普遍独立功能需求的模块(可替换),屏蔽了底层操作系统的复杂性,使得开发人员面对简单而统一的开发环境,将注意力放在业务的实现上。中间件的使用不仅是对于开发而言更加简便,也降低了后续的系统维护成本。常用的中间件包括负载均衡中间件(Nginx、CDN),分布式消息中间件(RabbitMQ、Kafka),缓存中间件(MemCache、Redis),缓存中间件(Mycat、ShardingJdbc),等
注意:项目架构和重构中国,需要谨慎思考和斟酌,因为任何技术的融入和变化都会增加相应成本,建议初创公司或者业务比较单一等先选择单体架构即可,后续随着业务或者项目不断驱动再加入对应的技术
在理解消息中间件是什么之前,我们先普及一下单体架构和分布式架构区别;
单体架构
公司开发初期架构中,基本都采取的是单体架构模式,他的特点就是把所有业务和模块代码放在一个工程下。单体架构本质就是我们发起一个请求,由JVM分配线程并调用需要使用的服务串行处理响应。引发的问题就是:我们升级其中一个模块或者迭代都需要整个项目重新编译和重新发布。这种架构存在的问题包括:代码耦合度太高,运维成本高,不利于升级架构
分布式架构
分布式架构的使用是建立在业务驱动下使用,相比单体架构,分布式架构中一个请求则是由多个系统(服务)共同协同完成,JVM和环境都是独立的。优势在于:a.合理分配服务资源 b.系统独立维护和部署,耦合度低,可插拔 c.每个系统的架构所使用的技术栈可灵活选择(比如Java/PHP/GO) d.弹性部署,不会造成平台因部署造成瘫痪
缺点:a.学习成本高,技术栈多 b.人员/运维/服务器成本增高 c.系统面临的容错性也会成倍增加
消息中间件是在消息传输过程中保存消息的一种容器,主要应用于分布式系统之间进行通信.通信过程中服务之间采取异步方式,被用于缓冲或缓解高峰期工作负载等业务场景
分布式架构中消息中间件示例从示意图可以了解什么是消息中间件(本质就是接收数据->存储数据->发送数据):
1.利用可靠的消息传递机制进行各个服务间的通讯
2.利用消息传递和排队机制,在分布式架构中扩展服务间通讯
消息中间件的组成部分
1.消息队列协议
消息队列协议都是基于TCP/IP协议实现的一种约定俗成的规范,目的是让客户端(Java/Go/Python)进行无障碍通讯,且这种协议下的各种实现必须具有持久性、高可用、高可靠性能。常见的消息队列协议包括:AMQP、MQTT、kafka、OpenMessage
AMQP协议-实现者RabbitMq、ActiveMQ
MQTT协议-物联网重要组成部分
OpenMessage协议-实现者RocketMQ(谨慎,万一公司倒闭,若研发能力不足则需要重构)
kafka协议-实现者Kafka(由于没有定义复杂的报文头,基于二进制传输更接近于底层,但不支持事务)
彩蛋:为什么消息中间件不直接使用 http 协议?
因为 http 请求报文头和响应报文头是比较复杂的,包含了cookie、数据的加密解密、状态码、响应码等附加的功能;对于消息而言,并不需要这么复杂的附加功能,目的只有负责数据传递、存储、分发,只需要简洁,快速保证高性能即可。
大部分情况下 http 都是短链接,当请求到响应这个过程出现中断后不会进行持久化,会造成请求的丢失。而对于消息中间件的业务场景,当出现问题和故障要时必须对数据或进行持久化,目的就是为了保证消息和数据的高可靠和稳健的运行
2.消息队列持久化
将消息数据存储到磁盘,使得数据可以永久保存,而不是存储在内存随服务器重启断开而消失
消息数据持久化3.消息的分发策略
MQ消息包含了生产者、存储消息、消费者,当生产者生成消息以及MQ存储数据到磁盘后,消费者是如何获取消息的呢?其实MQ采取的是一种推送机制,而消息推送分发的策略包括如下几种:
消息分发策略对比其中:
a.发布订阅:所有MQ实现者均支持,对于MQ队列的数据,都会推送给每一个消费者 ,比如有90条数据,那么每个消费者都会收到90条数据
b.轮询分发:MQ按照公平分发给每个消费者(不考虑各消费者服务性能的高低),且不会重复消费数据,但不会按照顺序消费。比如:有90条数据,那么每个消费者都会收到30条数据
c.公平分发:不会重复消费数据,但相比轮询分发,它会依据各消费者服务器性能有所倾斜,即能者多劳。比如:消费者甲qp为1000/ms,乙为100/ms,丙为300/ms,则对于90条数据,可能甲会消费5条,乙消费50条,丙消费35条
d.重发:当某个消费者出现异常故障,则队列会出现消息堆积,当消费者正常后,可重新发送消息,保证高可靠。但Kafka不支持重发
4.消息队列高可用和高可靠
高可用:指的是服务在某条件或时刻下处于可执行状态的能力,一般采取集群部署保证高可用
集群模式1-Master-slave主从共享数据部署
生产者将数据发送到Master消息队列节点,所有Slave节点和master共享存储数据,一旦master节点故障无法写入,则由slave节点继续服务,保证高可用,适合小型项目。但共享的数据一旦丢失,那么master和slave就没有意义了,所以出现了主从同步或者主从复制模式
Master-slave主从共享集群模式2-Master-slave主从同步数据部署
这种写入数据依然在master节点上,但同时master会同步数据到slave节点形成副本,对于存在多个消费者可以达到负载均衡效果,同时消息的拷贝和同步也会占用比较多的带宽和网络资源
Master-slave主从同步数据集群模式3-多主集群同步数据部署
和模式2区别在于,写入时可以采用任意的节点,这样每个都相当于主节点
多主集群同步数据集群模式4-多主集群转发数据部署
多主集群转发数据集群模式5-Master-slave和broke-cluster组合部署
Master-slave和broke-cluster组合部署总结:在不同业务中保证消息高可用有三种方式:消息共享,消息同步,元数据共享(大型项目)
高可靠:指的是系统服务长期无故障持续运行,比如系统突然崩溃等并不影响线上业务正常运行
实现中间件可靠性:
a.消息的传输:通过协议保证系统间数据解析正确性
b.消息的存储:通过持久化来保证消息可靠性
a. 异步:消息本身是异步的,它允许在生产者发送消息很长时间后再由消费者接收消息,而不需要等待响应;如果不用 MQ 的话,只能通过调用接口的方式进行同步处理.这样会存在如下问题:一是会阻塞,像我们系统提醒业务处理过程通常要 1-2 秒,如果一下来了太多请求,可能是处理不过来的,后面的请求只能一直等甚至超时;而 MQ 支持消息堆积,很好解决了这个问题; 二是调用失败无法自动重试,MQ 可以很容易实现失败重试
b. 解耦:消息队列减少了消息之间的耦合性,不同的服务之间通过消息队列通信不需要关心彼此之间的实现细节,只要定义好消息格式即可
如果用调用接口方式,调用三次接口,也不是不可以实现.但是如果后期 B 系统说你不需要推送给我了.我这边是不是需要删除掉推送给 B 的代码,这就是代码耦合了
c. 广播:我们只要将消息送到队列即可,减少联调和开发的工作量
d. 流量削峰与流控:当上下游系统处理能力存在差距的时候,利用消息队列做一个通用的 ”载体”.在下游有能力处理的时候,再进行分发与处理
a. 系统可用性降低: 如果 MQ 出现问题,就会导致使用到MQ的服务报错,最终导致系统崩溃
b. 系统复杂度提高: 加入MQ后,需要考虑如何保证消息的顺序性?消息传递过程中如何保证不被重复消费?保证消息不会丢失?等等问题
c. 一致性问题: 比如服务A给服务B、C、D发送了消息,只有都成功才返回成功;若只有C成功,其他失败,但是返回成功,这就出现一致性问题
RabbitMQ: 基于Erlang开发,并发能力强,微妙级别延时;吞吐量属于万级别,稍逊与十万级别的RockerMQ和Kafaka;更适合并发量不高的业务场景
RockerMQ: 阿里基于Java开发的产品,可以根据源码来定制公司的MQ,公司有技术实力我觉得用 RocketMQ 挺好
Kafka :超高吞吐量,毫秒级延迟,高可用和高可靠,适用于大数据领域中的实时计算以及日志采集场景
1.跨系统进行数据的分发和异步处理,比如发送邮件、推送短信等
2.高并发的流量削峰
3.大数据分析与传递,比如大数据领域的实时分析
高并发流量削峰示意图