消息中间件MQ知识概括
Posted GeorgeLin98
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消息中间件MQ知识概括相关的知识,希望对你有一定的参考价值。
消息中间件MQ知识概括
MQ简介
MQ的产品种类和对比:
- MQ就是消息中间件。MQ是一种理念,ActiveMQ是MQ的落地产品。不管是哪款消息中间件,都有如下一些技术维度:
①kafka:编程语言是scala,大数据领域的主流MQ。
②rabbitmq:编程语言是erlang,基于erlang语言,不好修改底层,不要查找问题的原因,不建议选用。
③rocketmq:编程语言是java,适用于大型项目。适用于集群。
④activemq:编程语言是java,适用于中小型项目。
MQ的产生背景:
-
系统之间直接调用存在的问题?
微服务架构后,链式调用是我们在写程序时候的一般流程,为了完成一个整体功能会将其拆分成多个函数(或子模块),比如模块A调用模块B,模块B调用模块C,模块C调用模块D。但在大型分布式应用中,系统间的RPC交互繁杂,一个功能背后要调用上百个接口并非不可能,从单机架构过渡到分布式微服务架构的通例。这些架构会有哪些问题? -
系统之间接口耦合比较严重:
①每新增一个下游功能,都要对上游的相关接口进行改造;
②举个例子:如果系统A要发送数据给系统B和系统C,发送给每个系统的数据可能有差异,因此系统A对要发送给每个系统的数据进行了组装,然后逐一发送;当代码上线后又新增了一个需求:把数据也发送给D,新上了一个D系统也要接受A系统的数据,此时就需要修改A系统,让他感知到D系统的存在,同时把数据处理好再给D。在这个过程你会看到,每接入一个下游系统,都要对系统A进行代码改造,开发联调的效率很低。 -
面对大流量并发时,容易被冲垮:
①每个接口模块的吞吐能力是有限的,这个上限能力如果是堤坝,当大流量(洪水)来临时,容易被冲垮。
②举个例子秒杀业务:上游系统发起下单购买操作,就是下单一个操作,很快就完成。然而,下游系统要完成秒杀业务后面的所有逻辑(读取订单,库存检查,库存冻结,余额检查,余额冻结,订单生产,余额扣减,库存减少,生成流水,余额解冻,库存解冻)。 -
等待同步存在性能问题:
①RPC接口上基本都是同步调用,整体的服务性能遵循“木桶理论”,即整体系统的耗时取决于链路中最慢的那个接口。比如A调用B/C/D都是50ms,但此时B又调用了B1,花费2000ms,那么直接就拖累了整个服务性能。 -
根据上述的几个问题,在设计系统时可以明确要达到的目标:
①要做到系统解耦,当新的模块接进来时,可以做到代码改动最小;能够解耦
②设置流量缓冲池,可以让后端系统按照自身吞吐能力进行消费,不被冲垮;能削峰
③强弱依赖梳理能将非关键调用链路的操作异步化并提升整体系统的吞吐能力;能够异步
MQ的主要作用:
- 异步。调用者无需等待。
- 解耦。解决了系统之间耦合调用的问题。
- 消峰。抵御洪峰流量,保护了主业务。
MQ的定义:
-
面向消息的中间件(message-oriented middleware)MOM能够很好的解决以上问题。是指利用高效可靠的消息传递机制与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型在分布式环境下提供应用解耦,弹性伸缩,冗余存储、流量削峰,异步通信,数据同步等功能。
-
大致的过程是这样的:发送者把消息发送给消息服务器,消息服务器将消息存放在若干队列/主题topic中,在合适的时候,消息服务器回将消息转发给接受者。在这个过程中,发送和接收是异步的,也就是发送无需等待,而且发送者和接受者的生命周期也没有必然的关系;尤其在发布pub/订阅sub模式下,也可以完成一对多的通信,即让一个消息有多个接受者。
MQ的特点:
- 采用异步处理模式:
①消息发送者可以发送一个消息而无须等待响应。消息发送者将消息发送到一条虚拟的通道(主题或者队列)上;
②消息接收者则订阅或者监听该爱通道。一条消息可能最终转发给一个或者多个消息接收者,这些消息接收者都无需对消息发送者做出同步回应。整个过程都是异步的。
③也就是说,一个系统跟另一个系统之间进行通信的时候,假如系统A希望发送一个消息给系统B,让他去处理。但是系统A不关注系统B到底怎么处理或者有没有处理好,所以系统A把消息发送给MQ,然后就不管这条消息的“死活了”,接着系统B从MQ里面消费出来处理即可。至于怎么处理,是否处理完毕,什么时候处理,都是系统B的事儿,与系统A无关。 - 应用系统之间解耦合:
①发送者和接受者不必了解对方,只需要确认消息。
②发送者和接受者不必同时在线。 - MQ的缺点:两个系统之间不能同步调用,不能实时回复,不能响应某个调用的回复。
MQ与RPC
传统的http请求存在那些缺点:
-
Http请求基于请求与响应的模型,在高并发的情况下,客户端发送大量的请求达到 服务器端有可能会导致我们服务器端处理请求堆积。
-
Tomcat服务器处理每个请求都有自己独立的线程,如果超过最大线程数会将该请求缓存到队列中,如果请求堆积过多的情况下,有可能会导致tomcat服务器崩溃的问题。
-
所以一般都会在nginx入口实现限流,整合服务保护框架。
-
http请求处理业务逻辑如果比较耗时的情况下,容易造成客户端一直等待,阻塞等待过程中会导致客户端超时发生重试策略,有可能会引发幂等性问题。
-
注意事项:
接口是为http协议的情况下,最好不要处理比较耗时的业务逻辑,耗时的业务逻辑应该单独交给多线程或者是mq处理
。
耗时解决方案对比:
同步发送http请求:
客户端发送请求到达服务器端,服务器端实现会员注册业务逻辑。
①insertMember() --插入会员数据 1s
②sendSms()----发送登陆短信提醒 3s
③sendCoupons()----发送新人优惠券 3s
④总共响应需要6s时间,可能会导致客户端阻塞6s时间,对用户体验
不是很好。
多线程处理业务逻辑:
用户向数据库中插入一条数据之后,在单独开启一个线程异步发送短信和优惠操作,客户端只需要等待1s时间。
①优点:适合于小项目,实现异步
②缺点:有可能会消耗服务器cpu资源资源(线程属于CPU资源),没有实现解耦。
Mq处理业务逻辑:
先向数据库中插入一条会员数据,让后再向MQ中投递一个消息,MQ服务器端在将消息推送给消费者异步解耦处理发送短信和优惠券。
①优点:MQ可以实现异步/解耦/流量削峰问题,适合大项目。
远程调用服务(RPC)和消息(Message Queue)对比:
- 结构对比:
RPC系统结构:
+----------+ +----------+
| Consumer | <=> | Provider |
+----------+ +----------+
Consumer调用的Provider提供的服务。
Message Queue系统结构:
+--------+ +-------+ +----------+
| Sender | <=> | Queue | <=> | Receiver |
+--------+ +-------+ +----------+
Sender发送消息给Queue;Receiver从Queue拿到消息来处理。
- 功能特点:
在架构上,RPC和Message的差异点是,Message有一个中间结点Message Queue,可以把消息存储
。 - 消息的特点:
①Message Queue把请求的压力保存一下,逐渐释放出来,让处理者按照自己的节奏来处理。
②Message Queue引入一下新的结点,让系统的可靠性会受Message Queue结点的影响。
③Message Queue是异步单向的消息。发送消息设计成是不需要等待消息处理的完成。
④所以对于有同步返回需求,用Message Queue则变得麻烦了
。 - RPC的特点:
①同步调用,对于要等待返回结果/处理结果的场景,RPC是可以非常自然直觉的使用方式
。
②RPC也可以是异步调用。由于等待结果,Consumer(Client)会有线程消耗
。
③如果以异步RPC的方式使用,Consumer(Client)线程消耗可以去掉。但不能做到像消息一样暂存消息/请求,压力会直接传导到服务Provider
。 - 适用场合说明:
①希望同步得到结果的场合,RPC合适。
②希望使用简单,则RPC;RPC操作基于接口,使用简单,使用方式模拟本地调用。异步的方式编程比较复杂。
③不希望发送端(RPC Consumer、Message Sender)受限于处理端(RPC Provider、Message Receiver)的速度时,使用Message Queue。 - 详细链接:远程调用服务(RPC)和消息(Message Queue)对比及其适用/不适用场合
消息中间件简介:
- 消息中间件基于队列模型实现异步/同步传输数据
- 作用:可以实现
支撑高并发
、异步解耦
、流量削峰
、降低耦合度
。 - Mq应用场景有那些:
①异步发送短信
②异步发送新人优惠券
③处理一些比较耗时的操作
JMS
JMS简介:
- JMS(java message service):由Sun公司早期提出的消息标准,是一个Java平台中关于面向消息中间件(MOM)的API,旨在为java应用提供统一的消息操作。
- JMS 定义了JAVA API层面的标准,在java体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差。
- 提供两种消息模型:
①Peer-2-Peer
②Pub/sub
AMQP与JMS对比:
AMQP为消息定义了线路层(wire-level protocol)的协议,而JMS所定义的是API规范。JMS的API协议能够确保所有的实现都能通过通用的API来使用,但是并不能保证某个JMS实现所发送的消息能够被另外不同的JMS实现所使用。而AMQP的线路层协议规范了消息的格式,消息在生产者和消费者间传送的时候会遵循这个格式。这样AMQP在互相协作方面就要优于JMS——它不仅能跨不同的AMQP实现,还能跨语言和平台。
- 相比JMS,AMQP另外一个明显的优势在于它具有更加灵活和透明的消息模型。使用JMS的话,只有两种消息模型可供选择:点对点和发布-订阅。这两种模型在AMQP可以实现,而且AMQP还能够以其他的多种方式来发送消息,这是通过将消息的生产者与存放消息的队列解耦实现的。
- 在JMS中,有三个主要的参与者:消息的生产者、消息的消费者以及在生产者和消费者之间传递消息的通道(队列或主题)。
- 在JMS中,通道有助于解耦消息的生产者和消费者,但是这两者依然会与通道相耦合。生产者会将消息发布到一个特定的队列或主题上,消费者从特定的队列或主题上接收这些消息。通道具有双重责任,也就是传递数据以及确定这些消息该发送到什么地方,队列的话会使用点对点算法发送,主题的话就使用发布-订阅的方式。
AMQP的生产者并不会直接将消息发布到队列中。AMQP在消息的生产者以及传递信息的队列之间引入了一种间接的机制:Exchange。
①消息的生产者将信息发布到一个Exchange。Exchange会绑定到一个或多个队列上,它负责将信息路由到队列上。信息的消费者会从队列中提取数据并进行处理。
②AMQP定义了四种不同类型的Exchange,每一种都有不同的路由算法,这些算法决定了是否要将信息放到队列中。根据Exchange的算法不同,它可能会使用消息的routing key和/或参数,并将其与Exchange和队列之间binding的routing key和参数进行对比。(routing key可以大致理解为Email的收件人地址,指定了预期的接收者。)如果对比结果满足相应的算法,那么消息将会路由到队列上。否则的话,将不会路由到队列上。
③四种标准的AMQP Exchange如下所示:
<1>Direct:如果消息的routing key与binding的routing key直接匹配的话,消息将会路由到该队列上;
<2>Topic:如果消息的routing key与binding的routing key符合通配符匹配的话,消息将会路由到该队列上;
<3>Headers:如果消息参数表中的头信息和值都与bingding参数表中相匹配,消息将会路由到该队列上;
<4>Fanout:不管消息的routing key和参数表的头信息/值是什么,消息将会路由到所有队列上。
JMS与MQ区别:
- JMS:
①JMS即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
②JMS是一种与厂商无关的 API,用来访问消息收发服务(消息中间件)。它类似于JDBC(Java DatabaseConnectivity):这里,JDBC 是可以用来访问许多不同关系数据库的API,而JMS同样提供与厂商无关的访问方式来访问消息收发服务,许多厂商目前都支持JMS,包括 IBM 的 MQSeries、BEA的 Weblogic JMS service和 Progress 的 SonicMQ,这只是几个例子。 JMS 使您能够通过消息收发服务从一个 JMS 客户机向另一个JMS客户机发送消息。消息是 JMS 中的一种类型对象,由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体则携带着应用程序的数据或有效负载。根据有效负载的类型来划分,可以将消息分为几种类型,它们分别携带:简单文本(TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。 - MQ:
①MQ全称为消息队列(Message Queue), MQ是一种应用程序与应用程序之间的通信方法。应用程序通过消息队列来通信,而无需专用连接来链接它们。消息传递指的是程序通过在消息队列中收发数据进行通信,而不是通过直接调用彼此来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM的WEBSPHERE MQ。 - 二者之间的关系:
①JMS是一个用于提供消息服务的技术规范,它制定了在整个消息服务提供过程中的所有数据结构和交互流程。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的消息服务提供者。MQ的实现可以基于JMS,也可以基于其他规范或标准,其中ActiveMQ就是基于JMS规范实现的消息队列。
Kafka与JMS:
- Apache Kafka是一个开源消息系统,由Scala写成。是由Apache软件基金会开发的一个开源消息系统项目,目标是为处理实时数据提供一个统一、高通量、低等待的平台,Kafka被广泛地应用于各种流式计算中。
Kafka提供了类JMS的特性,但在设计实现上并不遵循JMS规范,Kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)称为broker。同时无论是kafka集群,还是producer和consumer都依赖于zookeeper集群保存一些meta信息,来保证系统可用性。
MQ协议
MQ协议简介:
AMQP协议:
①AMQP即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
②优点:可靠、通用- MQTT协议:
①MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
②优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统 - STOMP协议:
①STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。
②优点:命令模式(非topic\\queue模式) - XMPP协议:
①XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
②优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大 其他基于TCP/IP自定义的协议:
①有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP\\IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。
MQ方案对比
主流mq区别对比:
MQ方案比较:
activemq:
①虽然是java写的消息队列,但是提供Java, C, C++, C#, Ruby, Perl, Python, php各种客户端,所以语言上是没什么问题的。配置和使用,基本上是java xml这一套。同时对jms、spring之类的支持很友好。
②而且因为是Java写的,所以可以作为一个jar包,放到java项目里,用代码启动和配置,这个对于java开发者而言是不是相当爽?毕竟还是有些场景,需要我们把队列放到自己项目内部,随项目启动而启动的。而且,还可以类似拓展tomcat一样,自己写java的plugin来拓展activemq。比如说,我有10万硬件连到mq上,这10万设备每个都有用户名密码,这个时候我们可以用java写个权限验证,从数据库里查这10万用户名密码。
③activemq支持主从复制、集群。但是集群功能看起来很弱,只有failover功能,即我连一个失败了,可以切换到其他的broker上。这一点貌似不太科学。假设有三个broker,其中一个上面没有consumer,但另外两个挂了,消息会转到这个上面来,堆积起来。看样子activemq还在升级中。
④activemq工作模型比较简单。只有两种模式 queue,topics 。
⑤queue就多对一,producer往queue里发送消息,消费者从queue里取,消费一条,就从queue里移除一条。如果一个消费者消费速度不够快怎么办呢?在activemq里,提供messageGroup的概念,一个queue可以有多个消费者,但是他们得标记自己是一个messageGroup里的。这样,消息会轮训发送给每个消费者,也就是说消费者不会重复消费同一条消息。但是每条消息只被消费一次。
⑥topics就是广播。producer往broker发消息,每个消息包含topic。消费者订阅感兴趣的topic,所有订阅了topic的消费者都会收到消息。当然订阅分持久不持久。持久订阅,当消费者断开一会,再连上来,仍然会把没收到的消费发过来。不持久的订阅,断开这段时间的消息就收不到了。
⑦activemq支持mqtt、ssl。rabbitmq:
①rabbitmq用erlang写的。安装完才10m不到,在windows上使用也非常方便,在这点上完爆了activemq,java又臭又长没办法啊。rabbitmq给我感觉更像oracle,功能非常强大。安装完,也有实例的概念,可以像建数据库一样,建实例,建用户划权限。同时监控系统也很好用。这些都是好处,同时也是累赘,整体上来说rabbitmq比activemq复杂太多了。
②从机制上来讲,rabbitmq也有queue和topic的概念,发消息的时候还要指定消息的key,这个key之后会做路由键用。但是多了一个概念叫做交换器exchange。exchange有四种,direct、fanout、topic、header。也就是说,发消息给rabbitmq时,消息要有一个key,并告诉他发给哪个exchange。exchange收到之后,根据key分发或者广播给queue。消费者是从queue里拿消息的,并接触不到交换机。
Kafka:
①Apache下的一个子项目,使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统,具有以下特性:
<1>快速持久化:通过磁盘顺序读写与零拷贝机制,可以在O(1)的系统开销下进行消息持久化;
<2>高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
<3>高堆积:支持topic下消费者较长时间离线,消息堆积量大;
<4>完全的分布式系统:Broker、Producer、Consumer都原生自动支持分布式,依赖zookeeper自动实现复杂均衡;
<5>支持Hadoop数据并行加载:对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。
②kafka中只有topic,但是每个topic可以有很多partition分区。
上图中kafka集群由两台机器组成。topic被分成四个分区,server1维护p0,p3。 在kafka中,每个消费者都要指出自己属于哪个consumerGroup,每个consumer可以读取多个partition。但是一个partition在同一个consumerGroup中,只会被一个consumer消费。以此保证不会重复消费。而且在一个partition中,消息被消费的顺序是可保障的。上图中,consumer group A 由两个consumer组成,因此一个consumer可以消费两个partition。如果要保证严格的顺序性,那么就要像consumer group B一样,每个consumer只消费一个partition。kafka和rabbitmq及activemq机制上略有区别。rabbitmq和activemq都是消费后就删除消息,没有重复消费的功能,而kafka 队列中的内容按策略存储一定时间,消费者通过指定偏移量来读取数据。如果使用基础api可以从任意位置读取。kafka同时提供高级api,即kafka来维护每个消费者当前读到什么位置了,下次再来,可以接着读。RocketMQ:
①阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,是阿里参照kafka设计思想使用java实现的一套mq。同时将阿里系内部多款mq产品(Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,目前主要多用于订单交易系统。
②具有以下特点:
<1>能够保证严格的消息顺序
<2>提供针对消息的过滤功能
<3>提供丰富的消息拉取模式
<4>高效的订阅者水平扩展能力
<5>实时的消息订阅机制
<6>亿级消息堆积能力总体而言:
①我感觉kafka安装使用最简单,同时如果有集群要求,那么kafka是当仁不让的首选。尤其在海量数据,以及数据有倾斜问题的场景里,因为partition的缘故,数据倾斜问题自动解决。比如个别Topic的消息量非常大,在kafka里,调整partition数就好了。反而在rabbitmq或者activemq里,这个不好解决。
②rabbitmq是功能最丰富,最完善的企业级队列。基本没有做不了的,就算是做类似kafka的高可用集群,也是没问题的,不过安装部署麻烦了点。
③activemq相对来说,显的老套了一些。不过毕竟是java写的,在内嵌到项目中的情况下,或者是简单场景下,还是不错的选择。- 链接:activemq、rabbitmq、kafka原理和比较
消息消费方式
消息消费通常有2种方式:
- push方式
- pull方式
push方式:
- push方式的过程:
①mq接收到消息
②mq主动将消息推送给消费者(消费者需提供一个消费接口)
③mq属于主动方,消费者属于一种被动消费,一旦有消息到达mq,会触发mq推送机制,将消息推送给消费者,不管消费者处于何种状态。 - push方式优点:
①消费者代码较少:对于消费者来说,只需提供一个消费接口给mq即可;mq将接收到的消息,随即推送到指定的消费接口
②消息实时性比较高:对于消费者来说,消息一旦到达mq,mq会立即推送给消费者 - push方式缺点:
①消费者属于被动方,消息量比较大时,对消费者性能要求比较高;若消费者机器资源有限,可能会导致压力过载,引发宕机的情况。
②对消费者可用性要求比较高:当消费者不可用时,会导致很push失败,在mq方需要考虑至少推送成功一次,这块的设计下章节会做说明。
pull方式:
- pull方式过程:
①消费端采用轮询的方式,从mq服务中拉取消息进行消费
②消费完成通知mq删除已消费成功的消息
③继续拉取消息消费
④对于消费者来说,是主动方,可以采用线程池的方式,根据机器的性能来增加或缩小线程池的大小,控制拉取消息的速度,可以很好的控制自身的压力。 - pull方式优点:
①消费者可以根据自己的性能主动控制消息拉去的速度,控制自己的压力,不至于把自己弄跨
②实时性相对于push方式会低一些
③消费者属于主动方,控制权更大一些 - pull方式缺点:
①消费方需要实现消息拉取的代码
②消费速度较慢时,可能导致mq中消息积压,消息消费延迟等
pull方式和push方式选择建议:
- 消费者性能较好,对实时性要求比较高的,可以采用push的方式
- 消费者性能有限,建议采用pull的方式
- 整体上来说,主要在于消费者的性能,机器的性能如果没有问题,push和pull都是可以的。
- 通常我们在Spring中使用的RabbitMQ、RocketMQ,Kafka都是采用pull的方式,消费端会启动多个消费者,不断的从MQ中拉取消息进行消费。RocketMQ 和 Kafka 都是利用“长轮询”来实现拉模式。
- 当然业界也有基于推模式的消息队列如 ActiveMQ。
- 链接:消息队列之推还是拉,RocketMQ 和 Kafka 是如何做的?
以上是关于消息中间件MQ知识概括的主要内容,如果未能解决你的问题,请参考以下文章