實現RabbitMQ廣播/動態創建隊列

實現RabbitMQ廣播/動態創建隊列

1、背景

在當前實際運用的項目中,遇到了一個需求,所有在線服務器實例都維護着一份JVM本地內存數據(系統運行參數配置);
當系統運行參數配置發生修改,需要觸發所有的實例清空和刷新本地內存數據;
系統之前的解決方案是:應用程序對外提供一個http接口地址,一個實例一個實例進行調用,觸發它們刷新JVM本地內存數據;

2、問題&思考

  • 原有的方案由於是單線程,隨着部署實例的個數越來越多,等待執行完成的時間越來越長。
  • 考慮到系統中有使用到RabbitMQ,就在想能否通過RabbitMQ實現對所有服務器的廣播呢?
  • RabbitMQ中比較適合的消息類型是基於exchange的fanout模式。
  • 但是RabbitMQ的消息通知是基於隊列queue來說的,而不是針對客戶端實例來說的。並且1個消息只能被隊列下的1個客戶端消費,這明顯不符合需求。
  • 如果需要N個實例客戶端都能同時消費同一條消息,就必須創建每個客戶端的專屬queue,綁定到exchange上,queue的個數與實例數相同。
  • 基於這個思路在網上查閱了相關資料,發現可以通過動態創建隊列queue和綁定來實現該需求。

3、解決方案

廢話不多說,直接上代碼

FanoutRabbitConfig.java

import com.wzl.rabbitmqdemo.receiver.MyMessageListener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.UUID;

/**
 * @author administrator
 * @date 2020-05-06 20:24
 */
@Configuration
public class FanoutRabbitConfig {

    public static final String MY_FANOUTEXCHANGE_NAME = "myFanoutExchange";

    //用UUID來生成一個queue名稱,也可以用服務器IP端口作爲queue名稱
    public static final String MY_QUEUE_NAME = UUID.randomUUID().toString();

    @Autowired
    RabbitTemplate rabbitTemplate;

    //創建動態queue
    @Bean
    public Queue myQueue2() {
        return new Queue(MY_QUEUE_NAME, true, true, true);
    }

    //創建Exchange
    @Bean
    public FanoutExchange fanoutExchange2() {
        return new FanoutExchange(MY_FANOUTEXCHANGE_NAME, true, true);
    }

    //綁定當前queue到Exchange
    @Bean
    public Binding bindingExchangeMyQueue2() {
        return BindingBuilder.bind(myQueue2()).to(fanoutExchange2());
    }

    //設置消息處理
    @Bean
    public SimpleMessageListenerContainer mqMessageContainer(MyMessageListener myMessageListener) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(rabbitTemplate.getConnectionFactory());
        container.setQueueNames(MY_QUEUE_NAME);
        container.setExposeListenerChannel(true);
        container.setPrefetchCount(1);//設置每個消費者獲取的最大的消息數量
        container.setConcurrentConsumers(1);//消費者個數
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//設置確認模式爲手工確認
        container.setMessageListener(myMessageListener);//消息處理類
        return container;
    }

}

MyMessageListener.java

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Service;

/**
 * @author administrator
 * @date 2020-05-06 22:59
 */
@Service
public class MyMessageListener implements ChannelAwareMessageListener {
    
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        System.out.println("消費消息:" + new String(message.getBody()));
        //確認消息
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章