RabbitMq使用方法

1.RabbitMq运行机制

发送者(Producer) --> 交换机(Exchange) --> 队列(Queues) --> 消费者(Consumer) ​​​​​​​

2.交换机(Exchange) 类型

direct(直接):消息精确发送,而且只能到达一个队列

fanout(扇出):消息会分发到所有绑定的队列上。

topic(主题):与fanout类似,但是多了个属性路由key绑定,消息会发送到路由key对应的队列上

3.spring整合rabbitmq

3.1 导入amqp依赖

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

 3.2 yml配置rabbitmq

rabbitmq:
    host: 128.122.111.68
    port: 5672
    username: root
    password: 123456
    virtual-host: /
    #开启发送端确认
    publisher-confirms: true
    #开启发送端信息抵达队列的确认
    publisher-returns: true
    #只要抵达队列,以异步发送优先回掉我们这个returnconfirm
    template:
      mandatory: true
    #手动ack消息
    listener:
      simple:
        acknowledge-mode: manual

3.3 在启动类上加上@EnableRabbit注解

3.4 配置 把RabbitMQ接收和发送的数据转为json

1. AmqpAdmin:管理组件
2. RabbitTemplate:消息发送处理组件
3. @RabbitListener 监听消息的方法可以有三种参数(不分数量,顺序)
• Object content, Message message, Channel

package com.wanxin.gulimall.order.config;


import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Rabbitmq 消息json化
 *
 * @author common
 * @date 2022/05/23
 */
@Configuration
public class MyRabbitConfig {
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
 }

3.5 测试发送消息

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallOrderApplicationTests {

    // 可以创建交换机,队列,并管理
    @Autowired
    private AmqpAdmin amqpAdmin;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 第一步:创建交换机
     * 1、如何创建Exchange、Queue、Binding
     *      1)、使用AmqpAdmin进行创建
     * 2、如何收发消息
     */
    @Test
    public void createExchange() {

        /**
         * name 名称
         * durable 是否持久化
         * autoDelete 是否可自动删除,没有绑定任何队列时删除
         */
        Exchange directExchange = new DirectExchange("hello-java-exchange",true,false);
        amqpAdmin.declareExchange(directExchange);
        log.info("Exchange[{}]创建成功:","hello-java-exchange");
    }


    /**
     * 第二步:创建队列
     */
    @Test
    public void testCreateQueue() {
        /**
         * name 名称
         * durable 是否持久化
         * exclusive 是否排它, 如果有一个连接了其他就无法连接它,这个最好设置为false
         * autoDelete 是否可自动删除,没有绑定任何队列时删除
         */
        Queue queue = new Queue("hello-java-queue",true,false,false);
        amqpAdmin.declareQueue(queue);
        log.info("Queue[{}]创建成功:","hello-java-queue");
    }


    /**
     *第三步:交换机与队列进行绑定
     */
    @Test
    public void createBinding() {

        /**
         * 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);
        log.info("Binding[{}]创建成功:","hello-java-binding");

    }

    /**
     * 第四步:交换机发送消息
     */
    @Test
    public void sendMessageTest() {
        OrderReturnReasonEntity reasonEntity = new OrderReturnReasonEntity();
        reasonEntity.setId(1L);
        reasonEntity.setCreateTime(new Date());
        reasonEntity.setName("reason");
        reasonEntity.setStatus(1);
        reasonEntity.setSort(2);
        String msg = "Hello World";
        //1、发送消息,如果发送的消息是个对象,会使用序列化机制,将对象写出去,对象必须实现Serializable接口

        //2、发送的对象类型的消息,可以是一个json
        /**
         * String exchange, 交换机
         * String routingKey,  路由键,要与第四步绑定时设置的路由键有关系才能接收
         * Object object, 发送的对象,如果时对象那么这个对象必须是序列化的
         * @Nullable CorrelationData correlationData
         */
        rabbitTemplate.convertAndSend("hello-java-exchange","hello2.java",
                reasonEntity,new CorrelationData(UUID.randomUUID().toString()));
        rabbitTemplate.convertAndSend("hello-java-exchange","hello.java",
                reasonEntity,new CorrelationData(UUID.randomUUID().toString()));
        log.info("消息发送完成:{}",reasonEntity);
    }

//    @Test
//    public void create() {
//        HashMap<String, Object> arguments = new HashMap<>();
//        arguments.put("x-dead-letter-exchange", "order-event-exchange");
//        arguments.put("x-dead-letter-routing-key", "order.release.order");
//        arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
//        Queue queue = new Queue("order.delay.queue", true, false, false, arguments);
//        amqpAdmin.declareQueue(queue);
//        log.info("Queue[{}]创建成功:","order.delay.queue");
//    }

}

