RabbitMQ部署

我们在Centos7虚拟机中使用Docker来安装。

在线拉取

docker pull rabbitmq:3-management 

解压

docker load -i mq.tar 

安装MQ

执行下面的命令来运行MQ容器:

docker run \
 -e RABBITMQ_DEFAULT_USER=itcast \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

输入上面的设置的账号和密码 

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring

MQ的官方文档中给出了5个MQ的Demo示例,对应了几种不同的用法 

   基本消息队列(BasicQueue)                              工作消息队列(WorkQueue)

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring_02

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_System_03

发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种 

Fanout Exchange:广播                                       Direct Exchange:路由

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring_04

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_System_05

   Topic Exchange:主题

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_后端_06

 


 HelloWorld案例

官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:

publisher:消息发布者,将消息发送到队列queue

queue:消息队列,负责接受并缓存消息

consumer:订阅队列,处理队列中的消息

 

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_发送消息_07

利用SpringAMQP实现HelloWorld中的基础消息队列功能

1、在父工程中引入spring-amqp的依赖

<!--AMQP依赖,包含RabbitMQ--><dependency>    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>



在publisher服务中利用RabbitTemplate发送消息到simple.queue这个队列

在publisher服务中编写application.yml,添加mq连接信息:

spring:
   rabbitmq:
     host: 192.168.61.130 # 主机名
     port: 5672 # 端口
     virtual-host: / # 虚拟主机
     username: itcast # 用户名
     password: 123321 # 密码

在publisher服务中新建一个测试类,编写测试方法

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
     @Autowired
     private RabbitTemplate rabbitTemplate;
     @Test
     public void testSimpleQueue() {
         String queueName = "simple.queue";
         String message = "hello, spring amqp!";
         rabbitTemplate.convertAndSend(queueName, message);
     }}

  成功获取

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_System_08

在consumer服务中编写消费逻辑,绑定simple.queue这个队列

在consumer服务中编写application.yml,添加mq连接信息:

spring:
   rabbitmq:
     host: 192.168.61.130 # 主机名
     port: 5672 # 端口
     virtual-host: / # 虚拟主机
     username: itcast # 用户名
     password: 123321 # 密码

在consumer服务中新建一个类,编写消费逻辑:

@Component
public class SpringRabbitListener {

     @RabbitListener(queues = "simple.queue")
     public void listenSimpleQueueMessage(String msg) throws InterruptedException {
         System.out.println("spring 消费者接收到消息 :【" + msg + "】");
     }}

 启动后可以看到已经接收到之前发送到RabbitMQ的数据已接收

 

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring_09

 

 


Work Queue 工作队列

Work queue,工作队列,可以提高消息处理速度,避免队列消息堆积

publisher:消息发布者,将消息发送到队列queue

queue:消息队列,负责接受并缓存消息

consumer:订阅队列,处理队列中的消息,有两个消费者

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_分布式_10

 

案例 模拟WorkQueue,实现一个队列绑定多个消费者

基本思路如下:

1.在publisher服务中定义测试方法,每秒产生50条消息,发送到simple.queue

2.在consumer服务中定义两个消息监听者,都监听simple.queue队列

3.消费者1每秒处理50条消息,消费者2每秒处理10条消息

 步骤一:引入依赖和添加application.yml配置,和上述一致

步骤二:生产者循环发送消息到simple.queue

在publisher服务中添加一个测试方法,循环发送50条消息到simple.queue队列

@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);
     }
 }

步骤三:编写两个消费者,都监听simple.queue

@RabbitListener(queues = "simple.queue")
public void listenSimpleQueueMessage(String msg) throws InterruptedException {
     System.out.println("spring 消费者1接收到消息:【" + msg + "】");Thread.sleep(25);
 }

@RabbitListener(queues = "simple.queue")
public void listenSimpleQueueMessage2(String msg) throws InterruptedException {
     System.err.println("spring 消费者2接收到消息:【" + msg + "】");
     Thread.sleep(100);
 }

消费预取限制

修改application.yml文件,设置preFetch这个值,可以控制预取消息的上限:

通过消费预期上限可以让消费者能者多劳,提高处理消息队列的速度

spring:
   rabbitmq:
     host: 192.168.61.130 # 主机名
     port: 5672 # 端口
     virtual-host: / # 虚拟主机
     username: itcast # 用户名
     password: 123321 # 密码
     listener:
       simple:
         prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

 

发布( Publish )、订阅( Subscribe )

发布订阅模式与之前案例的区别就是允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)。

常见exchange类型包括:

Fanout :广播

Direct :路由

Topic :话题

 


Fanout Exchange:广播                       

Fanout Exchange 会将接收到的消息广播到每一个跟其绑定的queue

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring_11


