訊息佇列究竟是個什麼鬼?

程式設計師共同成長發表於2019-02-20

本文簡單的介紹了一下RabbitMQ、應用場景以及使用Java和Python操作MQ的簡單例子。

01   什麼是RabbitMQ?

    RabbitMQ 是一個基於AMQP協議,服務端用Erlang開發的訊息佇列,支援Java、Python、Ruby、.NET、JMS、C、PHP、ActionScript、XMPP、STOMP等,支援AJAX。所謂訊息佇列就是用來實現系統與系統之間,程式與程式之間進行通訊的中介軟體。整體來看是一個非同步的過程,由生產者(Publish)來生產訊息,這個訊息會被先放到一個容器中,當滿足一定條件時,這個訊息會被消費者(Subscribe )拿走去消費。這個容器就是佇列。生產者和消費者之間遵守的協議就是AMQP協議。其次還可以對消費者設定一個優先順序(Priority),以及對消費者的請求進行限流,對負載進行有效均衡。

02    應用場景有哪些?

1、使用者註冊時需要傳送驗證郵箱或者簡訊驗證。使用訊息佇列之後,應用程式只需要關心註冊成功即可。不需要等待郵件或者簡訊傳送成功的響應。因為這個是由消費者去完成的。

2、電商系統,使用者下單消費成功之後,對應的庫存需要進行更新。我們可以呼叫庫存系統提供的介面,但是這樣如果庫存系統出現故障就會導致庫存不能準確的更新。而且耦合性非常高。我們可以透過訊息佇列進行解耦,而且訊息佇列具有持久化功能。保證資料的準確性

3、秒殺系統,可以透過訊息佇列過濾掉部分請求,緩解服務的壓力。

4、註冊、日誌、監控系統等大部分只要求最終一致性的場景

03   訊息佇列常見關鍵詞

AMQP的核心是Producer(訊息生產者)、Broker(訊息佇列的伺服器實體)、Consumer(訊息消費者)

Producer/Consumer概念比較好理解,無非就是一個生產者建立一個資訊去由消費者去進行相關的邏輯處理。

Broker訊息佇列的伺服器,一個Broker可以包含多個VirtualHost(虛擬主機),主要起到了一個隔離的作用。 而一個VirtualHost又包括以下三部分

Exchange(交換機):由它按照某些規則 去決定訊息最終路由到哪個佇列。

Binding:繫結,它的作用就是把 Exchange 和 Queue 按照路由規則繫結起來。如果沒有bind,訊息會直接被丟掉。

Queue:儲存訊息的地方,每個訊息都會被投入到一個或多個佇列。。

整體流程如下圖(源自網路)

訊息佇列究竟是個什麼鬼?

04    Java操作RabbitMQ

1、建立連結工廠的工具類

import com.rabbitmq.client.ConnectionFactory;

public class RabbitFactory {

   public ConnectionFactory getFactory() {
       // 建立連結工廠
       ConnectionFactory factory = new ConnectionFactory();
       factory.setHost("127.0.0.1");
       factory.setUsername("guest");
       factory.setPassword("guest");
       factory.setPort(5672);
       return factory;
   }
}

2、生產者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
* 訊息生產者
*/

public class Producer {
   /**
    * 宣告佇列名
    */

   public final static String QUEUENAME = "rabbitMQ";

   public static void main(String[] args) throws IOException, TimeoutException {

       // 獲取連線工廠
       RabbitFactory rabbitFactory = new RabbitFactory();
       ConnectionFactory factory = rabbitFactory.getFactory();

       // 建立一個新的連結
       Connection connection = factory.newConnection();
       // 建立一個通道
       Channel channel = connection.createChannel();
       // 宣告一個佇列
       channel.queueDeclare(QUEUENAME, false, false, false, null);

       //傳送一個訊息至佇列中
       channel.basicPublish("", QUEUENAME, null,"Hello World".getBytes());
       System.out.println("Producer Send Message Over");
       // 關閉通道和連線
       channel.close();
       connection.close();
   }
}

3、消費者程式碼

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Customer {
   /**
    * 宣告佇列名
    */

   public final static String QUEUENAME = "rabbitMQ";

   public static void main(String[] args) throws IOException, TimeoutException {
       RabbitFactory rabbitFactory = new RabbitFactory();
       ConnectionFactory factory = rabbitFactory.getFactory();

       // 建立一個新的連結
       Connection connection = factory.newConnection();
       // 建立一個通道
       Channel channel = connection.createChannel();
       // 宣告一個佇列
       channel.queueDeclare(QUEUENAME, false, false, false, null);

       //建立一個消費者
       Consumer consumer = new DefaultConsumer(channel) {
           @Override
           public void handleDelivery(String consumerTag,
                                      Envelope envelope,
                                      AMQP.BasicProperties properties,
                                      byte[] body)
throws IOException
{
               String message = new String(body, "UTF-8");
               System.out.println("[x]Received : " + message);
           }
       };
       //channel繫結佇列、消費者、autoAck為true表示一旦收到訊息則自動回覆確認訊息
       channel.basicConsume(QUEUENAME, true, consumer);
   }
}

05  Python操作RabbitMQ

1、定義一個生產者

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1'))

# 建立頻道
channel = connection.channel()

# 宣告一個訊息佇列
channel.queue_declare('mq_test')

# 訊息不能直接到達queue,需要經過exchange,exchange='' 表示使用預設的exchange
channel.basic_publish(exchange='', routing_key='mq_test', body='hello world')

print('push end')
# 關閉傳送訊息的mq連線
connection.close()

2、定義一個消費者

import pika

# 連線mq
connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672))
# 建立一個通道
channel = connection.channel()
# 宣告mq_test的佇列,如果不存在 則自動建立
channel.queue_declare(queue='mq_test')

# 定義一個回撥函式,列印收到的資訊
def callback(ch, method, properties, body):
  print('receive msg: %s' % body)

# 指明從哪個佇列(queue)接收message
# no_ack=True,表示不對訊息進行確認
channel.basic_consume(callback, queue='mq_test', no_ack=True)

print('waiting for msg')
channel.start_consuming()

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562041/viewspace-2636508/,如需轉載,請註明出處,否則將追究法律責任。

相關文章