springBoot+rabbitMq实现五种队列模式

首先搭建一个boot项目,pom引入rabbitMq

<!--rabbitMq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

编写application-dev.yml文件
1

rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    publisher-returns: true
    connection-timeout: 20000
    listener:
      simple:
        acknowledge-mode: manual
        concurrency: 1
        max-concurrency: 1
        retry:
          enabled: true

1 简单队列(1对1)

创建SimpleQueueConfig 简单队列配置类

package com.example.rabbitConfig;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SimpleQueueConfig {

    /**
     * 定义简单队列名.
     */
    private String simpleQueue = "queue_simple";

    @Bean
    public Queue simpleQueue() {
        return new Queue(simpleQueue);
    }
}

编写生产者

package com.example.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class SimpleProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        for (int i = 0; i < 5; i++) {
            String message = "简单消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend( "queue_simple", message);
        }
    }
}

编写消费者

package com.example.service;

        import com.rabbitmq.client.Channel;
        import org.springframework.amqp.core.Message;
        import org.springframework.amqp.rabbit.annotation.RabbitListener;
        import org.springframework.stereotype.Component;

        import java.io.IOException;

@Component
public class SimpleConsumers {

    @RabbitListener(queues = "queue_simple")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息:"+new String(message.getBody()));
    }
}

编写访问类

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {

    @Autowired
    private SimpleProducer simpleProducer;

    @RequestMapping(value = "simpleQueueTest")
    @ResponseBody
    public String simpleQueueTest() {
        simpleProducer.sendMessage();
        return "success";
    }
}

测试启动项目访问 simpleQueueTest:
结果:
在这里插入图片描述

2 工作队列(1生产者对多个消费者)

编写工作配置

package com.example.rabbitConfig;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WorkQueueConfig {

    /**
     * 队列名.
     */
    private final String work = "work_queue";

    @Bean
    public Queue workQueue() {
        return new Queue(work);
    }
}

编写生产者

package com.example.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class WorkProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        for (int i = 0; i < 10; i++) {
            String message = "工作消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("work_queue", message);
        }
    }
}

编写消费者1

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class WorkConsumers1 {

    @RabbitListener(queues = "work_queue")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息111:" + new String(message.getBody()));
    }
}

编写消费者2

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class WorkConsumers2 {

    @RabbitListener(queues = "work_queue")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息222222:" + new String(message.getBody()));
    }
}

编写测试方法

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {

    @Autowired
    private WorkProducer workProducer;

    @RequestMapping(value = "workQueueTest")
    @ResponseBody
    public String workQueueTest() {
        workProducer.sendMessage();
        return "success";
    }
}

启动项目,访问
测试结果:
在这里插入图片描述
控制台打印,发现10条消息 偶数条消费者1获取,奇数条消费者2获取,并且平均分配。

当然通过代码实现按需分配,即谁的性能强,谁优先原则,实现负载均衡。
配置可控分配数
在这里插入图片描述

3 Fanout Exchange(订阅模式–多个消费者监听不同的队列,但队列都绑定同一个交换机)

编写订阅配置类

package com.example.rabbitConfig;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FanoutQueueConfig {

    /**
     * 声明队列名.
     */
    private final String fanout1 = "fanout_queue_1";

    private final String fanout2 = "fanout_queue_2";

    /**
     * 声明交换机的名字.
     */
    private final String fanoutExchange = "fanoutExchange";

    /**
     * 声明队列.
     *
     * @return
     */
    @Bean
    public Queue fanoutQueue1() {
        return new Queue(fanout1);
    }

    @Bean
    public Queue fanoutQueue2() {
        return new Queue(fanout2);
    }

    /**
     * 声明交换机.
     */
    @Bean
    public FanoutExchange exchange() {
        return new FanoutExchange(fanoutExchange);
    }

    /**
     * 队列绑定交换机,也可在可视化工具中进行绑定.
     *
     * @return
     */
    @Bean
    public Binding bindingFanoutQueue1(Queue fanoutQueue1, FanoutExchange exchange) {
        return BindingBuilder.bind(fanoutQueue1).to(exchange);
    }

    @Bean
    public Binding bindingFanoutQueue2(Queue fanoutQueue2, FanoutExchange exchange) {
        return BindingBuilder.bind(fanoutQueue2).to(exchange);
    }
}

编写订阅生产者

