消息队列-RabbitMQ学习1

RabbitMQ

1.工作模型

在这里插入图片描述

在这里插入图片描述

上图说明:
producer:生产者
channel:信道(多路复用,虚连接Connection)
Broker:主机
VHost:虚拟主机
Exchange:交换机,主要用于消息的路由。常见类型:direct、fanout、headers、topic
Queue:队列
Consumer:消费者

2.交换器

2.1 Direct Exchange 直连

在这里插入图片描述

2.2 Fanout Exchange 广播

在这里插入图片描述

2.3 Topic Exchange 主题

在这里插入图片描述

3.SpringBoot整合RabbitMQ

3.1 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

<!-- springboot引入rabbitmq -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

<!-- RabbitMQ原生的客户端 -->
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.0.0</version>
</dependency>

3.2 properties配置文件

spring.application.name=springboot-rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/

3.3 配置类:

服务器配置、创建交换机、创建队列、创建绑定关系
package com.zwt.rabbitmq.config;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {

    @Value("${spring.rabbitmq.host}")
    private String address;

    @Value("${spring.rabbitmq.port}")
    private String port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;

    // 连接工厂
    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(address + ":" + port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        return connectionFactory;
    }

    // rabbitAdmin封装了对rabbitMQ的管理操作
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        return new RabbitAdmin(connectionFactory);
    }

    // 使用template
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        return new RabbitTemplate(connectionFactory);
    }

    // 声明交换器
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("directExchange");
    }
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutExchange");
    }
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange("topicExchange");
    }

    // 声明队列
    @Bean
    public Queue queue1(){
        return new Queue("queue1");
    }
    @Bean
    public Queue queue2(){
        return new Queue("queue2");
    }

    // 绑定关系
    @Bean
    public Binding bindQueue1AndDirectExchange(Queue queue1, DirectExchange directExchange){
        return BindingBuilder.bind(queue1).to(directExchange).with("red");
    }
    @Bean
    public Binding bindQueue2AndDirectExchange(Queue queue2, DirectExchange directExchange){
        return BindingBuilder.bind(queue2).to(directExchange).with("white");
    }
    @Bean
    public Binding bindQueue1AndFanoutExchange(Queue queue1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue1).to(fanoutExchange);
    }
    @Bean
    public Binding bindQueue2AndFanoutExchange(Queue queue2, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue2).to(fanoutExchange);
    }
    @Bean
    public Binding bindQueue1AndTopicExchange(Queue queue1, TopicExchange topicExchange){
        return BindingBuilder.bind(queue1).to(topicExchange).with("red.*");
    }
    @Bean
    public Binding bindQueue2AndTopicExchange(Queue queue2, TopicExchange topicExchange){
        return BindingBuilder.bind(queue2).to(topicExchange).with("white.82");
    }
}

在这里插入图片描述

3.4 生产者:

使用路由键发送消息(使用template)
package com.zwt.rabbitmq.producer;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@RequestMapping("/rabbitmq")
public class RabbitProducer {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/direct")
    public String direct(@RequestParam String key){
        String msg = "key(" + key + "),exchange(direct)-" + System.currentTimeMillis();
        System.out.println("DirectSender: " + msg);
        rabbitTemplate.convertAndSend("directExchange", key, msg);
        return "发送direct消息成功";
    }

    @GetMapping("/topic")
    public String topic(@RequestParam String key){
        String msg = "key(" + key + "),exchange(topic)-" + System.currentTimeMillis();
        System.out.println("TopicSender: " + msg);
        rabbitTemplate.convertAndSend("topicExchange", key, msg);
        return "发送topic消息成功";
    }

    @GetMapping("/fanout")
    public String fanout(@RequestParam String key){
        String msg = "key(" + key + "),exchange(fanout)-" + System.currentTimeMillis();
        System.out.println("FanoutSender: " + msg);
        rabbitTemplate.convertAndSend("fanoutExchange", key, msg);
        return "发送fanout消息成功";
    }
}

3.5 消费者

监听类(监听队列	)
package com.zwt.rabbitmq.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "queue1")
public class RabbitConsumer1 {

    @RabbitHandler
    public void process(String msg){
        System.out.println("Consumer1: " + msg);
    }
}

package com.zwt.rabbitmq.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "queue2")
public class RabbitConsumer2 {

    @RabbitHandler
    public void process(String msg){
        System.out.println("Consumer2: " + msg);
    }
}

4.消息可靠性分析

在这里插入图片描述

1.确保消息到MQ
2.确保消息路由到正确的队列,即路由保证
3.确保消息在队列正确的存储,即消息持久化
4.确保消息从队列中正确的投递到消费者,即消费者确认

4.1 确保消息到MQ

采用确认模式,来确保。这里需要1.修改配置类中的连接工厂ConnectionFactory 2.修改配置类中的rabbitTemplate
// 连接工厂
@Bean
public ConnectionFactory connectionFactory(){
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setAddresses(address + ":" + port);
    connectionFactory.setUsername(username);
    connectionFactory.setPassword(password);
    connectionFactory.setVirtualHost(virtualHost);

    // 修改1:确保消息到MQ-开启确认模式
    connectionFactory.setPublisherConfirms(true);

    return connectionFactory;
}

// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    // 修改2:确保消息到MQ-开启一个异步回调
    rabbitTemplate.setConfirmCallback(confirmCallback());
    return rabbitTemplate;
}

// 生产者发送确认
private RabbitTemplate.ConfirmCallback confirmCallback(){
    return new RabbitTemplate.ConfirmCallback() {
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            if(ack){
                System.out.println("确认消息发送到mq成功");
            }else {
                System.out.println("确认消息发送到mq失败,考虑重发:" + cause);
            }
        }
    };
}

4.2 路由保证

