RabbitMQ服務之路由篇

RabbitMQ服務之路由篇

相關聲明:

1、轉載請標明出處:

http://blog.csdn.net/why_2012_gogo/article/details/54017756

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

 

在上一篇文章中,我們介紹了RabbitMQ服務的發佈訂閱機制,也就是每個接收端都會接收到消息發送端發送的全部消息,不清楚的同學可以查看文章《RabbitMQ服務之發佈/訂閱》,這種方式不是很靈活,當然也要根據具體的業務需求來選擇使用它。這裏我們繼續介紹另一種消息的轉發機制,稱之爲“路由轉發”,對應的轉換器類型爲“direct”,它可以讓接收端可以有選擇的接收自己喜歡的消息,具體的關於“direct”轉發器的原理,請查看文章《RabbitMQ服務之入門篇》

 

l   轉發器

l   例子

 

一、轉發器

1、路由說明

這裏要介紹的轉發器,我們稱之爲“路由”轉發器,理由是消息接收端可以設置自己想要接收到的消息類型,所以不同接收端設置不同的過濾條件,而發送端同樣發送所有類型的消息,就形成了一種類似路由條件的轉發機制。那麼我們該做怎樣的處理才能正常使用,那就先提前瞭解下它的使用規則吧,後面的例子會詳細介紹羅列。

 

需要額外說明的是,路由轉發機制規則爲完全匹配,也就是接收端設置精確的過濾條件,只會接收完全匹配條件的消息!

 

2、兩端處理

在接收端和發送端,我們都需要設置統一的路由匹配條件bindingKey,對於發送端,需要這樣來處理:

channel.basicPublish(EXCHANGE_NAMEbindingKey

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

而對於接收端,我們需要這樣處理(兩個參數分別爲:轉發器名字和類型):

channel.exchangeDeclare(EXCHANGE_NAME,EXCHANGE_TYPE);

我們需要將轉發器與隊列進行綁定,具體如下:

channel.queueBind(QUEUE_NAME,EXCHANGE_NAME, bindingKey); 

如何處理接收端返回消息,應該如下操作:

channel.basicConsume(QUEUE_NAME,true, consume);

 

 

二、例子

這裏繼續以上一篇文章中的用戶操作記錄爲例,來說明RabbitMQ服務direct路由轉發器的使用特點。我們同樣設定兩個消息接收端,和一個消息發送端;一個接收端依舊負責保存記錄到文件,另一個接收端依舊打印記錄到控制檯,不同的是保存的記錄有所過濾,只保存訂單”order”類型的消息,而打印的記錄也只是打印積分”score”類型消息,而發送消息端會同時發送”order”、”score”及”order.pay”三種類型消息,預期情況下,接收端一隻保存”order”消息,接收端二隻打印”score”消息,而”order.pay”類型消息則不做處理,那麼接下來我們來驗證下這個預期的目標。

 

 

1、基類

BaseObject.java:

public class BaseObject {

    static Logger log = LogManager.getLogger(BaseObject.class.getSimpleName());

   

    public static void debug(String debug) {

       log.debug(debug);

    }

   

    public static void debug(Object obj) {

       if(null!= obj)

           log.debug(obj.toString());

    }

   

    public static void error(String error) {

       log.error(error);

    }

}

 

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

 

 

BaseConnector.java:

public class BaseConnector extends BaseObject {

    protected Channel channel;

    protected Connection connection;

   

    public BaseConnector()throws IOException, TimeoutException {  

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

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

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

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

       }

    }

   

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

    try {

           channel.close();

           connection.close();

       }catch (Exception e) {

           e.printStackTrace();

       }

    }

}

 

 

2、發送端

PublisherHandle.java:

public class PublisherHandler extends BaseConnector {

   

    public PublisherHandler()throws IOException,TimeoutException {

       super();

    }

 

    public void sendMessage(MessageInfo messageInfo,String exchangeName,String exchangeType,String bindingKey) {

       try{

           //聲明轉發

           channel.exchangeDeclare(exchangeName,exchangeType);

           //給轉發器,發送消息,並設置路由條件

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

       }catch (IOException e) {

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

       }

    }

   

    publicvoid close() {

       super.close();

    }  

}

 

 

3、接收端

ReceiverHandle.java:

public class ReceiverHandler extends BaseConnector implements Runnable,Consumer {

    private MessageInfo messageInfo =new MessageInfo();

    private int hashCode;

    private String  exchangeName;   //轉發器名字

    private Stirng  exchangeType;   //轉發器類型

    private  String bindingKey; // 路由過濾條件

   

    private volatile Thread thread;

 

    public ReceiverHandler(String exchangeName,String exchangeType,String   bindingKey)throws IOException, TimeoutException {

       this.exchangeName= exchangeName;

       this.exchangeType = exchangeType;

       this.bindingKey= bindingKey;

       super();

    }

   

    public void receiveMessage() {

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

       

       try{

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

          

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

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

           //聲明轉發器

           channel.exchangeDeclare(exchangeName,exchangeType);

           //direct & bindingKey

           channel.queueBind(queueName,exchangeName,bindingKey);

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

           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

    public void handleCancel(String arg0)throws IOException {

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

    }

 

    @Override

    public void handleCancelOk(String arg0) {

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

    }

 

    @Override

    public void handleConsumeOk(String consumeOk) {

    }

 

    @Override

    public void 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

    public void handleRecoverOk(String arg0) {

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

    }

 

    @Override

    public void handleShutdownSignal(String arg0, ShutdownSignalException arg1) {

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

    }

 

    @Override

    public void run() {

       receiveMessage();

    }

   

    public void start() {

       if(thread == null){

         thread = new Thread(this);

         thread.start();

       }

    }

}

 

recordToFile方法:

    //記錄操作到文件

    void recordToFile(String msg) {

       FileOutputStreamout = null;

      

       try{

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

           StringopFileName = "操作記錄【" +newSimpleDateFormat("yyyy-MM-dd").format(new Date())+""+".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方法:

    //記錄顯示到控制檯

    void recordToConsole(Stringmsg) {

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

    }

 

 

4、測試入口

public static void main(String[]args) {

       PublisherHandler publisher= null;

       ReceiverHandlerreceiver = null;

       ReceiverHandlerreceiver2 = null;

       try{

           receiver= new ReceiverHandler("exchange_direct","direct","order"); //接收者1-文件保存

           ThreadreceiverThread = new Thread(receiver);

           receiverThread.start();

           receiver2= new ReceiverHandler("exchange_direct","direct","score");    //接收者2-記錄打印

           ThreadreceiverThread2 = new Thread(receiver2);

           receiverThread2.start();

          

           publisher= newPublisherHandler();    //發送者

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

              MessageInfomsgInfo = new MessageInfo();

              Stringop = "order";

              if(i==3)

                  op= "score";

              elseif(i==4)

                  op= "order.pay";

                 

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

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

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

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

              publisher.sendMessage(msgInfo, "exchange_direct","direct",op);

           }

       }catch (IOException | TimeoutException e) {

           e.printStackTrace();

       }finally {

           publisher.close();

       }

}

 

 

 

5、結果顯示

保存記錄接收端:

 

 

打印控制接收端:

 

 

上圖的結果,已經證實了最開始時我們的預期,只保存”order”類型消息,只打印”score”類型的消息到控制檯。

 

 

 

 

 

 

 

 

 

 

RabbitMQ服務之路由篇就介紹到這裏,由於作者水平有限,如有問題請在評論發言或是QQ羣(245389109(新))討論,謝謝。

 

 

 

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