package com.example.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class FanoutProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        for (int i = 0; i < 5; i++) {
            String message = "订阅模式消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("fanoutExchange", "", message);
        }
    }
}

编写订阅消费者1

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class FanoutConsumers1 {

    @RabbitListener(queues = "fanout_queue_1")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息111:" + new String(message.getBody()));
    }
}

编写订阅消费者2

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class FanoutConsumers2 {

    @RabbitListener(queues = "fanout_queue_2")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息222222:" + new String(message.getBody()));
    }
}

编写测试方法

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {

    @Autowired
    private FanoutProducer fanoutProducer;

    @RequestMapping(value = "fanoutQueueTest")
    @ResponseBody
    public String fanoutQueueTest() {
        fanoutProducer.sendMessage();
        return "success";
    }
}

启动项目访问,测试结果:
在这里插入图片描述
控制台打印 ,发现两个绑定了不同队列的消费者都接受到了同一条消息
查看RabbitMq 服务器:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 Direct Exchange(路由模式,订阅模式+指定routingKey)

编写路由配置类

package com.example.rabbitConfig;

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 DirectQueueConfig {

    /**
     * 声明队列名.
     */
    private final String direct1 = "direct_queue_1";

    private final String direct2 = "direct_queue_2";

    /**
     * 声明交换机的名字.
     */
    private final String directExchange = "directExchange";

    /**
     * 声明队列.
     *
     * @return
     */
    @Bean
    public Queue directQueue1() {
        return new Queue(direct1);
    }

    @Bean
    public Queue directQueue2() {
        return new Queue(direct2);
    }

    /**
     * 声明路由交换机.
     *
     * @return
     */
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(directExchange);
    }

    /**
     * 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
     *
     * @return
     */
    @Bean
    Binding bindingDirectExchange1(Queue directQueue1, DirectExchange exchange) {
        return BindingBuilder.bind(directQueue1).to(exchange).with("update");
    }

    /**
     * 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
     *
     * @return
     */
    @Bean
    Binding bindingDirectExchange2(Queue directQueue2, DirectExchange exchange) {
        return BindingBuilder.bind(directQueue2).to(exchange).with("add");
    }

}

编写生产者

package com.example.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DirectProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessageA() {
        for (int i = 0; i < 5; i++) {
            String message = "路由模式--routingKey=update消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("directExchange", "update", message);
        }
    }

    public void sendMessageB() {
        for (int i = 0; i < 5; i++) {
            String message = "路由模式--routingKey=add消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("directExchange", "add", message);
        }
    }
}

编写消费者1

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class DirectConsumers1 {

    @RabbitListener(queues = "direct_queue_1")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息111:" + new String(message.getBody()));
    }
}

消费者2

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class DirectConsumers2 {

    @RabbitListener(queues = "direct_queue_2")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息222222:" + new String(message.getBody()));
    }
}

编写访问类

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {

    @Autowired
    private DirectProducer directProducer;

    @RequestMapping(value = "directQueueTest1")
    @ResponseBody
    public String directQueueTest1() {
        directProducer.sendMessageA();
        return "success";
    }

    @RequestMapping(value = "directQueueTest2")
    @ResponseBody
    public String directQueueTest2() {
        directProducer.sendMessageB();
        return "success";
    }
}

启动项目,访问路径。
测试结果:
directQueueTest1
在这里插入图片描述
directQueueTest2
在这里插入图片描述

5 Topic Exchange(路由模式)

编写路由配置类

package com.example.rabbitConfig;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TopicQueueConfig {

    /**
     * 声明队列名.
     */
    private final String topic1 = "topic_queue_1";

    private final String topic2 = "topic_queue_2";

    /**
     * 声明交换机的名字.
     */
    private final String topicExchange = "topicExchange";

    /**
     * 声明队列.
     *
     * @return
     */
    @Bean
    public Queue topicQueue1() {
        return new Queue(topic1);
    }

    @Bean
    public Queue topicQueue2() {
        return new Queue(topic2);
    }

    /**
     * 声明路由交换机.
     *
     * @return
     */
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(topicExchange);
    }

    /**
     * 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
     *
     * @return
     */
    @Bean
    Binding bindingTopicExchange1(Queue topicQueue1, TopicExchange exchange) {
        return BindingBuilder.bind(topicQueue1).to(exchange).with("topic.keyA");
    }

    /**
     * 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
     * 绑定的routing key 也可以使用通配符:
     * *:匹配不多不少一个词
     * #:匹配一个或多个词
     *
     * @return
     */
    @Bean
    Binding bindingTopicExchange2(Queue topicQueue2, TopicExchange exchange) {
        return BindingBuilder.bind(topicQueue2).to(exchange).with("topic.#");
    }
}