3.5 测试接收消息

1.@RabbitListener 类+方法上(监听哪些队列)

2.@RabbitHandler 标在方法上(重载区分不同的消息)

    /**
     * queues:声明需要监听的队列
     * message : 如果我们不知道发送的对象类型,可以统一用org.springframework.amqp.core.Message 来接收
     * OrderReturnReasonEntity:接收消息的对象,当时传的是什么类型的对象,接收的时候就是什么类型的对象
     * Message message,OrderReturnReasonEntity 这两个参数写一个就可以
     * channel:当前传输数据的通道
     */
    @RabbitListener(queues = {"hello-java-queue"})
    public void revieveMessage(Message message, OrderReturnReasonEntity content, Channel channel) {
        //拿到主体内容
        byte[] body = message.getBody();
        //拿到的消息头属性信息
        MessageProperties messageProperties = message.getMessageProperties();
        System.out.println("接受到的消息...内容" + message + "===内容:" + content.getClass());

    }

4.RabbitMq信息确认机制

@发送端

1.ConfirmCallback : Broker收到消息就回调(发送端 --> Broker)

1.1 publisher-confirms: true

1.2 设置确认回调ConfirmCallback

2.消息未正确抵达Queue进行回调(Exchange --> Queue)

2.1 publisher-returns: true
template:
mandatory: true
2.2 设置确认回调ReturnCallback

@消费端(保证每一个消息被正确消费, 此时才可以broker删除这个消息)

​​​​​​​

1.默认是自动确认的,只要消息接收到,客户端就会自动确认,服务端就会移除这个消息

问题: 

我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了,发生消息丢失;

解决方法:

消费者手动确认模式。只要我们没有明确动告诉MQ,消息没有ack(货物没有被签收),
消息就一直是unacked(未被接收)状态,即使Consumer(消费者)宕机,消息也不会丢失,会重新变为Ready(准备被接收)状态,
下一次有新的Consumer(消费者)连接进来就发给他

2.如何签收
channel.basicAck(deliveryTag,false); 签收: 业务成功就应该签收
channel.basicNack(deliveryTag,false,false); 拒签: 业务失败就应该拒签

4.1 java代码配置消息确认配置

package com.wanxin.gulimall.order.config;


import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.PostMapping;

import javax.annotation.Resource;

/**
 * Rabbitmq 消息json序列化
 *
 * @author common
 * @date 2022/05/23
 */
@Configuration
public class MyRabbitConfig {

    @Resource
    RabbitTemplate rabbitTemplate;

    /**
     * 消息json序列化
     *
     * @return {@link MessageConverter}
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     * MyRabbitConfig对象创建完成以后,执行这个方法
     *
     * @发送端
     * 1.Broker收到消息就回调(发送者 --> Broker)
     *      1. publisher-confirms: true
     *      2. 设置确认回调ConfirmCallback
     *
         * 2.消息正确抵达Queue进行回调(Exchange --> Queue)
     *      1. publisher-returns: true
     *         template:
     *           mandatory: true
     *      2.设置确认回调ReturnCallback
     *
     * @消费端 (保证每一个消息被正确消费, 此时才可以broker删除这个消息)
     *      1.默认是自动确认的,只要消息接收到,客户端就会自动确认,服务端就会移除这个消息
     *        1. listener:
     *           simple:
     *            acknowledge-mode: manual
     *      问题:
     *          我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了,发生消息丢失;
     *      解决方法:
     *          消费者手动确认模式。只要我们没有明确动告诉MQ,消息没有ack(货物没有被签收),
     *          消息就一直是unacked(未被接收)状态,即使Consumer(消费者)宕机,消息也不会丢失,会重新变为Ready(准备被接收)状态,
     *          下一次有新的Consumer(消费者)连接进来就发给他
     *
     *      2.如何签收
     *            channel.basicAck(deliveryTag,false); 签收: 业务成功就应该签收
     *            channel.basicNack(deliveryTag,false,false); 拒签: 业务失败就应该拒签
     *
     */
    @PostMapping
    public void initRabbitTemplate(){
        //消息抵达Broker的确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 确认
             * 1.只要消息抵达Broker就 b=true
             * @param correlationData 当前消息的唯一关联数据(这个是消息的唯一id)
             * @param b               消息是否成功到达Broker
             * @param s               失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("confirm。。。correlationData["+correlationData+"]==>b["+b+"]==>s["+s+"]");
            }
        });

        //消息抵达Queue的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 返回消息
             * 只要消息没有抵达指定的Queue,就会触发这个失败回调
             * @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+"]==>i["+i+"]==>s["+s+"]==>s1["+s1+"]==>s2["+s2+"]");
            }
        });
    }
 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值