rabbitMQ–消息中间件
一、什么是MQ?
MQ 是message queue ,消息队列,也叫消息中间件,是再消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。遵守JMS(java message service)规范的一种软件。(同时还有另一个叫AMQP的应用层协议,语言无关性不受产品 语言等限制,rabbitMQ支持这个 )
是类似于数据库一样需要独立部署在服务器上的一种应用,提供接口给其他系统调用。
主要用于各个系统之间通信的解耦。
二、MQ的优缺点
优点:
- 异步处理:相比于传统的串行、并行方式,提高了系统吞吐量。
- 应用解耦:系统间通过信息通信,不用关心其他系统的处理
- 流量削锋:可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求
- 日志处理:解决大量日志传输
- 消息通讯:消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或聊天室等。
详答:
解耦、异步、削峰
缺点:
系统可用性降低:如果在系统正常运行时,强行加入消息队列,如果一旦消息队列挂掉,那么系统就完了,系统的可用性会降低
系统复杂度提高:加入了消息队列,需要考虑多方面问题。譬如:一致性问题、如何保证信息不被重复消费、如何保证信息可靠性的传输等。需要的考虑的东西太多,复杂性增大。
一致性问题:
A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?
三、常见的MQ有哪些?
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
ActiveMQ | RabbitMQ | RocketMQ | KafKa | ZeroMQ | |
---|---|---|---|---|---|
单机吞吐量 | 比RabbitMQ | 2.6w/s(消息做持久化) | 11.6w/s | 17.3w/s | 29w/s |
开发语言 | Java | Erlang | Java | Scala/Java | C |
主要维护者 | Apache | Mozilla/Spring | Alibaba | Apache | iMatix,创始人已去世 |
成熟度 | 成熟 | 成熟 | 开源成本不够成熟 | 比较成熟 | 只有C、PHP等版本成熟 |
订阅形式 | 点对点(p2p)、广播(发布-订阅) | 提供了4种:direct, topic ,Headers和fanout。fanout就是广播模式 | 基于topic、messageTag以及按照消息类型、属性进行正则匹配的发布订阅模式 | 基于topiv进行正则匹配的发布订阅模式 | 点对点(p2p) |
持久化 | 支持少量堆积 | 支持少量堆积 | 支持大量堆积 | 支持大量堆积 | 不支持 |
顺序消息 | 不支持 | 不支持 | 支持 | 支持 | 不支持 |
性能稳定性 | 好 | 好 | 一般 | 较差 | 很好 |
集群方式 | 支持简单集群模式,比如‘主-备’,对高级集群模式支持不好。 | 支持简单集群,‘复制’模式,对高级集群模式支持不好。 | 常用多对‘master-slave’模式,开源版本需手动切换Slave变成Master | 天然的‘Leader-Slave’无状态集群,每台服务器既是Master也是Slave | 不支持 |
管理界面 | 一般 | 较好 | 一般 | 无 | 无 |
四、rabbitMQ的结构以及每个组件的作用
组成部分说明:
Producer 消息生产者,即生产方客户端,将消息发送给MQ
Consumer 消息消费者,即消费者客户端,接收MQ发送的信息
Broker 消息队列服务进程,此服务包括两部分:exchange、queue
Exchange 消息队列交换机,交换机按照一定的规则将消息路由转发到队列,对消息进行过滤。
Queue 消息队列,存储消息,消息到达队列并将消息转发给消费方。
信息发送接收流程:
发送消息:
生产者和broker建立TCP连接 即MQ的Connection
生产者和broker建立通道,通道是通过Connection创建的
生产者通过通道将消息发送给broker,由exchange将消息转发
exchange将信息转发给指定的队列
接收信息:
消费者和broker建立TCP连接
消费者和broker建立通道
消费者监听队列
当消息到达Queue时,broker默认将消息推送给消费者
消费者接收到消息
Broker:接收和分发消息的应用,RabbitMQ Server就是 Message Broker
Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等
Connection:publisher/consumer 和 broker 之间的 TCP 连接
Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销
Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)
Queue:消息最终被送到这里等待 consumer 取走
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据
五、rabbitMQ的模式
(1)简单模式
从图上可以看到只有三个角色:
p 【product】: 生产者 发生消息的
红色[queue]: 队列。 存储消息的
C [consumer]: 消费者 消费消息
导入依赖:
<dependencies>
<!--引入rabbitmq依赖-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>
</dependencies>
生产者代码:
package com.hong.product;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @Author hongCheng
* @Date 2021/4/19 20:14
* @Version 1.0
*/
public class ProductTest {
public static void main(String[] args) throws Exception {
//创建连接大厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置连接主机的ip
factory.setHost("192.168.31.129");
//根据工厂对象获取连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue "队列的名字"
* boolean durable, 是否该队列持久化,rabbitMQ服务重启后该存放是否存在
* boolean exclusive, 是否独占
* boolean autoDelete, 是否自动删除
* Map<String, Object> arguments 额外参数
*/
channel.queueDeclare("hong",true,false,false,null);
//发送消息
/**
* String exchange, 交换机名称,如果没有用”“,会自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定,使用队列的名称
* BasicProperties props, 消息的一些额外配置,不加默认为null
* byte[] body 消息的内容
*/
String body="冬冬";
channel.basicPublish("","hong",null,body.getBytes());
}
}
消费者代码
package com.hong.consumer;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @Author hongCheng
* @Date 2021/4/19 20:46
* @Version 1.0
*/
public class ConsumerTest {
public static void main(String[] args) throws Exception {
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置连接主机的ip
factory.setHost("192.168.31.129");
//根据factory获取得到Connection对象
Connection connection = factory.newConnection();
//根据connection获取信道channel对象
Channel channel = connection.createChannel();
//接收信息
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out