【RabbitMQ】——隊列模式(1)

        MQ:Message Queue,消息隊列,是系統和系統之間的通信方法。

        RabbitMQ是MQ的一種實現,較爲常用的還有ActiveMQ,Kafka(分佈式發佈訂閱消息系統)等。本篇博客是RabbitMQ的入門,簡單的瞭解一些RabbitMQ的5中隊列,分別爲:簡單隊列、work模式、訂閱模式、路由模式、通配符模式。

 

環境:windows上安裝RabbitMQ


工具類:用來獲取連接的,下面所有隊列的中獲取連接時使用,具體實現如下

public class ConnectionUtil {
 
    publicstatic Connection getConnection() throws Exception {
       //定義連接工廠
       ConnectionFactory factory = new ConnectionFactory();
       //設置服務地址
       factory.setHost("localhost");
       //端口
       factory.setPort(5672);
       //設置賬號信息,用戶名、密碼、vhost
       factory.setVirtualHost("/test");
       factory.setUsername("test");
       factory.setPassword("test");
       // 通過工程獲取連接
       Connection connection = factory.newConnection();
       return connection;
    }
 
}

簡單隊列:

 

        

        P:消息生成者

        C:消息消費者

        紅色:消息隊列

 

  •         最簡單的模式,生產者將消息發送到隊列,消費者從隊列中獲取消息。
  •         一條消息只能被消費一次,消費完畢後消息被刪除。

  

生產者代碼:

public class Send {
 
   private final static String QUEUE_NAME = "test_queue";
 
   public static void main(String[] argv) throws Exception {
       // 獲取到連接以及mq通道,ConnectionUtil爲封裝的工具類
       Connection connection = ConnectionUtil.getConnection();
       // 從連接中創建通道
       Channel channel = connection.createChannel();
 
       // 聲明(創建)隊列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 
        // 消息內容
       String message = "Hello World!";
       channel.basicPublish("", QUEUE_NAME, null,message.getBytes());
       System.out.println(" [x] Sent '" + message + "'");
 
       //關閉通道和連接
       channel.close();
       connection.close();
    }
}


啓動完畢後從服務上查詢消息隊列,如下:

        有一個名爲test_queue的隊列,消息數量爲1。

 

消費者代碼:

public class Recv {
 
   private final static String QUEUE_NAME = "test_queue";
 
   public static void main(String[] argv) throws Exception {
 
       // 獲取到連接以及mq通道
       Connection connection = ConnectionUtil.getConnection();
       Channel channel = connection.createChannel();
 
       // 聲明隊列,要創建的隊列已經存在則不再創建
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 
       // 定義隊列的消費者
       QueueingConsumer consumer = new QueueingConsumer(channel);
       // 監聽隊列
       channel.basicConsume(QUEUE_NAME, true, consumer);
 
       // 獲取消息
       while (true) {
           QueueingConsumer.Delivery delivery = consumer.nextDelivery();
           String message = new String(delivery.getBody());
           System.out.println(" [x] Received '" + message +"'");
       }
    }
}

work模式:

        P:生產者

        C1、C2:消費者

        紅色:消息隊列

 

  •         一條消息只能被一個消費者消費

 

生產者代碼:

public class Send {
 
   private final static String QUEUE_NAME = "test_queue_work";
 
   public static void main(String[] argv) throws Exception {
       // 獲取到連接以及mq通道
       Connection connection = ConnectionUtil.getConnection();
       Channel channel = connection.createChannel();
 
       // 聲明隊列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 
       //發送50條消息
       for (int i = 0; i < 50; i++) {
           // 消息內容
           String message = "" + i;
           channel.basicPublish("", QUEUE_NAME, null,message.getBytes());
           System.out.println(" [x] Sent '" + message + "'");
 
           Thread.sleep(i * 10);
       }
 
       channel.close();
        connection.close();
    }
}

 

消費者1:獲取一條消息休眠10ms後,返回確認狀態

public class Recv {
 
   private final static String QUEUE_NAME = "test_queue_work";
 
   public static void main(String[] argv) throws Exception {
 
       // 獲取到連接以及mq通道
       Connection connection = ConnectionUtil.getConnection();
       Channel channel = connection.createChannel();
 
       // 聲明隊列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 
       // 同一時刻服務器只會發一條消息給消費者
       //channel.basicQos(1);
 
       // 定義隊列的消費者
       QueueingConsumer consumer = new QueueingConsumer(channel);
       // 監聽隊列,手動返回完成
       channel.basicConsume(QUEUE_NAME, false, consumer);
 
       // 獲取消息
       while (true) {
           QueueingConsumer.Delivery delivery = consumer.nextDelivery();
           String message = new String(delivery.getBody());
           System.out.println(" [x] Received '" + message +"'");
           //休眠
           Thread.sleep(10);
           // 返回確認狀態
           channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
       }
    }
}


消費者2:獲取一條消息休眠1000ms後,返回確認狀態

public class Recv2 {
 
   private final static String QUEUE_NAME = "test_queue_work";
 
   public static void main(String[] argv) throws Exception {
 
       // 獲取到連接以及mq通道
       Connection connection = ConnectionUtil.getConnection();
       Channel channel = connection.createChannel();
 
       // 聲明隊列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
 
       // 同一時刻服務器只會發一條消息給消費者
       //channel.basicQos(1);
 
       // 定義隊列的消費者
       QueueingConsumer consumer = new QueueingConsumer(channel);
       // 監聽隊列,手動返回完成狀態
       channel.basicConsume(QUEUE_NAME, false, consumer);
 
       // 獲取消息
       while (true) {
           QueueingConsumer.Delivery delivery = consumer.nextDelivery();
           String message = new String(delivery.getBody());
           System.out.println(" [x] Received '" + message +"'");
           // 休眠1秒
           Thread.sleep(1000);
 
           channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
       }
    }
}


運行結果:

         消費者1和消費者2獲得的消息數量相同

         但是這樣是不合理的,消費者2獲得消息後休眠的時間長,所以它得到的消息數量應該少用消費者1。

 

work模式——能者多勞

         將上面兩個消費者中的註解代碼 channel.basicQos(1) 解注,這就意味着同一時刻服務器只發一條消息給消費者,消費者返回確認狀態後獲得新的消息,這樣消費者2獲得的消息數量就少於消費者1.

 

 

消息確認模式:

自動確認:

         消費者從隊列中獲取消息,無論消費者獲取到消息後是否成功消費,服務端都會認爲消息已經被成功消費。

         如下代碼中,消費者不需要向服務器反饋

        

 

手動確認:

         消費者從隊列獲得消息,服務器會將該消息標記爲不可用狀態,等待消費者反饋。如果消費者一直沒有反饋消息將一直處於不可用狀態。

手動模式消費者需要向服務端反饋狀態。

        

 

小結:

         文章中的兩種MQ模式組成包括生產者、消費者、隊列,還有在代碼中體現出來的Connection連接、Channel通道。MQ的使用和hibernate、mysql的流程及其相像。首先獲得連接,通過連接獲得通道,通過通道完成消息隊列的聲明、消息的發佈,對消息的監聽等系列工作。

 

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