SpringBoot整合RabbitMQ

SpringBoot整合RabbitMQ

引入依赖

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

开启注解

@EnableRabbit

配置文件rabbitMQ

spring.rabbitmq.host=192.168.56.10
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/

配置config

@Configuration
public class MyRabbitConfig {
    // 配置使用json的方式序列化对象
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

创建交换机

// 注入amqpAdmin
@Autowired
AmqpAdmin amqpAdmin;
// 创建交换机
// 交换机名称 是否持久化 是否自动删除 相关参数
// String name, boolean durable, boolean autoDelete, Map<String, Object> arguments
DirectExchange directExchange = new DirectExchange("hello-java-exchange", true, false, null);
amqpAdmin.declareExchange(directExchange);

创建队列

// 注入amqpAdmin
@Autowired
AmqpAdmin amqpAdmin;
// 创建队列
// 队列名称 是否持久化 是否只能连接一个交换机 是否自动删除 相关参数
// String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
Queue queue = new Queue("hello-java-queue", true, false, false, null);
amqpAdmin.declareQueue(queue);

绑定交换机和队列

// 注入amqpAdmin
@Autowired
AmqpAdmin amqpAdmin;       
// 绑定交换机和队列
// 将exchange指定的交换机和destination目的地进行绑定(绑定队列也可以绑定交换机)使用routingKey作为指定的路由键
// String destination, Binding.DestinationType destinationType, String exchange, String routingKey, Map<String, Object> arguments
Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE, "hello-java-exchange", "hello.java", null);
amqpAdmin.declareBinding(binding);

发送消息

        // 发送消息
        // 交换机 路由键 消息
        // String exchange, String routingKey, Object object
        rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", "hello world");

接收/监听消息

  1. 使用@RabbitListener,要想使用必须开启@EnableRabbit

    // queues:要监听的队列数组
    // 监听的内容会自动封装到方法的参数上
    @RabbitListener(queues = {"hello-java-queue"})
    public void receiveMessage(Object message) {
        System.out.println("message: " + message.toString());
        System.out.println("类型:" + message.getClass());
        System.out.println("接收到消息...内容...");
    }

// 接收监听到的消息
message: (Body:'{"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1618122170115}' MessageProperties [headers={__TypeId__=com.atguigu.gulimall.order.entity.OrderReturnReasonEntity}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=hello-java-exchange, receivedRoutingKey=hello.java, deliveryTag=1, consumerTag=amq.ctag-0TUO4qdSSCcnsHUQ9XZyzg, consumerQueue=hello-java-queue])
类型:class org.springframework.amqp.core.Message
接收到消息...内容...

由监听的消息类型可以看出为class org.springframework.amqp.core.Message类型,因此可以改造参数类型

  1. Message message:原生消息详细信息,头+体
  2. T<发送的消息的类型>:OrderReturnReasonEntity content
  3. Channel channel:当前传输数据的通道

    @RabbitListener(queues = {"hello-java-queue"})
    public void receiveMessage(Message message, ContentEntity content, Channel channel) {
        // 获取消息体,即json的数据 {"id":1,"name":"哈哈","sort":null,"status":null,"createTime":1618122170115}
        byte[] body = message.getBody();
        // 获取消息头信息 即
        // [headers={__TypeId__=com.atguigu.gulimall.order.entity.ContentEntity}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=hello-java-exchange, receivedRoutingKey=hello.java, deliveryTag=1, consumerTag=amq.ctag-0TUO4qdSSCcnsHUQ9XZyzg, consumerQueue=hello-java-queue])
        MessageProperties messageProperties = message.getMessageProperties();
        System.out.println("message: " + message.toString());
        System.out.println("类型:" + message.getClass());
        System.out.println("转化后消息...内容...");
        System.out.println("content: " + content.toString());
    }

Queue队列:可以很多人都来监听。只要收到消息,队列删除消息,并且只能同时用且只有一个人接收到此消息

  1. 同一个消息,只能被一个客户端收到
  2. 只有当前消息队列处理完成,才可以接收下一个消息队列

使用场景

  1. @RabbitHandler: 标记方法

  2. @RabbitListener:类+方法

    使用@RabbitListener标记在类上(重载区分不同的消息),说明这个类就是用来接受消息队列的方法类,在该类下的所以方法上标记@RabbitHandler,每个方法指定不同的接收参数,这样就可以接收不同类型的消息

