RabbitMQ服務之發佈/訂閱篇

RabbitMQ服務之發佈/訂閱篇

 

相關聲明:

1、轉載請標明出處:http://blog.csdn.net/why_2012_gogo/article/details/53995557

2、本系列文章均來自於實際項目、官網、網絡資源整理而來,並自己進行修改、優化及調試,內容僅供參考;

 

在上一篇文章中,我們實現了RabbitMQ的應答、任務轉發數及隊列和消息的持久化操作,也就是一個消息只能發送給一個消費者,除非該消費者因意外被停掉後,纔會轉發給其它工作者,不清楚的同學可以回頭看下《RabbitMQ服務之任務分發篇》;而在這裏要介紹的是將消息發送給多個消費者,有點類似廣播機制(觀察者模式),也就是在第一篇文章《RabbitMQ入門篇》中介紹的轉發器,不清楚的讀者可以去瀏覽。我們這裏要說的是Fanout機制轉發器,具體請往下俯看!

 

l   轉發器

l   例子

 

1、轉發器

在RabbitMQ中,消息發送首先是發送到轉發器,然後通過相應的轉發器機制將消息轉發到指定隊列,最後接收者直接從所綁定的隊列中獲取符合條件的消息。有人會問,前面的文章中沒有使用轉發器,怎麼也可以發送消息?答案是,它們也使用了轉發器。在RabbitMQ中,有匿名轉發器的概念存在,也就是””所代表的轉發器,具體如下:

