一,基本概念
MQ是消費-生產者模型的一個典型的代表,一端往消息隊列中不斷寫入消息,而另一端則可以讀取或者訂閱隊列中的消息。MQ和JMS類似,但不同的是JMS是SUN JAVA消息中間件服務的一個標準和API定義,而MQ則是遵循了AMQP協議的具體實現和產品。
RabbitMQ是一個在AMQP基礎上完整的,可複用的企業消息系統。他遵循Mozilla Public License開源協議。
RabbitMQ是流行的開源消息隊列系統,用erlang語言開發。RabbitMQ是AMQP(高級消息隊列協議)的標準實現。
1、AMQP
模型圖如下:
主要概念:
Broker:簡單來說就是消息隊列服務器實體。
Exchange:消息交換機,決定了消息路由規則,它指定消息按什麼規則,路由到哪個隊列。
Queue:消息隊列載體,每個消息都會被投入到一個或多個隊列。
Binding:綁定,它的作用就是把exchange和queue按照路由規則綁定起來,意即爲符合什麼樣路由規則的消息,將會放置入哪一個消息隊列。
Routing
Key:路由關鍵字,exchange根據這個關鍵字進行消息投遞。
vhost:虛擬主機,一個broker裏可以開設多個vhost,用作不同用戶的權限分離。
producer:消息生產者,就是投遞消息的程序。
consumer:消息消費者,就是接受消息的程序。
channel:進行消息讀寫的通道,在客戶端的每個連接裏,可建立多個channel,每個channel代表一個會話任務。
MessageQueue、Exchange和Binding構成了AMQP協議的核心,三者關係如下圖:
消息隊列的使用過程大概如下:
(1)客戶端連接到消息隊列服務器,打開一個channel。
(2)客戶端聲明一個exchange,並設置相關屬性。
(3)客戶端聲明一個queue,並設置相關屬性。
(4)客戶端使用routing
key,在exchange和queue之間建立好綁定關係。
(5)客戶端投遞消息到exchange。
exchange接收到消息後,就根據消息的key和已經設置的binding,進行消息路由,將消息投遞到一個或多個隊列裏。
*Exchange是接受生產者消息並將消息路由到消息隊列的關鍵組件。ExchangeType和Binding決定了消息的路由規則。
*在RabbitMQ中,聲明一個Exchange需要三個參數:ExchangeName,ExchangeType和Durable。Durable是該Exchange的持久化屬性。
*有三種類型的Exchange:direct,fanout和topic,不同的Exchange會表現出不同路由行爲。
a)如果是Direct類型,則會將消息中的RoutingKey與該Exchange關聯的所有Binding中的BindingKey進行比較,如果相等,則發送到該Binding對應的Queue中。
b)如果是Fanout類型,則會將消息發送給所有與該 Exchange定義過Binding 的所有Queues中去,其實是一種廣播行爲。
c)如果是Topic類型,則會按照正則表達式,對RoutingKey與BindingKey進行匹配,如果匹配成功,則發送到對應的Queue中。(符號”#”匹配一個或多個詞,符號”*”匹配正好一個詞)
詳細解釋:
Exchange可能bind了很多Queue,但是消息具體分到哪一個或哪一些Q呢?具體要看exchange的類型。
具體exchange也有幾個類型:
1、完全根據key進行投遞的叫做Direct交換機,例如,綁定時設置了routingkey爲”abc”,那麼客戶端提交的消息,只有設置了key爲”abc”的纔會投遞到隊列。
2、對key進行模式匹配後進行投遞的叫做Topic交換機,符號”#”匹配一個或多個詞,符號”*”匹配正好一個詞。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。
3、還有一種不需要key的,叫做Fanout交換機,它採取廣播模式,一個消息進來時,投遞到與該交換機綁定的所有隊列。
RabbitMQ支持消息的持久化,也就是數據寫在磁盤上,爲了數據安全考慮,我想大多數用戶都會選擇持久化。消息隊列持久化包括3個部分:
(1)exchange持久化,在聲明時指定durable
=> 1
(2)queue持久化,在聲明時指定durable => 1
(3)消息持久化,在投遞時指定delivery_mode
=> 2(1是非持久化)
如果exchange和queue都是持久化的,那麼它們之間的binding也是持久化的。如果exchange和queue兩者之間有一個持久化,一個非持久化,就不允許建立綁定。
AMQP協議可參考以下文章:http://blog.chinaunix.net/uid-22312037-id-3458208.html
2、簡單實現
一下爲java的簡單實現:
客戶端:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; public class Send { private final static String QUEUE_NAME = "hello"; public static void main(String[] args) throws.IOException{ ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); 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(); } } 消費者端: public class RabbitMQRecv { private final static String QUEUE_NAME = "hello"; public static void main(String avg[]) throws.IOException, java.lang.InterruptedException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 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 + "'"); } } }
*通過設置Exchange和MessageQueue的durable屬性爲true,可以使得隊列和Exchange持久化; *生產者在發送消息的時候,將deliverymode設置爲MessageProperties.PERSISTENT_TEXT_PLAIN; *durable爲true的Exchange和durable爲ture的Queues才能綁定,否則在綁定時,RabbitMQ都會拋錯的。