rabbitmq-生產端消息確認

當生產端推送消息到rabbit上時我們需要確認消息是否正常的推送到rabbit上了,推送成功了生產端無需在關心消息的進一步處理,推送失敗了生產端需要對該信息做進一步的處理。

rabbit爲該類問題提供了對應的解決策略。

  • addConfirmListener
  • addReturnListener
addConfirmListener:監聽信息是否有推送到rabbit中
/**
 * Channel 對象提供的 ConfirmListener()回調方法只包含 deliveryTag(當前 Chanel 發出的消息序號),我們需要自己爲
 * 每一個 Channel 維護一個 unconfirm 的消息序號集合,每 publish 一條數據,集合中元素加 1,每回調一次 handleAck
 * 方法,unconfirm 集合刪掉相應的一條(multiple=false)或多條(multiple=true)記錄。從程序運行效率上看,這個
 * unconfirm 集合最好採用有序集合 SortedSet 存儲結構。實際上,SDK 中的 waitForConfirms()方法也是通過 SortedSet
 * 維護消息序號的。
 * Created by py
 * 2020/4/20
 */
public class SendAync {
    private static final String QUEUE_NAME = "QUEUE_simple_confirm_aync";
    public static void main(String[] args) throws IOException, TimeoutException {
        /* 獲取一個連接 */
        Connection connection = RabbitUtils.getInstance();
        /* 從連接中創建通道 */
        Channel channel = connection.createChannel();
        channel.basicQos(1);
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //生產者通過調用channel的confirmSelect方法將channel設置爲confirm模式
        channel.confirmSelect();
        final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
        channel.addConfirmListener(new ConfirmListener() {
            //每回調一次handleAck方法,unconfirm集合刪掉相應的一條(multiple=false)或多條(multiple=true)記錄。
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                if (multiple) {
                    System.out.println("--multiple--");
                    confirmSet.headSet(deliveryTag + 1).clear();
                    //用一個SortedSet, 返回此有序集合中小於end的所有元素。
                } else {
                    System.out.println("--multiple false--");
                    confirmSet.remove(deliveryTag);
                }
            }
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                //正常只有rabbit出問題纔會走此方法:如磁盤寫滿了,MQ出現了一些異常,或者Queue容量到達上限了等等       
                 System.out.println("Nack, SeqNo: " + deliveryTag + ", multiple: " + multiple);
                if (multiple) {
                    confirmSet.headSet(deliveryTag + 1).clear();
                } else {
                    confirmSet.remove(deliveryTag);
                }
            }
        });
        String msg = "Hello QUEUE !";
        //while (true) {
        for (int i = 0; i <10 ; i++) {
            long nextSeqNo = channel.getNextPublishSeqNo();
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            confirmSet.add(nextSeqNo);
        }

        //}
    }
}
/**
 * Created by py
 * 2020/4/19
 */
public class RecRabbit {


    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        /*//老版本監聽,已過時
        oldGet(channel);*/
        //聲明隊列 如果能確定是哪一個隊列 這邊可以刪掉,不去掉 這裏會忽略創建
        channel.basicQos(1);
        channel.queueDeclare("QUEUE_simple_confirm_aync",false,false,false,null);
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String message = new String(body, "UTF-8");
                System.err.println("new==Received '" + message + "'");
            }
        };
        channel.basicConsume("QUEUE_simple_confirm_aync",true,defaultConsumer);
    }
}
addReturnListener:監聽是否有推送到隊列中(信息可能推送到rabbit中了但是沒有推送進指定的隊列中)
/**
 * Created by py
 * 2020/4/19
 */
public class SendRabbit {

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
       /* * 獲取一個連接 */
        Connection instance = RabbitUtils.getInstance();
        /** 從連接中創建通道*/
        Channel channel = instance.createChannel();
        /** 在通道中聲明一個隊列
         * 因爲我們要往隊列裏面發送消息,這是後就得知道往哪個隊列中發送,就好比在哪個管子裏面放水
         **/
        boolean durable=false;
        boolean exclusive=false;
        boolean autoDelete=false;
        //channel.queueDeclare("simple_queue",durable,exclusive,autoDelete,null);
        channel.exchangeDeclare("test_exchange_topic","topic",false);
        //第一個參數是exchangeName(默認情況下代理服務器端是存在一個""名字的exchange的,
        //因此如果不創建exchange的話我們可以直接將該參數設置成"",如果創建了exchange的話
        //我們需要將該參數設置成創建的exchange的名字,第二個參數是路由鍵
        /*channel.confirmSelect();*/
        String msg = "This is simple queque!";
        //channel.basicPublish("","simple_queue",null,msg.getBytes());
        //basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)
        /**
         * exchange:交換機名稱
         * routingKey:交換機與隊列綁定的key
         * mandatory:消息爲傳送到指定隊列時broker的處理規則,
         *     true:由生產者定義的returnListener接收,false:broker直接刪除該條信息,
         *     todo 默認爲false
         * props:請求時攜帶的參數配置;如:配置死信隊列
         * body:傳輸的信息
         */
        channel.basicPublish("test_exchange_topic","audit.saas.534351",true,null,msg.getBytes());
        /*if(channel.waitForConfirms()){
            System.out.println("信息發送成功");
        }else{
            System.out.println("信息發送失敗");
        }*/
        CustomerReturnListener customerReturnListener = new CustomerReturnListener();
        channel.addReturnListener(customerReturnListener);


        channel.close();
        instance.close();

    }
}
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.ReturnListener;

import java.io.IOException;

/**
 * Created by py
 * 2020/4/22
 */
public class CustomerReturnListener implements ReturnListener {
    @Override
    public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
        System.out.println("i:"+i);
        System.out.println("s:"+s);
        System.out.println("s1:"+s1);
        System.out.println("s2:"+s2);
        String body = new String(bytes,"utf-8");
        System.out.println("body:"+body);
    }
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;

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

/**
 * Created by py
 * 2020/4/19
 */
public class RecMessage {


    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection instance = RabbitUtils.getInstance();
        Channel channel = instance.createChannel();
        //聲明隊列 如果能確定是哪一個隊列 這邊可以刪掉,不去掉 這裏會忽略創建
        channel.queueDeclare("simple_queue",false,false,false,null);
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String message = new String(body, "UTF-8");
                System.err.println("new==Received '" + message + "'");
            }
        };
        channel.basicConsume("simple_queue",true,defaultConsumer);
    }
}

PS:basicPublish()中mandatory參數必須設置爲true纔會返回爲投遞成功的信息給生產者,設置爲false時rabbit會自動丟棄該條信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章