编写生产者

package com.example.controller;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TopicProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessageA() {
        for (int i = 0; i < 5; i++) {
            String message = "通配符模式--routingKey=topic.keyA消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("topicExchange", "topic.keyA", message);
        }
    }

    public void sendMessageB() {
        for (int i = 0; i < 5; i++) {
            String message = "通配符模式--routingKey=topic.#消息" + i;
            System.out.println("我是生产信息的:" + message);
            rabbitTemplate.convertAndSend("topicExchange", "topic.keyD.keyE", message);
        }
    }
}

编写消费者1

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class TopicConsumers1 {

    @RabbitListener(queues = "topic_queue_1")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息111:" + new String(message.getBody()));
    }
}

编写消费者2

package com.example.service;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class TopicConsumers2 {

    @RabbitListener(queues = "topic_queue_2")//队列的名称
    public void readMessage(Message message, Channel channel) throws IOException {
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        System.out.println("我是消费信息222222:" + new String(message.getBody()));
    }
}

编写访问类

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {

    @Autowired
    private TopicProducer topicProducer;

    @RequestMapping(value = "topicQueueTest1")
    @ResponseBody
    public String topicQueueTest1() {
        topicProducer.sendMessageA();
        return "success";
    }

    @RequestMapping(value = "topicQueueTest2")
    @ResponseBody
    public String topicQueueTest2() {
        topicProducer.sendMessageB();
        return "success";
    }
}

启动项目,访问路径。
结果
topicQueueTest1方法,两个消费者都能消费
在这里插入图片描述
topicQueueTest2方法只有消费者2 可以消费
在这里插入图片描述
在这里插入图片描述
至此,五种队列的实现已结束,如果你觉得本篇文章对你有所帮助的话,点个赞哈哈。

实现生产者消息确认

在这里插入图片描述
编写消息发送确认类 RabbitConfirmCallback

package com.example.rabbitConfig;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("=======ConfirmCallback=========");
        System.out.println("correlationData = " + correlationData);
        System.out.println("ack = " + ack);
        System.out.println("cause = " + cause);
        System.out.println("=======ConfirmCallback=========");
    }
}

编写消息发送交换机返回机制RabbitConfirmReturnCallBack

package com.example.rabbitConfig;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;

public class RabbitConfirmReturnCallBack implements RabbitTemplate.ReturnCallback {
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("--------------ReturnCallback----------------");
        System.out.println("message = " + message);
        System.out.println("replyCode = " + replyCode);
        System.out.println("replyText = " + replyText);
        System.out.println("exchange = " + exchange);
        System.out.println("routingKey = " + routingKey);
        System.out.println("--------------ReturnCallback----------------");
    }
}

在我们的rabbit队列配置类里设置RabbitTemplate
举例

@Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void initRabbitTemplate() {
        // 设置生产者消息确认
        rabbitTemplate.setConfirmCallback(new RabbitConfirmCallback());
        rabbitTemplate.setReturnCallback(new RabbitConfirmReturnCallBack());
    }

在这里插入图片描述
启动项目发送消息,消息被正常消费,confim回调返回ack=true
如果我们将exchange修改,发送到一个不存在的exchange中,会怎么样呢?

会发现confirm回调为false,打印出结果为不存在topicExchange1111的交换机
在这里插入图片描述
如果我们在消费端处理逻辑时出错会怎么样呢?修改消费端代码我们在消费时让它报错
在这里插入图片描述
confirm回调为true,但是在rabbitmq的web界面会发现存在一条没有消费的消息
在这里插入图片描述
如果我们把channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);中最后一个参数改为false呢,会发现在web管理界面没有未被消费的消息,说明这条消息已经被摒弃。

实际开发中,到底是打回到队列呢还是摒弃,要看自己的需求,但是打回队列应该有次数限制,不然会陷入死循环。

继续测试,将routingKey修改为一个没有的key,
在这里插入图片描述
结论:

如果消息没有到exchange,则confirm回调,ack=false

如果消息到达exchange,则confirm回调,ack=true

exchange到queue成功,则不回调return

exchange到queue失败,则回调return

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值