1.失败通知(mandatory+ReturnListener)
  这里需要1.修改配置类中的rabbitTemplate
2.备用交换机
// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    // 设置监听
    rabbitTemplate.setConfirmCallback(confirmCallback());
    // 修改1:另外一个监听,失败通知
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.setReturnCallback(returnCallback());

    return rabbitTemplate;
}

// 路由失败通知
private RabbitTemplate.ReturnCallback returnCallback(){
    return new RabbitTemplate.ReturnCallback() {
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            System.out.println("无法路由的消息,需要考虑另行处理");
            System.out.println("returned message: " + new String(message.getBody()));
            System.out.println("returned replyCode: " + replyCode);
            System.out.println("returned replyText: " + replyText);
            System.out.println("returned exchange: " + exchange);
            System.out.println("returned routingKey: " + routingKey);
        }
    };
}

4.3 消息持久化

在3.3环节中的配置类:
有关Queue的初始化、Exchange的初始化都是持久化的

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.4 消费者确认

在3.3环节中的配置类:
1.新增配置SimpleMessageListenerContainer
@Resource
private Receiver receiver;

@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory, Queue queue1, Queue queue2){
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
    // 绑定队列
    container.setQueues(queue1, queue2);
    // 手动提交
    container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
    // 消费确认方法
    container.setMessageListener(receiver);
    return container;
}
package com.zwt.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class Receiver implements ChannelAwareMessageListener {


    public void onMessage(Message message, Channel channel) {
        try {
            String msg = new String(message.getBody());
            System.out.println("Receiver------>接收到的信息: " + msg);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Receiver------>消息已经消费");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            try {
                // true的意思就是重发
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            } catch (IOException e1) {
                System.out.println("Receiver------>拒绝消息,要求MQ重新发送");
            }
        }

    }

    public void onMessage(Message message) {

    }
}

5.项目结构与代码

环节3、4整合后的项目结构与代码

在这里插入图片描述

5.1 完整的RabbitConfig代码

package com.zwt.rabbitmq.config;

import com.zwt.rabbitmq.consumer.Receiver;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;

@Configuration
public class RabbitConfig {

    @Value("${spring.rabbitmq.host}")
    private String address;

    @Value("${spring.rabbitmq.port}")
    private String port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;

    @Resource
    private Receiver receiver;

    // 连接工厂
    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(address + ":" + port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);

        // 开启确认模式
        connectionFactory.setPublisherConfirms(true);

        return connectionFactory;
    }

    // rabbitAdmin封装了对rabbitMQ的管理操作
    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
        return new RabbitAdmin(connectionFactory);
    }

    // 使用template
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 设置监听
        rabbitTemplate.setConfirmCallback(confirmCallback());
        // 另外一个监听,路由失败通知
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnCallback(returnCallback());

        return rabbitTemplate;
    }

    // 生产者发送确认
    private RabbitTemplate.ConfirmCallback confirmCallback(){
        return new RabbitTemplate.ConfirmCallback() {
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if(ack){
                    System.out.println("确认消息发送到mq成功");
                }else {
                    System.out.println("确认消息发送到mq失败,考虑重发:" + cause);
                }
            }
        };
    }

    // 路由失败通知
    private RabbitTemplate.ReturnCallback returnCallback(){
        return new RabbitTemplate.ReturnCallback() {
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("无法路由的消息,需要考虑另行处理");
                System.out.println("returned message: " + new String(message.getBody()));
                System.out.println("returned replyCode: " + replyCode);
                System.out.println("returned replyText: " + replyText);
                System.out.println("returned exchange: " + exchange);
                System.out.println("returned routingKey: " + routingKey);
            }
        };
    }

    // 声明交换器
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("directExchange");
    }
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutExchange");
    }
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange("topicExchange");
    }

    // 声明队列
    @Bean
    public Queue queue1(){
        return new Queue("queue1");
    }
    @Bean
    public Queue queue2(){
        return new Queue("queue2");
    }

    // 绑定关系
    @Bean
    public Binding bindQueue1AndDirectExchange(Queue queue1, DirectExchange directExchange){
        return BindingBuilder.bind(queue1).to(directExchange).with("red");
    }
    @Bean
    public Binding bindQueue2AndDirectExchange(Queue queue2, DirectExchange directExchange){
        return BindingBuilder.bind(queue2).to(directExchange).with("white");
    }
    @Bean
    public Binding bindQueue1AndFanoutExchange(Queue queue1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue1).to(fanoutExchange);
    }
    @Bean
    public Binding bindQueue2AndFanoutExchange(Queue queue2, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(queue2).to(fanoutExchange);
    }
    @Bean
    public Binding bindQueue1AndTopicExchange(Queue queue1, TopicExchange topicExchange){
        return BindingBuilder.bind(queue1).to(topicExchange).with("red.*");
    }
    @Bean
    public Binding bindQueue2AndTopicExchange(Queue queue2, TopicExchange topicExchange){
        return BindingBuilder.bind(queue2).to(topicExchange).with("white.82");
    }

    @Bean
    public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory, Queue queue1, Queue queue2){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        // 绑定队列
        container.setQueues(queue1, queue2);
        // 手动提交
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        // 消费确认方法
        container.setMessageListener(receiver);
        return container;
    }
}

5.2 完整的Receiver代码

package com.zwt.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
public class Receiver implements ChannelAwareMessageListener {

    public void onMessage(Message message, Channel channel) {
        try {
            String msg = new String(message.getBody());
            System.out.println("Receiver------>接收到的信息: " + msg);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            System.out.println("Receiver------>消息已经消费");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            try {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            } catch (IOException e1) {
                System.out.println("Receiver------>拒绝消息,要求MQ重新发送");
            }
        }
    }

    public void onMessage(Message message) {
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值