@RabbitListener
class receive {
    @RabbitHandler(queues = {"hello-java-queue"})
    public void receiveMessage1(Entity1 content) {
        System.out.println("content1: " + content.toString());
    }

    @RabbitHandler(queues = {"hello-java-queue"})
    public void receiveMessage2(Entity2 content) {
        System.out.println("content2: " + content.toString());
    }
}

RabbitMQ消息确认机制-可靠抵达

  • 保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍,为此引入确认机制
  • publisher confirmCallback 确认模式(触发时机:服务端将消息发送给RabbitMQ所在的服务器)
  • publisher returnCallback 未投递到queue退出模式(触发时机:RabbitmQ所在的服务器调用交换机投递给对应队列)
  • consumer ack机制(触发机制:消费端成功获取到消息队列的消息)

可靠抵达-服务端确认(confirmCallback 、returnCallback )

  1. 开启发送端确认

# 开启发送端确认
spring.rabbitmq.publisher-confirms=true
  1. 开启发送端消息抵达队列的确认

# 开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
# 只要发送端消息抵达队列,以异步方式优先回调这个returnConfirm(绑定一起使用)
spring.rabbitmq.template.mandatory=true
  1. 定制RabbitTemplate自定义confirmCallback 、returnCallback 触发方法

    /**
     * 定制RabbitTemplate
     * 1. MQ服务器收到消息就回调
     *      1. spring.rabbitmq.publisher-confirms=true
     *      2. 设置回调确认confirmCallback 
     * 2. 消息正确抵达队列进行回调
     *      1. spring.rabbitmq.publisher-returns=true
     *      2. spring.rabbitmq.template.mandatory=true
     *      3. 设置回调确认returnCallback 
     */
    // PostConstruct: 当MyRabbitConfig对象创建完再执行该方法
    @PostConstruct
    public void initRabbitTemplate() {
        // 设置MQ服务器收到消息回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 只要消息抵达MQ服务器ack就为true
             * @param correlationData:当前消息的唯一关联数据(这个是消息的唯一id)即发送时传的CorrelationData参数
             * @param b:ack,消息是否成功还是失败
             * @param s:失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("correlationData: " + correlationData);
                System.out.println("ack: " + b);
                System.out.println("s: " + s);
            }
        });
        // 设置消息抵达队列回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 只要消息没有投递给指定的队列,就触发这个失败回调
             * @param message:投递失败的消息详细信息
             * @param i:回复的状态码
             * @param s:回复的文本内容
             * @param s1:当时这个消息发送给哪个交换机
             * @param s2:当时这个消息发送给哪个路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("fail message: " + message);
                System.out.println("i: " + i);
                System.out.println("s: " + s);
                System.out.println("s1: " + s1);
                System.out.println("s2: " + s2);
            }
        });
    }

可靠抵达-消费端确认(ack)

保证每个消息被正确消费,此时才可以MQ删除这个消息

  1. basic.ack 用于肯定确认;MQ服务器会移除此消息
  2. basic.nack用于否定确认;可以指定MQ服务器是否丢弃此消息,可以批量
  3. basic.reject用于否定确认;跟nack使用一样,但是不能批量
  4. 默认是自动ack,只要消息接收到,客户端会自动确认,服务端就会移除这个消息,如果客户端在处理消息时候宕机则会丢失消息,因此要手动确认,保证消息不丢失。当客户端宕机后,消息会从unacked状态变成ready状态,当下一次新的客户端连接进来再将消息重新发送给客户端

# 设置客户端手动确认接受到消息
spring.rabbitmq.listener.simple.acknowledge-mode=manual

  @RabbitListener(queues = {"hello-java-queue"})
    public void receiveMessage1(Message message, Content content, Channel channel) {
        System.out.println("content1: " + content.toString());
        // 通道内按顺序自增
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            // 确认消息接收成功,非批量签收模式
            // long deliveryTag, boolean multipe (当前消息的标签,是否批量签收)
            channel.basicAck(deliveryTag, false);
            // 消息接收成功,但是拒绝签收消息
            // long deliveryTag, boolean multipe, boolean requeue (当前消息的标签,是否批量签收,是否重新入队(false丢掉消息,true将消息重新入队))
            channel.basicNack(deliveryTag,false,false);
        } catch (IOException e) {
            // 网络中断
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值