1、简介
2、作用
2.1 流量消峰
2.2 应用解耦
![](https://img-blog.csdnimg.cn/4a6979e608bd4dcdb46d951c5f26b235.png)
2.3异步处理
![](https://img-blog.csdnimg.cn/af80911d7a074d8da1cf909fc35c81ce.png)
3、MQ 的分类
3.1ActiveMQ
3.2 .Kafka
优点: 性能卓越,单机写入 TPS 约在百万条 / 秒,最大的优点,就是吞 吐量高 。时效性 ms 级可用性非常高,kafka 是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 , 消费者采用 Pull 方式获取消息 , 消息有序 , 通过控制能够保证所有消息被消费且仅被消费一次 ; 有优秀的第三方 KafkaWeb 管理界面 Kafka-Manager ;在日志领域比较成熟,被多家公司和多个开源项目使用;功能支持: 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及 日志采集 被大规模使用。缺点:Kafka 单机超过 64 个队列 / 分区, Load 会发生明显的飙高现象,队列越多, load 越高,发送消息响应时间变长,使用短轮询方式,实时性取决于轮询间隔时间,消费失败不支持重试;支持消息顺序,但是一台代理宕机后,就会产生消息乱序,社区更新较慢 ;
3.3 RocketMQ
3.4 RabbitMQ
4、MQ 的选择
5、RabbitMQ
5.1RabbitMQ 的概念
RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ 是一个快递站,一个快递员帮你传递快件。RabbitMQ 与快递站的主要区别在于,它不处理快件而是接收,存储和转发消息数据。
5.2RabbitMQ的四大核心组件
生产者:产生数据发送消息的程序是生产者。
5.3RabbitMQ 核心部分
![](https://img-blog.csdnimg.cn/efcc5ee1094549068ee30c39948d1866.png)
名词解释Broker:接收和分发消息的应用, RabbitMQ Server 就是 Message Broker。Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个 vhost ,每个用户在自己的 vhost 创建 exchange / queue 等。Connection : publisher / consumer 和 broker 之间的 TCP 连接。Channel:如果每一次访问 RabbitMQ 都建立一个 Connection ,在消息量大的时候建立 TCPConnection 的开销将是巨大的,效率也较低。 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 的分发依据。
6、Rabbit的使用
6.1 RabbitMQ 的简单队列使用
第一步,引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.8.0</version> </dependency>
第二步,编写队列代码
package com.hihonor.rabbit.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 本类用来编写rabbit 配置类
* */
@Configuration
public class RabbitConfig {
@Bean("queueA")
public Queue queueA(){
return new Queue("queueA");
}
}
第三步,编写消费者
package com.hihonor.rabbit;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RabbitListenerA {
@RabbitListener(queues = "queueA")
public void listenerQueueA(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
}
第四步,编写生产者
package com.hihonor.rabbit.controller;
import com.hihonor.rabbit.config.SpringApplication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("rabbit")
@Slf4j
public class RabbitController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/producer")
public void producer(){
log.info("生产者开始发送消息.........");
String msg = "我是刘晓辉,收到回复收到回复!";
// 开始发送消息
// 队列,消息
rabbitTemplate.convertAndSend("queueA",msg);
}
}
以上的代码是 一对一,一个队列对应一个消费者。
下面写一个多对多的代码
package com.hihonor.rabbit;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RabbitListenerA {
@RabbitListener(queues = {"queueA"})
@RabbitListener(queues = {"queueB"})
public void listenerQueueA(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
@RabbitListener(queues = {"queueA"})
public void listenerQueueACopy(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
}
我们可以看到,此时队列A被两个消费者方法监听,但是要注意,这个消息只能被两个消费者其中一个所消费,无法同时两个消费。
所以我们看到的现象是queueA队列的消息,只消费了一次。
如果需要多个队列同时消费同一个消息,就需要多个队列了。---广播机模式
6.2 交换机
6.2.1 FanoutExchange交换机----扇出交换机(没有key值)
fanoutExchange----扇出交换机,也叫做广播交换机。
生产者---->交换机------>队列----->消费者
伪代码
第一步---创建交换机和队列,以及二者的绑定关系。
package com.hihonor.rabbit.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 本类用来编写
* */
@Configuration
public class RabbitConfig {
@Bean("queueA")
public Queue queueA() {
return new Queue("queueA");
}
@Bean("queueB")
public Queue queueB() {
return new Queue("queueB");
}
@Bean("exchange_A")
public FanoutExchange fanoutExchange() {
return new FanoutExchange("exchange_A");
}
@Bean
public Binding bindingA(@Qualifier("queueA") Queue queueA,
@Qualifier("exchange_A") FanoutExchange fanoutExchangeA){
return BindingBuilder.bind(queueA).to(fanoutExchangeA);
}
@Bean
public Binding bindingB(@Qualifier("queueB") Queue queueB,
@Qualifier("exchange_A") FanoutExchange fanoutExchangeA){
return BindingBuilder.bind(queueB).to(fanoutExchangeA);
}
}
第二步,发送消息
package com.hihonor.rabbit.controller;
import com.hihonor.rabbit.config.SpringApplication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("rabbit")
@Slf4j
public class RabbitController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/producer")
public void producer(){
log.info("生产者开始发送消息.........");
String msgA = "我是刘晓辉A,收到回复收到回复!";
// 开始发送消息
// 队列,消息
rabbitTemplate.convertAndSend("exchange_A","",msgA);
}
}
第三步,接收消息
package com.hihonor.rabbit;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RabbitListenerA {
@RabbitListener(queues = {"queueA"})
@RabbitListener(queues = {"queueB"})
public void listenerQueueA(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
@RabbitListener(queues = {"queueA"})
public void listenerQueueACopy(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
}
根据结果分析,也可以看出来,当这个扇出交换机将消息发送的 A队列和B队列的时候,两个消费者都监听了A队列,存在一个消息争抢问题,一个消息只可以让一个消费者消费。
6.2.2 Direct exchange
![](https://img-blog.csdnimg.cn/6513fc0de41c49a4b9deb2a0f1a1c72d.png)
![](https://img-blog.csdnimg.cn/f967f31e02e1408a917035da3e82345e.png)
伪代码
第一步,交换机的创建和队列绑定
package com.hihonor.rabbit.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 本类用来编写
* */
@Configuration
public class RabbitConfig {
@Bean("queueA")
public Queue queueA() {
return new Queue("queueA");
}
@Bean("queueB")
public Queue queueB() {
return new Queue("queueB");
}
@Bean("exchange_A")
public FanoutExchange fanoutExchange() {
return new FanoutExchange("exchange_A");
}
@Bean
public Binding bindingA(@Qualifier("queueA") Queue queueA,
@Qualifier("exchange_A") FanoutExchange fanoutExchangeA) {
return BindingBuilder.bind(queueA).to(fanoutExchangeA);
}
@Bean
public Binding bindingB(@Qualifier("queueB") Queue queueB,
@Qualifier("exchange_A") FanoutExchange fanoutExchangeA) {
return BindingBuilder.bind(queueB).to(fanoutExchangeA);
}
/*
* 创建交换机B
*交换机B和队列A绑定,key值是KeyA 交换机B和队列B绑定,key值是KeyB
* */
@Bean("exchange_B")
public DirectExchange directExchangeA() {
return new DirectExchange("exchange_B");
}
@Bean
public Binding bindingAandEB(@Qualifier("queueA") Queue queueA,
@Qualifier("exchange_B") DirectExchange directExchange){
return BindingBuilder.bind(queueA).to(directExchange).with("KeyA");
}
@Bean
public Binding bindingBandEB(@Qualifier("queueB") Queue queueB,
@Qualifier("exchange_B") DirectExchange directExchange){
return BindingBuilder.bind(queueB).to(directExchange).with("KeyB");
}
}
第二步,发送消息
package com.hihonor.rabbit.controller;
import com.hihonor.rabbit.config.SpringApplication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("rabbit")
@Slf4j
public class RabbitController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/producer")
public void producer(){
log.info("生产者开始发送消息.........");
String msgA = "我是交换机A--queueA和queueB,收到回复收到回复!";
String msgBA = "我是交换机B--queueA,收到回复收到回复!";
String msgBB = "我是交换机B--queueB,收到回复收到回复!";
// 开始发送消息
// 队列,消息
rabbitTemplate.convertAndSend("exchange_A","",msgA);
rabbitTemplate.convertAndSend("exchange_B","KeyA",msgBA);
rabbitTemplate.convertAndSend("exchange_B","KeyB",msgBB);
}
}
第三步,接收消息
package com.hihonor.rabbit;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RabbitListenerA {
@RabbitListener(queues = {"queueA"})
public void listenerQueueA(Message message, Channel channel) {
log.info("消费者开始消费队列----queueA");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
@RabbitListener(queues = {"queueB"})
public void listenerQueueACopy(Message message, Channel channel) {
log.info("消费者开始消费队列----queueB");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
}
6.2.3 Topics交换机
![](https://img-blog.csdnimg.cn/95f725e02ee54bb9af623a55f9918b8d.png)
![](https://img-blog.csdnimg.cn/9034b4f72b8b44c6b9ec84aa9d9c94a1.png)
![](https://img-blog.csdnimg.cn/71ee306a4782437ab07700a7d775a331.png)
package com.hihonor.rabbit.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 本类用来编写
* */
@Configuration
public class RabbitConfig {
@Bean("queue1")
public Queue queue1() {
return new Queue("queue1");
}
@Bean("queue2")
public Queue queue2() {
return new Queue("queue2");
}
@Bean("queue3")
public Queue queue3() {
return new Queue("queue3");
}
@Bean("exchange_A")
public TopicExchange fanoutExchange() {
return new TopicExchange("exchange_A");
}
@Bean
public Binding bindingA(@Qualifier("queue1") Queue queue1,
@Qualifier("exchange_A") TopicExchange fanoutExchangeA) {
return BindingBuilder.bind(queue1).to(fanoutExchangeA).with("keyA.*");
}
@Bean
public Binding bindingB(@Qualifier("queue2") Queue queue2,
@Qualifier("exchange_A") TopicExchange fanoutExchangeA) {
return BindingBuilder.bind(queue2).to(fanoutExchangeA).with("keyA.two");
}
@Bean
public Binding bindingC(@Qualifier("queue3") Queue queue3,
@Qualifier("exchange_A") TopicExchange fanoutExchangeA) {
return BindingBuilder.bind(queue3).to(fanoutExchangeA).with("keyB.*");
}
}
第二步,消息发送
package com.hihonor.rabbit.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("rabbit")
@Slf4j
public class RabbitController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/producer")
public void producer(){
log.info("生产者开始发送消息.........");
String msgA = "我是交换机A--queue1和queue2,收到回复收到回复!";
// 开始发送消息
// 队列,消息
rabbitTemplate.convertAndSend("exchange_A","keyA.two",msgA);
}
}
第三步,消息接收
package com.hihonor.rabbit;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class RabbitListenerA {
@RabbitListener(queues = {"queue1"})
public void listenerQueue1(Message message, Channel channel) {
log.info("消费者开始消费队列----queue1");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
@RabbitListener(queues = {"queue2"})
public void listenerQueue2(Message message, Channel channel) {
log.info("消费者开始消费队列----queue2");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
@RabbitListener(queues = {"queue3"})
public void listenerQueue3(Message message, Channel channel) {
log.info("消费者开始消费队列----queue3");
String msg = new String(message.getBody());
System.out.println("获取得到消息------" + msg);
}
}