RabbitMQ-两种机制和消费端优化编写方式

return机制:

  Return Listener用于处理一些不可路由的消息。

  我们的消息生产者,通过指定一个Exchange和Routingkey,把消息送到某一个队列中,

  然后我们的消费者监听队列,进行消息处理操作。

  但是在某些情况下,如果我们在发送消息的时候,当前的exchange不存在或者指定的路由key路由不到,

  这个时候我们需要监听这种不可达的消息,就要使用return listener。

生产端代码:

package com.sy.rabbitmq.return_listener;

import com.rabbitmq.client.*;
import com.sy.rabbitmq.TestProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 消息返回机制 -return  生产端
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建ConnectionFactory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(TestProperties.getIp());
        factory.setPort(TestProperties.getPort());
        factory.setVirtualHost("/");

        //2.获取Connection
        Connection connection = factory.newConnection();

        //3.获取channel
        Channel channel = connection.createChannel();

        String exchangeName = "test_confirm_exchange";
        String routeKey = "save";

        //5.发送一条消息
        String msg = "Hello RabbitMQ send confirm message!";
        //6.添加一个错误路由监听-防止消息未送达
        channel.addReturnListener(
                new ReturnListener() {
                    @Override
                    public void handleReturn(
                        int replyCode,
                        String replyText,
                        String exchange,
                        String routingKey,
                        AMQP.BasicProperties properties,
                        byte[] body
                    ) throws IOException {
                        System.out.println("-------------return无法路由消息--------------");
                    }
                }
        );
        channel.basicPublish(exchangeName,routeKey,true,null,msg.getBytes());
    }
}

消费端代码:

package com.sy.rabbitmq.return_listener;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.sy.rabbitmq.TestProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 消息返回机制 -return  消费端
 */
public class Comsumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 创建ConnectionFactory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(TestProperties.getIp());
        factory.setPort(TestProperties.getPort());
        factory.setVirtualHost("/");

        //2.获取Connection
        Connection connection = factory.newConnection();

        //3.获取channel
        Channel channel = connection.createChannel();

        //4.通过channel声明一个exchange
        String exchangeName = "test_confirm_exchange";
        String routeKey = "confirm.#";
        String queueName = "test_confirm_queue";
        channel.exchangeDeclare(exchangeName,"topic",true);

        //5.通过channel声明一个队列,绑定设置路由key
        channel.queueDeclare(queueName,true,false,false,null);
        channel.queueBind(queueName,exchangeName,routeKey);

        //6.创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //7.绑定消费到队列上
        channel.basicConsume(queueName,true,consumer);
        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.out.println(msg);
        }
    }
}

confirm机制:

正常情况下,如果消息经过交换器进入队列就可以完成消息的持久化,但如果消息在没有到达broker之前出现意外,那就造成消息丢失

  1. 通过AMQP提供的事务机制实现;
  2. 使用发送者确认模式(confirm机制)实现;

如何实现Confirm确认消息?

 第一步:在channel上开启确认模式:channel.confirmSelect()

 第二步:在channel上添加监听:addConfirmListener,监听成功和失败的返回结果,

 根据具体的结果对消息进行重新发送、或者记录日志等后续处理。

生产端代码:

package com.sy.rabbitmq.confirm_listener;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.sy.rabbitmq.TestProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 消息确认模式 -confirm  生产端
 */
public class Producer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建ConnectionFactory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(TestProperties.getIp());
        factory.setPort(TestProperties.getPort());
        factory.setVirtualHost("/");

        //2.获取Connection
        Connection connection = factory.newConnection();

        //3.获取channel
        Channel channel = connection.createChannel();

        //4.指定消息的投递模式: confirmSelect: 消息的确认模式
        channel.confirmSelect();

        String exchangeName = "test_confirm_exchange";
        String routeKey = "confirm.save";

        //5.发送一条消息
        String msg = "Hello RabbitMQ send confirm message!";
        channel.basicPublish(exchangeName,routeKey,null,msg.getBytes());

        //6.添加一个确认监听
        channel.addConfirmListener(
                new ConfirmListener() {
                    @Override
                    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                        System.out.println("----------ack----------");
                    }

                    @Override
                    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                        System.out.println("----------no-ack----------");
                    }
                }
        );
    }
}

消费端代码:

package com.sy.rabbitmq.confirm_listener;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.sy.rabbitmq.TestProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 消息确认模式 -confirm  消费端端
 */
public class Comsumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 创建ConnectionFactory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(TestProperties.getIp());
        factory.setPort(TestProperties.getPort());
        factory.setVirtualHost("/");

        //2.获取Connection
        Connection connection = factory.newConnection();

        //3.获取channel
        Channel channel = connection.createChannel();

        //4.通过channel声明一个exchange
        String exchangeName = "test_confirm_exchange";
        String routeKey = "confirm.#";
        String queueName = "test_confirm_queue";
        channel.exchangeDeclare(exchangeName,"topic",true);

        //5.通过channel声明一个队列,绑定设置路由key
        channel.queueDeclare(queueName,true,false,false,null);
        channel.queueBind(queueName,exchangeName,routeKey);

        //6.创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //7.绑定消费到队列上
        channel.basicConsume(queueName,true,consumer);
        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg = new String(delivery.getBody());
            System.out.println(msg);
        }
    }
}

新版本消费端编写方式:

消费端使用循环消费队列中的消息,代码上不太美观可以采用:

package com.sy.rabbitmq.consumer;

import com.rabbitmq.client.*;
import com.sy.rabbitmq.TestProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 新消费方式 绑定对象
 */
public class Comsumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 创建ConnectionFactory
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(TestProperties.getIp());
        factory.setPort(TestProperties.getPort());
        factory.setVirtualHost("/");

        //2.获取Connection
        Connection connection = factory.newConnection();

        //3.获取channel
        Channel channel = connection.createChannel();

        //4.通过channel声明一个exchange
        String exchangeName = "test_confirm_exchange";
        String routeKey = "confirm.#";
        String queueName = "test_confirm_queue";
        channel.exchangeDeclare(exchangeName,"topic",true);

        //5.通过channel声明一个队列,绑定设置路由key
        channel.queueDeclare(queueName,true,false,false,null);
        channel.queueBind(queueName,exchangeName,routeKey);

        //---- 老版本编写方式 ----
        //6.创建消费者
//        QueueingConsumer consumer = new QueueingConsumer(channel);
        //7.绑定消费到队列上
//        channel.basicConsume(queueName,true,consumer);
//        while(true){
//            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
//            String msg = new String(delivery.getBody());
//            System.out.println(msg);
//        }

        //---- 新方式直接绑定对象,每次消费过来会调用对象的handleDelivery ----
        channel.basicConsume(queueName,true,new MyConsumer(channel));
    }


    /**
     * 重写消费端
     */
    public static class MyConsumer extends DefaultConsumer {
        /**
         * Constructs a new instance and records its association to the passed-in channel.
         *
         * @param channel the channel to which this consumer is attached
         */
        public MyConsumer(Channel channel) {
            super(channel);
        }


        /**
         * 具体消费代码编写
         * @param consumerTag
         * @param envelope
         * @param properties
         * @param body
         * @throws IOException
         */
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("-----------------新消费方式-----------------");
            System.out.println("consumerTag"+consumerTag);
            System.out.println("envelope"+envelope);
            System.out.println("AMQP.BasicProperties"+properties);
            System.out.println("body"+new String(body));
        }
    }

}
发布了70 篇原创文章 · 获赞 18 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_40325734/article/details/104144273