利用Rabbit MQ 實現一對多通知功能(動態添加刪除隊列交換機)

樓主在項目中需要實現分佈式lucene查詢,由於lucene的索引是存放在本地的。網上有很多方案實現起來相對比較複雜,故樓主爲了簡單化針對索引同步問題採用的方案是,如果某一結點發生索引的增刪改,通過rabbitmq通知所有lucene節點也進行本地的索引的更改。
fanout類型的exchange雖然可以通知所有隊列,但一個隊列只能綁定一個消費者(如果綁定多個消費者,只要有一個消費者消費了這個消息,其他的消費者就獲得不到這個消息了,至於手動ask經測試不行,具體原因沒有細看),然後受spring cloud bus的啓發,每個lucene實例都綁定一個獨立的隊列,所有的隊列綁定到同一個exchange,從而實現一對多通知的功能。
同時,考慮到上線和拓展的便捷性,要求隊列的添加和綁定全部動態自動完成。代碼邏輯不復雜,只是有些類不常用,網上的資料相對較少,故把完整源碼貼出來,供大家學習交流。代碼質量不高,如有錯誤歡迎大家及時指正!

package com.unionpay.wash.lucene_service.MQListener;

import com.rabbitmq.http.client.domain.QueueInfo;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitManagementTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;
@Component
public class MQListener implements CommandLineRunner {
    @Autowired
    private RabbitTemplate template;
    @Autowired
    private AmqpAdmin admin;
    private static String queueName;
    @Override
    public void run(String... args) throws Exception {
        RabbitManagementTemplate managementTemplate = new RabbitManagementTemplate();
        String exchangeName = "lucene_exchange";
        Exchange exchange = managementTemplate.getExchange(exchangeName);
        if (exchange == null) {
            managementTemplate.addExchange(new FanoutExchange(exchangeName));
        }
        List<QueueInfo> queues = managementTemplate.getClient().getQueues();
        for (QueueInfo queueInfo : queues){
            String s = queueInfo.getName();
            String idleSince = queueInfo.getIdleSince();
            if (queueInfo.getConsumerCount() == 0 && s.contains("lucene_index") && idleSince != null){
                managementTemplate.deleteQueue(new Queue(s));
            }
        }
        queueName = "lucene_index_"+UUID.randomUUID();
        managementTemplate.addQueue(new Queue(queueName));
        Binding binding = BindingBuilder.bind(new Queue(queueName)).to(new FanoutExchange(exchangeName));
        admin.declareBinding(binding);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try{
                        Object o = template.receiveAndConvert(queueName,100);
                        if (o == null){
                            Thread.sleep(1000);
                        }else{

                            //TODO
                        }
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }catch (AmqpException e){
                        managementTemplate.addQueue(new Queue(queueName));
                        BindingBuilder.bind(new Queue(queueName)).to(new FanoutExchange(exchangeName));
                    }


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