channel.basicPublish("""queue_name"

MessageProperties.PERSISTENT_TEXT_PLAIN"message".getBytes());

 

如上所示,第一個參數爲轉發器的名字,如果第二個參數(routingKey)存在,就由其決定將消息轉發到哪個隊列;如果不存在,那麼就使用臨時隊列(routingKey),此時消息轉發到哪個臨時隊列,由轉發器決定。

 

 

2、例子

這裏我們以用戶的操作記錄錄入爲例,來演示說明Fanout轉發器的使用。例子是這樣的,我們開啓兩個消息接受者,一個消息發送者發送消息,而兩個消息接受者都可以接收到發送的消息,一個接受者負責記錄文件內容到記錄文件中,另一個接受者則負責顯示操作記錄到控制檯。

 

A、基類

BaseObject.java:

publicclassBaseObject {

    staticLogger log = LogManager.getLogger(BaseObject.class.getSimpleName());

   

    publicstatic void debug(String debug) {

       log.debug(debug);

    }

   

    publicstatic void debug(Object obj) {

       if(null!= obj)

           log.debug(obj.toString());

    }

   

    publicstatic void error(String error) {

       log.error(error);

    }

}

 

該類爲所有對象繼承的基類,其中LogManager爲Java中的Log4j的使用,這裏不做相關介紹,請讀者查閱相關資料。

 

BaseConnector.java:

publicclass BaseConnector extends BaseObject {

    protectedChannel channel;

    protectedConnection connection;

   

    publicBaseConnector()throws IOException, TimeoutException {  

       ConnectionFactory factory = new ConnectionFactory();  //新建鏈接工廠並綁定主機地址

       factory.setHost("127.0.0.1");      //設置MabbitMQ所在主機ip或者主機名

       connection = factory.newConnection();   //創建連接 

       channel = connection.createChannel();   //創建頻道

       }

    }

   

    protectedvoid close() { //關閉頻道及鏈接

    try {

           channel.close();

           connection.close();

       }catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

 

B、發送端

PublisherHandle.java:

publicclass PublisherHandler extends BaseConnector {

   

    public PublisherHandler()throws IOException,TimeoutException {

       super();

    }

 

    publicvoid sendMessage(MessageInfo messageInfo,String exchangeName,String exchangeType) {

       try{

           //聲明轉發

           channel.exchangeDeclare(exchangeName,exchangeType);

           //給轉發器,發送消息

           channel.basicPublish(exchangeName,"",null, SerializationUtils.serialize(messageInfo)); 

       }catch (IOException e) {

           debug("RabbitMQSend Message Error:"+e.getMessage());

       }

    }

   

    publicvoid close() {

       super.close();

    }  

}

 

 

C、接收端

ReceiverHandle.java:

publicclass ReceiverHandler extends BaseConnector implements Runnable,Consumer {

    private MessageInfo messageInfo =new MessageInfo();

    private int hashCode;

    private String exchangeName;    // 轉發器名字

    private Stirng exchangeType;    // 轉發器類型

   

    privatevolatile Thread thread;

 

    public ReceiverHandler(String exchangeName,String exchangeType)throwsIOException, TimeoutException {

        this.exchangeName = exchangeName;

        this. exchangeType = exchangeType;

       super();

    }

   

   

    publicvoid receiveMessage() {

       hashCode = Thread.currentThread().hashCode(); //區分不同工作進程的輸出

       

       try{

           debug(hashCode+ " [*] Waiting for messages Receiving...");

          

           //不存在隊列時創建臨時隊列此時隊列須要爲非持久化類型(含消息)

           String queueName = channel.queueDeclare().getQueue();

           //聲明轉發器

           channel.exchangeDeclare(exchangeName,exchangeType);

           // fanout

           channel.queueBind(queueName,exchangeName, "");

           //指定消費隊列(是否開啓應答模式:默認關閉)

           Stringop_result = channel.basicConsume(queueName, true,this);   

           if("".equals(op_result)){

              debug("BasicConsumeConfig Consumer Queue Error!");

           }

       }catch (IOException e) {

           debug("ConsumerDelivery Error,Msg info:" + e.getMessage());

       }catch (Exception e) {

           debug("ErrorIs Opening,Msg info:" + e.getMessage());

       }

    }

 

 

    @Override

    publicvoid handleCancel(String arg0)throws IOException {

       debug("===handleCancel==="+arg0);

    }

 

    @Override

    publicvoid handleCancelOk(String arg0) {

       debug("===handleCancelOk==="+arg0);

    }

 

    @Override

    publicvoid handleConsumeOk(String consumeOk) {

    }

 

    @Override

    publicvoid handleDelivery(String consumerTag, Envelope env,

           BasicPropertiesprops, byte[] body) throws IOException {

       messageInfo= (MessageInfo) SerializationUtils.deserialize(body);

       messageInfo.setHashCode(hashCode);

       // 下面兩個在單個接收端只出現一次,這裏都羅列是因爲兩個接收端只有此處代碼不同,其它地方均相同,只是爲了簡略代碼而已

       // 記錄操作信息到文件或數據庫

       recordToFile(msgInfo.getContent());

       //顯示操作信息到控制檯或系統

       recordToConsole(msgInfo.getContent());

    }

 

    @Override

    publicvoid handleRecoverOk(String arg0) {

       debug("===handleRecoverOk==="+arg0);

    }

 

    @Override

    publicvoid handleShutdownSignal(String arg0, ShutdownSignalException arg1) {

       debug("===handleShutdownSignal==="+arg0+"===ShutdownSignalException==="+arg1.getMessage());

    }

 

    @Override

    publicvoid run() {

       receiveMessage();

    }

   

    publicvoid start() {

       if(thread == null){

         thread = new Thread(this);

         thread.start();

       }

    }

}

 

recordToFile方法:

    //記錄操作到文件

    voidrecordToFile(String msg) {

       FileOutputStream out = null;

      

       try{

           StringlocalDir = AppUnit.class.getClassLoader().getResource("").getPath();

           StringopFileName = "操作記錄【" +new SimpleDateFormat("yyyy-MM-dd").format(newDate())+""+".txt";

          

           Filefile = new File(localDir,opFileName);

           out= new FileOutputStream(file,true);

           out.write((msg+"\r\n").getBytes());

           out.flush();

           out.close();

       }catch(Exception e) {

           e.printStackTrace();

       }finally {

           try{

              out.close();

           }catch (IOException e) {

              e.printStackTrace();

           }

       }

    }

 

 

recordToConsole方法:

    //記錄顯示到控制檯

    voidrecordToConsole(Stringmsg) {

       debug("【記錄操作內容】" + msg);

    }

 

D、測試入口

publicstatic void main(String[] args){

       PublisherHandler publisher = null;

       ReceiverHandler receiver = null;

       ReceiverHandler receiver2 = null;

       try{

           receiver= new ReceiverHandler("exchange_fanout","fanout"); //接收者1

           ThreadreceiverThread = new Thread(receiver);

           receiverThread.start();

           receiver2= new ReceiverHandler("exchanage_fanout","fanout");  //接收者2

           ThreadreceiverThread2 = new Thread(receiver2);

           receiverThread2.start();

          

           publisher= new PublisherHandler();    //發送者

           for(inti=0;i<5;i++) {

              MessageInfo msgInfo = new MessageInfo();

                String message = "【記錄操作】" + "新增訂單【記錄編號】"+ (i+1) + " 【記錄日期】"+new SimpleDateFormat("yyyy-MM-dd").format(newDate());

              msgInfo.setConsumerTag("demo2_tag");   //回調標誌

              msgInfo.setChannel("demo2");    //頻道

              msgInfo.setContent(message);    //消息內容

              publisher.sendMessage(msgInfo,"exchange_fanout","fanout");

           }

       }catch (IOException | TimeoutException e) {

           e.printStackTrace();

       }finally {

           publisher.close();

       }

}

 

 

E、結果顯示

保存記錄接收端:

 

 

打印控制接收端:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

消息隊列RabbitMQ的發佈訂閱就介紹到這裏,由於作者水平有限,如有問題請在評論中發言或是QQ羣(245389109(新))討論,謝謝。

 

 

發佈了132 篇原創文章 · 獲贊 164 · 訪問量 47萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章