RabbitMQ 介紹
RabbitMQ 是一個由 Erlang 語言開發的 AMQP 的開源實現。
AMQP :Advanced Message Queue,高級消息隊列協議。它是應用層協議的一個開放標準,爲面向消息的中間件設計,基於此協議的客戶端與消息中間件可傳遞消息,並不受產品、開發語言等條件的限制。
RabbitMQ 最初起源於金融系統,用於在分佈式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。具體特點包括:
可靠性(Reliability)
RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、發佈確認。靈活的路由(Flexible Routing)
在消息進入隊列之前,通過 Exchange 來路由消息的。對於典型的路由功能,RabbitMQ 已經提供了一些內置的 Exchange 來實現。針對更復雜的路由功能,可以將多個 Exchange 綁定在一起,也通過插件機制實現自己的 Exchange 。消息集羣(Clustering)
多個 RabbitMQ 服務器可以組成一個集羣,形成一個邏輯 Broker 。高可用(Highly Available Queues)
隊列可以在集羣中的機器上進行鏡像,使得在部分節點出問題的情況下隊列仍然可用。多種協議(Multi-protocol)
RabbitMQ 支持多種消息隊列協議,比如 STOMP、MQTT 等等。多語言客戶端(Many Clients)
RabbitMQ 幾乎支持所有常用語言,比如 Java、.NET、Ruby 等等。管理界面(Management UI)
RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息 Broker 的許多方面。跟蹤機制(Tracing)
如果消息異常,RabbitMQ 提供了消息跟蹤機制,使用者可以找出發生了什麼。插件機制(Plugin System)
RabbitMQ 提供了許多插件,來從多方面進行擴展,也可以編寫自己的插件。
RabbitMQ 中的概念模型
- 所有 MQ 產品從模型抽象上來說都是一樣的過程:
消費者(consumer)訂閱某個隊列。生產者(producer)創建消息,然後發佈到隊列(queue)中,最後將消息發送到監聽的消費者。
RabbitMQ 基本概念
上面只是最簡單抽象的描述,具體到 RabbitMQ 則有更詳細的概念需要解釋。上面介紹過 RabbitMQ 是 AMQP 協議的一個開源實現,所以其內部實際上也是 AMQP 中的基本概念:
RabbitMQ 內部結構
- Message
消息,消息是不具名的,它由消息頭和消息體組成。消息體是不透明的,而消息頭則由一系列的可選屬性組成,這些屬性包括routing-key(路由鍵)、priority(相對於其他消息的優先權)、delivery-mode(指出該消息可能需要持久性存儲)等。 - Publisher
消息的生產者,也是一個向交換器發佈消息的客戶端應用程序。 - Exchange
交換器,用來接收生產者發送的消息並將這些消息路由給服務器中的隊列。 - Binding
綁定,用於消息隊列和交換器之間的關聯。一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所以可以將交換器理解成一個由綁定構成的路由表。 - Queue
消息隊列,用來保存消息直到發送給消費者。它是消息的容器,也是消息的終點。一個消息可投入一個或多個隊列。消息一直在隊列裏面,等待消費者連接到這個隊列將其取走。 - Connection
網絡連接,比如一個TCP連接。 - Channel
信道,多路複用連接中的一條獨立的雙向數據流通道。信道是建立在真實的TCP連接內地虛擬連接,AMQP 命令都是通過信道發出去的,不管是發佈消息、訂閱隊列還是接收消息,這些動作都是通過信道完成。因爲對於操作系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所以引入了信道的概念,以複用一條 TCP 連接。 - Consumer
消息的消費者,表示一個從消息隊列中取得消息的客戶端應用程序。 - Virtual Host
虛擬主機,表示一批交換器、消息隊列和相關對象。虛擬主機是共享相同的身份認證和加密環境的獨立服務器域。每個 vhost 本質上就是一個 mini 版的 RabbitMQ 服務器,擁有自己的隊列、交換器、綁定和權限機制。vhost 是 AMQP 概念的基礎,必須在連接時指定,RabbitMQ 默認的 vhost 是 / 。 - Broker
表示消息隊列服務器實體。
AMQP 中的消息路由
AMQP 中消息的路由過程和 Java 開發者熟悉的 JMS 存在一些差別,AMQP 中增加了 Exchange 和 Binding 的角色。生產者把消息發佈到 Exchange 上,消息最終到達隊列並被消費者接收,而 Binding 決定交換器的消息應該發送到那個隊列。
Exchange 類型
Exchange分發消息時根據類型的不同分發策略有區別,目前共四種類型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由鍵,此外 headers 交換器和 direct 交換器完全一致,但性能差很多,目前幾乎用不到了,所以直接看另外三種類型:direct
消息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致, 交換器就將消息發到對應的隊列中。路由鍵與隊列名完全匹配,如果一個隊列綁定到交換機要求路由鍵爲“dog”,則只轉發 routing key 標記爲“dog”的消息,不會轉發“dog.puppy”,也不會轉發“dog.guard”等等。它是完全匹配、單播的模式。fanout
每個發到 fanout 類型交換器的消息都會分到所有綁定的隊列上去。fanout 交換器不處理路由鍵,只是簡單的將隊列綁定到交換器上,每個發送到交換器的消息都會被轉發到與該交換器綁定的所有隊列上。很像子網廣播,每臺子網內的主機都獲得了一份複製的消息。fanout 類型轉發消息是最快的。topic
topic 交換器通過模式匹配分配消息的路由鍵屬性,將路由鍵和某個模式進行匹配,此時隊列需要綁定到一個模式上。它將路由鍵和綁定鍵的字符串切分成單詞,這些單詞之間用點隔開。它同樣也會識別兩個通配符:符號“#”和符號“”。#匹配0個或多個單詞,匹配不多不少一個單詞。
RabbitMq 安裝
爲了避免麻煩,我就用docker安裝RabbitMq, 本博客用CentOS 7.4 爲例
- 在終端中輸入:
# 連接服務器
ssh -p 22 root@IP
# 在終端上使用Docker安裝
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management
# 查看rabbitMq運行情況
[root@VM_140_90_centos rabbitmq]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b05c2aa0f7f4 rabbitmq:management "docker-entrypoint..." About an hour ago Up 27 minutes 0.0.0.0:4369->4369/tcp, 0.0.0.0:5671-5672->5671-5672/tcp, 0.0.0.0:15671-15672->15671-15672/tcp, 0.0.0.0:25672->25672/tcp rabbitmq03
rabbitmq就運行起來了
- 安裝amqp擴展
AMQP擴展能夠支持rabbitMQ,AMQP是rabbitMQ的協議(Advanced Message Queue Protocol 高級消息隊列協議)這個可自行百度。
1、安裝rabbitmq-c-0.8.0
爲什麼要安裝這個,不知道原因,PHP的官方網站上是怎麼說滴。
下載地址:https://github.com/alanxz/rabbitmq-c/releases/download/v0.8.0/rabbitmq-c-0.8.0.tar.gz
tar -zxvf rabbitmq-c-0.8.0.tar,gz
cd rabbitmq-c-0.8.0
./configure --prefix=/usr/local/rabbitmq-c
make
make install
2、安裝amqp
下載地址:https://pecl.php.net/package/amqp
cd amqp-1.7.1
phpize
./configure --with-php-config=/usr/local/php7/bin/php-config --with-amqp --with-librabbitmq-dir=/usr/local/rabbitmq-c/
make
make install
在/usr/local/php7/lib/php/extensions/no-debug-non-zts-20151012/ 目錄下有個amqp.so
在php.ini的尾部添加
extension=amqp.so
PHP操作RabbitMq
建立send.php
<?php
$config = [
'host' => '127.0.0.1',
'vhost' => '/',
'port' => 5672,
'login' => 'guest',
'password' => 'guest'
];
// 建立連接
$conn = new \AMQPConnection($config);
$conn->connect();
$channel = new \AMQPChannel($conn);
// 爲了一個AMQP的鏈接,創建一個channel instance(通道實例)
$queue = new \AMQPQueue($channel);
$queue->setName('hello');
$exchange = new \AMQPExchange($channel);
$result = $exchange->publish('hello world!','hello');
var_dump($result);
建立recevie.php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('hello', false, false, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function($msg) {
echo " [x] Received ", $msg->body, "\n";
};
$channel->basic_consume('hello', '', false, true, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}