步骤一:引入依赖和添加application.yml配置,和上述一致 

步骤二:在consumer服务声明Exchange、Queue、Binding

@Configuration
public class FanoutConfig {
     // 声明FanoutExchange交换机
     @Bean
     public FanoutExchange fanoutExchange(){
         return new FanoutExchange("itcast.fanout");
     }
     // 声明第1个队列
     @Bean
     public Queue fanoutQueue1(){
         return new Queue("fanout.queue1");
     }
     //绑 定队列1和交换机
     @Bean
     public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
         return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
     }
     //  ... 略, 以相同方式声明第2个队列,并完成绑定
}

 步骤三:在consumer服务声明两个消费者

@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {
     System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
 }

@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {
     System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
 }

 步骤四:在publisher服务发送消息到FanoutExchange

@Test
public void testFanoutExchange() {
     // 队列名称
     String exchangeName = "itcast.fanout";
     // 消息
     String message = "hello, everyone!";
     // 发送消息,参数分别是:交互机名称、RoutingKey(暂时为空)、消息
     rabbitTemplate.convertAndSend(exchangeName, "", message);
 }

 Direct Exchange:路由

Direct Exchange 会将接收到的消息根据规则路由到指定的Queue,因此称为路由模式(routes)。

每一个Queue都与Exchange设置一个BindingKey

发布者发送消息时,指定消息的RoutingKey

Exchange将消息路由到BindingKey与消息RoutingKey一致的队列

 

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_后端_12

步骤一:引入依赖和添加application.yml配置,和上述一致 

步骤二:在consumer服务声明Exchange、Queue  

在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2,

并利用@RabbitListener声明Exchange、Queue、RoutingKey

@RabbitListener(bindings = @QueueBinding(
         value = @Queue(name = "direct.queue1"),
         exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
         key = {"red", "blue"}
 ))
public void listenDirectQueue1(String msg){
     System.out.println("消费者1接收到Direct消息:【"+msg+"】");
 }

@RabbitListener(bindings = @QueueBinding(
         value = @Queue(name = "direct.queue2"),
         exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
         key = {"red", "yellow"}
 ))
public void listenDirectQueue2(String msg){
     System.out.println("消费者2接收到Direct消息:【"+msg+"】 ");
 }

 步骤三:在publisher服务发送消息到DirectExchange

@Test
public void testDirectExchange() {
     // 队列名称
     String exchangeName = "itcast.direct";
     // 消息
     String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
     // 发送消息,参数依次为:交换机名称,RoutingKey,消息
     rabbitTemplate.convertAndSend(exchangeName, "red", message);
 } 描述下Direct交换机与Fanout交换机的差异?
1、Fanout交换机将消息路由给每一个与之绑定的队列
2、Direct交换机根据RoutingKey判断路由给哪个队列
3、如果多个队列具有相同的RoutingKey,则与Fanout功能类似
基于@RabbitListener注解声明队列和交换机有哪些常见注解?
1、@Queue
2、@Exchange

 

发布订阅-TopicExchange  

TopicExchange与DirectExchange类似,区别在于routingKey必须是多个单词的列表,并且以 . 分割。
Queue与Exchange指定BindingKey时可以使用通配符:
#:代指0个或多个单词
*:代指一个单词

rabbitmq springboot 多队列 多消费者 rabbitmq direct多个队列_spring_13

 

步骤一:引入依赖和添加application.yml配置,和上述一致 

步骤二:在consumer服务声明Exchange、Queue  

1、在consumer服务中,编写两个消费者方法,分别监听topic.queue1和topic.queue2,

2、并利用@RabbitListener声明Exchange、Queue、RoutingKey



@RabbitListener(bindings = @QueueBinding(
         value = @Queue(name = "topic.queue1"),
         exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
         key = "china.#"
))
public void listenTopicQueue1(String msg){
     System.out.println("消费者1接收到Topic消息:【"+msg+"】");
 }
  
@RabbitListener(bindings = @QueueBinding(
         value = @Queue(name = "topic.queue2"),
         exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
         key = "#.news"
))
public void listenTopicQueue2(String msg){
     System.out.println("消费者2接收到Topic消息:【"+msg+"】");
 }

步骤三:在publisher服务发送消息到TopicExchange 

@Test
public void testTopicExchange() {
     // 队列名称
     String exchangeName = "itcast.topic";
     // 消息
     String message = "喜报!孙悟空大战哥斯拉,胜!";
     // 发送消息
     rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
 }描述下Direct交换机与Topic交换机的差异?
Topic交换机接收的消息RoutingKey必须是多个单词,以 . 分割
Topic交换机与队列绑定时的bindingKey可以指定通配符
#:代表0个或多个词
*:代表1个词