文章目录
课程资料
链接:https://pan.baidu.com/s/14ziQH62MeYmM8N6JsH5RcA
提取码:yyds
快速入门
安装RabbitMQ
docker load -i mq.tar #解压镜像
运行 RabbitMQ
docker run \
-e RABBITMQ_DEFAULT_USER=user \
-e RABBITMQ_DEFAULT_PASS=123456 \
--name mq \
--hostname mq1 \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:management
15672是管理台的端口!!!
MQ的基本结构:
RabbitMQ中的一些角色:
- publisher:生产者
- consumer:消费者
- exchange:交换机,负责消息路由
- queue:队列,存储消息
- virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离
依赖
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
yaml文件
spring:
rabbitmq:
host: 192.168.111.101 # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: user # 用户名
password: 123456 # 密码
@RabbitListener和@RabbitHandler的使用
@RabbitListener 标注在方法上, 直接 监听指定的队列,此时接收的参数需要 与发送时类型一致
@Component
public class PointConsumer {
//监听的队列名
@RabbitListener(queues = "point.to.point")
public void processOne(String name) {
System.out.println("point.to.point:" + name);
}
}
@RabbitListener 可以标注在类上面,需配合 @RabbitHandler 注解一起使用
@RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理,根据接受的参数类型进入具体的方法中
@Component
@RabbitListener(queues = "consumer_queue")
public class Receiver {
@RabbitHandler
public void processMessage1(String message) {
System.out.println(message);
}
@RabbitHandler
public void processMessage2(byte[] message) {
System.out.println(new String(message));
}
}
RabbitMQ消息模型
WorkQueue
让多个消费者
绑定
到一个队列,共同消费队列中的消息
当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理!!
消息发送
@Test
public void testWorkQueue() throws InterruptedException {
// 队列名称
String queueName = "simple.queue";
// 消息
String message = "hello, message_";
for (int i = 0; i < 50; i++) {
// 发送消息
rabbitTemplate.convertAndSend(queueName, message + i);
Thread.sleep(20);
}
}
消息接收
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(200);
}
默认是平均分配给每个消费者
加快消息处理,simple代表
简单队列模型
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息
发布/订阅
发布订阅的模型如图:
可以看到,在订阅模型中,多了一个exchange角色,而且过程略有变化:
- Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
- Consumer:消费者,与以前一样,订阅队列,没有变化
- Queue:消息队列也与以前一样,接收消息、缓存消息。
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
Fanout
MQ中叫广播更合适
声明
队列和交换机
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutConfig {
/**
* 声明交换机
* @return Fanout类型交换机
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("myfanout");
}
/**
* 第1个队列
*/
@Bean
public Queue queue1() {
return new Queue("fanout.queue1");
}
/**
* 第2个队列
*/
@Bean
public Queue queue2() {
return new Queue("fanout.queue2");
}
/**
* 绑定队列和交换机
*/
@Bean
public Binding bindQueue1(Queue queue1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue1).to(fanoutExchange);
}
@Bean
public Binding bindQueue2(Queue queue2, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue2).to(fanoutExchange);
}
}
消息发送
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testFanoutExchange() {
// 队列名称
String exchangeName = "myfanout";
// 消息
String message = "hello, everyone!";
rabbitTemplate.convertAndSend(exchangeName, "", message);
}
消息接收
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "fanout.queue1")
public class FanoutReceiver {
@RabbitHandler
public void process(String message) {
System.out.println (message);
}
}
Direct
声明
队列和交换机
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DirectConfig {
//Direct交换机
@Bean
DirectExchange directExchange() {
return new DirectExchange ("mydirect");
}
//队列
@Bean
public Queue directQueue() {
return new Queue ("direct.queue1", true);
}
//绑定
@Bean
Binding bindingDirect(Queue directQueue, DirectExchange directExchange) {
return BindingBuilder.bind (directQueue).to (directExchange)
.with ("red");
}
//绑定
@Bean
Binding bindingDirect2(Queue directQueue, DirectExchange directExchange) {
return BindingBuilder.bind (directQueue).to (directExchange)
.with ("yellow");
}
}
消息发送的流程:
1、指定exchangeName交换机名称
2、指定routingKey 路由键
3、指定消息体
4、发送
5、发送到交换机后,根据routingKey 路由键找到队列
direct.queue1队列绑定了两个 routingKey(red、yellow),故yellow、red作为路由键都能发送到该队列中
Direct交换机根据RoutingKey判断路由给哪个队列
消息发送
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "mydirect";
// 消息
String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "red", message);
}
消息接收方
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "direct.queue1")
public class DirectReceiver {
@RabbitHandler
public void process(String message) {
System.out.println (message);
}
}
Topic
声明
队列和交换机
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TopicConfig {
/**
* 交换机
*
* @return
*/
@Bean
TopicExchange exchange() {
return new TopicExchange ("mytopic");
}
/**
* 队列
*
* @return
*/
@Bean
public Queue queue() {
return new Queue ("topic.queue1");
}
// 绑定topic.second队列到topic.#,凡是topic.开头的routingKey消息都发送到此队列
@Bean
Binding bindingExchangeMessage2(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind (queue).to (exchange).with ("topic.#");
}
}
Topic交换机根据RoutingKey判断路由给哪个队列
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendTopicExchange() {
// 交换机名称
String exchangeName = "mytopic";
// 消息
String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
rabbitTemplate.convertAndSend (exchangeName, "topic.hi", message);
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "topic.queue1")
public class TopicReceiver {
@RabbitHandler
public void process(String message) {
System.out.println (message);
}
}
配置JSON转换器
在publisher和consumer两个服务中都
引入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
在
启动类
中添加一个Bean
即可:
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}