RabbitMQ 工作模式介紹

RabbitMQ 工作模式介紹

1.Hello World

RabbitMQ 是一個消息代理:它接受並轉發消息。您可以將其視爲郵局:當您將要郵寄的郵件放入郵箱時,您可以確定信使最終會將郵件交付給您的收件人。在這個類比中,RabbitMQ是一個郵政信箱,一個郵局和一個信件載體。

RabbitMQ 和郵局之間的主要區別在於它不處理紙張,而是接受、存儲和轉發二進制數據 - 消息

以下是RabbitMq的專業術語

  • 消費與接受的含義相似。使用者是一個主要等待接收消息的程序

  • 生產只意味着發送。發送消息的程序是生產者

  • 隊列是 RabbitMQ 中郵箱的名稱。儘管消息流經 RabbitMQ 和您的應用程序,但它們只能存儲在隊列中。隊列僅受主機內存和磁盤限制的約束,它本質上是一個大型消息緩衝區。許多生產者可以發送到一個隊列的消息,許多使用者可以嘗試從一個隊列接收數據。這就是我們表示隊列的方式

請注意,生產者、使用者和代理不必駐留在同一個主機上;事實上,在大多數應用程序中,它們不會。應用程序也可以是生產者和使用者

“hello world”
(使用 php-amqplib 客戶端)
在本教程的這一部分中,我們將用 PHP 編寫兩個使用 RabbitMQ 進行通信的程序。本教程使用需要 PHP 7.x 或 8.x 的客戶端庫。

第一個程序將是發送單個消息的生產者,第二個程序將是接收消息並將其打印出來的消費者。我們將掩蓋php-amqplib API中的一些細節,專注於這個非常簡單的事情,只是爲了開始。這是一個消息傳遞的“Hello World”。

在下圖中,“P”是我們的生產者,“C”是我們的消費者。中間的框是一個隊列 - RabbitMQ 代表消費者保留的消息緩衝區。

Sending

<?php
include "../../vendor/autoload.php";
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// 創建服務器連接
$connection = new AMQPStreamConnection(
    '127.0.0.1',
    5672,
    'guest',
    'guest',
    '/');
// 創建一個通道,這是大多數用於完成工作的 API 所在的位置
$channel = $connection->channel();
// 要發送,我們必須聲明一個隊列供我們發送到;然後我們可以向隊列發佈一條消息:
$channel->queue_declare('hello', false, false, false, false);
// 創建消息
$msg = new AMQPMessage('Hello World!');
// 發送消息
$channel->basic_publish($msg, '', 'hello');
echo " [x] Sent 'Hello World!'\n";
$channel->close();
$connection->close();

Recive

我們的接收器偵聽來自 RabbitMQ 的消息,因此與發佈單個消息的發佈者不同,我們將保持接收器運行以偵聽消息並打印出來

<?php
include "../../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";

// 我們的代碼將阻塞,而我們的$channel有回調。
// 每當我們收到消息時,我們的$callback函數都會傳遞收到的消息。
$callback = function ($msg) {
    echo ' [x] Received ', $msg->body, "\n";
};

$channel->basic_consume('hello', '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

2. Work Queue

在第一個教程中(hello world),我們編寫了程序來發送和接收來自命名隊列的消息。在此部分中,我們將創建一個工作隊列,用於在多個工作線程之間分配耗時的任務

工作隊列(又名:任務隊列)背後的主要思想是避免立即執行資源密集型任務並必須等待其完成。相反,我們將任務安排在以後完成。我們將任務封裝爲消息並將其發送到隊列。在後臺運行的工作進程將彈出任務並最終執行作業。當您運行許多工作線程時,任務將在它們之間共享

此概念在 Web 應用程序中特別有用,在這些應用程序中,在短 HTTP 請求窗口期間無法處理複雜任務。

task.php (P)

<?php
// 工作隊列 work queue 發送 send
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

include "../../vendor/autoload.php";

// 創建服務器連接
$connection = new AMQPStreamConnection(
    '127.0.0.1',
    5672,
    'guest',
    'guest',
    '/');
// 創建一個通道,這是大多數用於完成工作的 API 所在的位置
$channel = $connection->channel();
// 要發送,我們必須聲明一個隊列供我們發送到;然後我們可以向隊列發佈一條消息:
$channel->queue_declare('task_queue', false, false, false, false);
// 創建消息
$data = implode(' ', array_slice($argv, 1));
if (empty($data)) {
    $data = "hello world!";
}
// [
//    'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
//]
$msg = new AMQPMessage($data);
// 發送消息
$channel->basic_publish($msg, '', 'task_queue');
echo " [x] Sent '".$data ."'\n";
$channel->close();
$connection->close();

worker.php (C)

<?php
include "../../vendor/autoload.php";
use PhpAmqpLib\Connection\AMQPStreamConnection;
// 設置與發佈者相同;我們打開一個連接和一個通道,並聲明我們要從中使用的隊列。
// 請注意,這與將發佈發送到的隊列匹配
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->queue_declare(
    'task_queue',
    false,
    false,
    false,
    false
);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] Received ', $msg->body, "\n";
    sleep(substr_count($msg->body, '.'));
    echo " [x] Done\n";
    // 手動ack
    // $msg->ack();
};
// 公平調度
$channel->basic_qos(null, 1, null);
// 手動ack
// $channel->basic_consume('task_queue', '', false, false, false, false, $callback);
$channel->basic_consume('task_queue', '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

執行結果

# shell1
php task.php First message.
php task.php Second message..
php task.php Third message...
php task.php Fourth message....
php task.php Fifth message.....

# shell2
php worker.php

# => [*] Waiting for messages. To exit press CTRL+C
# => [x] Received 'First message.'
# => [x] Received 'Third message...'
# => [x] Received 'Fifth message.....'

# shell3
php worker.php

# => [*] Waiting for messages. To exit press CTRL+C
# => [x] Received 'Second message..'
# => [x] Received 'Fourth message....'

默認情況下,RabbitMQ 將按順序將每條消息發送給下一個消費者。平均而言,每個消費者都會收到相同數量的消息。這種分發消息的方式稱爲輪詢。對三個或更多工作人員進行嘗試。

消息確認(Confirm)

消息確認以前由我們自己關閉。是時候通過將第四個參數設置爲 basic_consume false 來打開它們了(true 表示沒有確認),並在我們完成任務後從工作人員發送適當的確認

$callback = function ($msg) {
    echo ' [x] Received ', $msg->body, "\n";
    $msg->ack();
};


$channel->basic_consume('task_queue', '', false, false, false, false, $callback);

使用此代碼,可以確保即使在處理消息時使用 CTRL+C 終止工作線程,也不會丟失任何內容。工作線程終止後不久,將重新傳遞所有未確認的消息

消息持久性(Message Durability)

我們已經學會了如何確保即使消費者死亡,任務也不會丟失。但是如果 RabbitMQ 服務器停止,我們的任務仍然會丟失。

當 RabbitMQ 退出或崩潰時,它會忘記隊列和消息,除非您告訴它不要這樣做。要確保消息不會丟失,需要做兩件事:我們需要將隊列和消息都標記爲持久

首先,我們需要確保隊列在 RabbitMQ 節點重新啓動後能夠倖存下來

$channel->queue_declare('task_queue', false, true, false, false);

此標誌設置爲 true 需要同時應用於生產者代碼和使用者代碼

現在我們需要將我們的消息標記爲持久 - 通過設置 delivery_mode = 2 消息屬性,AMQPMessage 將其作爲屬性數組的一部分

$msg = new AMQPMessage( $data, array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT));

公平調度(Fair dispatch)

您可能已經注意到,調度仍然不能完全按照我們想要的方式工作。例如,在有兩個工作線程的情況下,當所有奇數消息都很重而偶數消息都很輕時,一個工作人員將一直很忙,而另一個工作人員幾乎不會做任何工作。好吧,RabbitMQ 對此一無所知,仍然會均勻地調度消息

發生這種情況是因爲 RabbitMQ 只是在消息進入隊列時調度消息。它不會查看使用者的未確認消息數。它只是盲目地將每 n 條消息發送給第 n 個消費者

爲了解決這個問題,我們可以將basic_qos方法與 prefetch_count = 1 設置一起使用。這告訴 RabbitMQ 不要一次向一個 worker 提供多條消息。或者,換句話說,在工作人員處理並確認前一條消息之前,不要向工作人員發送新消息。相反,它會將其調度給下一個尚未繁忙的工作人員

$channel->basic_qos(null, 1, null);

3. Publish/Subscribe

在上一教程中,我們創建了一個工作隊列。工作隊列背後的假設是每個任務只傳遞給一個工作人員。在這一部分中,我們將做一些完全不同的事情 - 我們將向多個消費者傳遞消息。此模式稱爲“發佈/訂閱”

爲了說明這種模式,我們將構建一個簡單的日誌記錄系統。它將由兩個程序組成 - 第一個將發出日誌消息,第二個將接收和打印它們

在我們的日誌記錄系統中,接收器程序的每個運行副本都將獲得消息。這樣,我們將能夠運行一個接收器並將日誌定向到磁盤;同時,我們將能夠運行另一個接收器並在屏幕上查看日誌

本質上,已發佈的日誌消息將廣播到所有接收方

Exchanges

在本教程的前幾部分中,我們向隊列發送消息和從隊列接收消息。現在是時候在 Rabbit 中引入完整的消息傳遞模型了

讓我們快速回顧一下前面教程中介紹的內容:

  • 生產者(Producer)是發送消息的用戶應用程序。
  • 隊列是(Queue)存儲消息的緩衝區。
  • 使用者(Consumer)是接收消息的用戶應用程序

RabbitMQ 中消息傳遞模型的核心思想是,生產者從不將任何消息直接發送到隊列。實際上,很多時候,生產者甚至根本不知道消息是否會傳遞到任何隊列

相反,生產者只能向交易所(exchange)發送消息。交換是一件非常簡單的事情。一方面,它接收來自生產者的消息,另一方面將它們推送到隊列中。交換必須確切地知道如何處理它收到的消息。是否應將其附加到特定隊列?是否應該將其附加到許多隊列中?或者應該丟棄它。其規則由交換類型(exchange type)定義。

交換機類型有 direct, topic, headers and fanout, 下面我們創建一個direct類型的交換機

$channel->exchange_declare('logs', 'fanout', false, false, false);

現在,我們可以發佈到我們命名的交易所(exchange)

$channel->exchange_declare('logs', 'fanout', false, false, false);
$channel->basic_publish($msg, 'logs');

Temporary queues(臨時隊列)

您可能還記得以前我們使用具有特定名稱的隊列(還記得 hello 和 task_queue 嗎?能夠命名隊列對我們來說至關重要 - 我們需要將工人指向同一個隊列。如果要在生產者和使用者之間共享隊列,爲隊列命名非常重要

首先,每當我們連接到Rabbit時,我們都需要一個新的空隊列。爲此,我們可以創建一個具有隨機名稱的隊列,或者更好的是 - 讓服務器爲我們選擇一個隨機隊列名稱

其次,一旦我們斷開了消費者的連接,隊列應該被自動刪除。

在 php-amqplib 客戶端中,當我們以空字符串形式提供隊列名稱時,我們創建一個具有生成名稱的非持久隊列:

list($queue_name, ,) = $channel->queue_declare("");

當該方法返回時,$queue_name 變量包含由 RabbitMQ 生成的隨機隊列名稱。例如,它可能看起來像amq.gen-JzTY20BRgKO-HjmUJj0wLg。

Bindings(綁定)

交換機和隊列之間的關係稱爲綁定

$channel->queue_bind($queue_name, 'logs');

將一切都整合到一起

發出日誌消息的生產者程序看起來與上一教程沒有太大區別。最重要的變化是我們現在希望將消息發佈到我們的日誌交換機

publish.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
// 創建log交換機 類型爲:fanout
$channel->exchange_declare('logs', 'fanout', false, false, false);

$data = implode(' ', array_slice($argv, 1));
if (empty($data)) {
    $data = "info: Hello World!";
}
$msg = new AMQPMessage($data);

// 把消息發佈到日誌交換機
$channel->basic_publish($msg, 'logs');

echo ' [x] Sent ', $data, "\n";

$channel->close();
$connection->close();

subscribe.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// 日誌交換機類型爲:fanout
$channel->exchange_declare('logs', 'fanout', false, false, false);

// 生成臨時隊列
list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

// 隊列綁定交換機
$channel->queue_bind($queue_name, 'logs');

echo " [*] Waiting for logs. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] ', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

$channel->close();
$connection->close();

執行結果

# shell1
php publish.php 記錄日誌信息
# shell2
php subscribe.php 
	
 [*] Waiting for logs. To exit press CTRL+C
 [x] 記錄日誌信息

# shell3
php subscribe.php

 [*] Waiting for logs. To exit press CTRL+C
 [x] 記錄日誌信息

4. Routing

在前面的教程中,我們構建了一個簡單的日誌記錄系統。我們能夠將日誌消息廣播到許多接收器

在本教程中,我們將爲其添加一個功能 - 我們將使僅訂閱消息的子集成爲可能。例如,我們將能夠僅將關鍵錯誤消息定向到日誌文件(以節省磁盤空間),同時仍然能夠在控制檯上打印所有日誌消息

Bindings

綁定可以採用額外的routing_key參數。爲了避免與 $channel::basic_publish 參數混淆,我們將其稱爲綁定鍵。這就是我們如何用鍵創建綁定

$channel->queue_bind($queue_name, 'logs');

$binding_key = 'black';
$channel->queue_bind($queue_name, $exchange_name, $binding_key);

Direct exchange

上一教程中的日誌記錄系統將所有消息廣播給所有使用者。我們希望對其進行擴展,以允許根據消息的嚴重性篩選消息。例如,我們可能希望將日誌消息寫入磁盤的腳本僅接收嚴重錯誤,而不是在警告或信息日誌消息上浪費磁盤空間

fanout 這並沒有給我們帶來太大的靈活性 - 它只能進行無意識的廣播

direct 我們將改用直接交換。直接交換背後的路由算法很簡單 - 消息進入綁定鍵與消息的路由鍵完全匹配的隊列

![](C:\Users\yanwe\Pictures\Saved Pictures\python-four.png)

send.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// 創建連接
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

// 聲明交換機 類型爲:direct
$channel->exchange_declare('direct_logs', 'direct', false, false, false);

// 接受控制檯參數
$severity = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'info';

$data = implode(' ', array_slice($argv, 2));
if (empty($data)) {
    $data = "Hello World!";
}

// 實例化消息
$msg = new AMQPMessage($data);

// 發送消息:exchange、routingkey
$channel->basic_publish($msg, 'direct_logs', $severity);

echo ' [x] Sent ', $severity, ':', $data, "\n";

$channel->close();
$connection->close();

revice.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
// 聲明交換機類型爲:direct
$channel->exchange_declare('direct_logs', 'direct', false, false, false);

// 聲明空隊列
list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);


$severities = array_slice($argv, 1);
if (empty($severities)) {
    file_put_contents('php://stderr', "Usage: $argv[0] [info] [warning] [error]\n");
    exit(1);
}

// 綁定多個routingkey
foreach ($severities as $severity) {
    $channel->queue_bind($queue_name, 'direct_logs', $severity);
}

echo " [*] Waiting for logs. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] ', $msg->delivery_info['routing_key'], ':', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

$channel->close();
$connection->close();

執行結果

# shell1  發送error類型日誌
php send.php error "Run. Run. Or it will explode."

# shell2 監聽  warning error 類型
php receive.php warning error
# 有消息輸出

# shell3 監聽info類型日誌內容
php receive.php info
# 正常情況 無法收到消息
	

5. Topics

在前面的教程中,我們改進了日誌記錄系統。我們沒有使用只能進行虛擬廣播的fanout交換,而是使用了direct直接交換,並獲得了有選擇地接收日誌的可能性。

儘管使用direct直接交換改進了我們的系統,但它仍然存在侷限性 - 它不能基於多個標準進行路由

Topic exchange

發送到主題交換的消息不能具有任意routing_key - 它必須是單詞列表,由點分隔。單詞可以是任何東西,但通常它們指定與消息相關的一些特徵。一些有效的路由密鑰示例:“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”。路由密鑰中可以有任意數量的單詞,最多 255 字節的限制

綁定密鑰也必須採用相同的形式。主題交換背後的邏輯類似於直接交換 - 使用特定路由密鑰發送的消息將被傳遞到與匹配綁定密鑰綁定的所有隊列。但是,綁定鍵有兩種重要的特殊情況

  • * (star) can substitute for exactly one word. (可以替代一個詞)
  • # (hash) can substitute for zero or more words. (可以替代零個或者多個單詞)

![](C:\Users\yanwe\Pictures\Saved Pictures\python-five.png)

在這個例子中,我們將發送所有描述動物的消息。消息將使用由三個單詞(兩個點)組成的路由密鑰發送。路由鍵中的第一個詞將描述速度,第二個詞描述顏色,第三個詞描述物種:<speed>.<colour>.<species>

send.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->exchange_declare('topic_logs', 'topic', false, false, false);

$routing_key = isset($argv[1]) && !empty($argv[1]) ? $argv[1] : 'anonymous.info';
$data = implode(' ', array_slice($argv, 2));
if (empty($data)) {
    $data = "Hello World!";
}

$msg = new AMQPMessage($data);

$channel->basic_publish($msg, 'topic_logs', $routing_key);

revice.php

<?php

require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();

$channel->exchange_declare('topic_logs', 'topic', false, false, false);

list($queue_name, ,) = $channel->queue_declare("", false, false, true, false);

$binding_keys = array_slice($argv, 1);
if (empty($binding_keys)) {
    file_put_contents('php://stderr', "Usage: $argv[0] [binding_key]\n");
    exit(1);
}

foreach ($binding_keys as $binding_key) {
    $channel->queue_bind($queue_name, 'topic_logs', $binding_key);
}

echo " [*] Waiting for logs. To exit press CTRL+C\n";

$callback = function ($msg) {
    echo ' [x] ', $msg->delivery_info['routing_key'], ':', $msg->body, "\n";
};

$channel->basic_consume($queue_name, '', false, true, false, false, $callback);

while ($channel->is_open()) {
    $channel->wait();
}

$channel->close();
$connection->close();

執行結果

# shell1

# shell2
php revice.php "#"
# shell3
php revice.php "kern.*"
# shell4 
php revice.php "*.critical"
# shell5
php revice.php "kern.*" "*.critical"

6. Publisher Confirms

發佈者確認是 RabbitMQ 擴展,以實現可靠的發佈。當在通道上啓用發佈者確認時,代理會異步確認客戶端發佈的消息,這意味着它們已在服務器端處理

概述

在本教程中,我們將使用發佈者確認來確保已發佈的消息已安全到達代理。我們將介紹使用發佈商確認的幾種策略,並解釋它們的優缺點

開啓發布者確認

$channel = $connection->channel();
$channel->confirm_select();

Strategy #1: Publishing Messages Individually(策略#1:單獨發佈消息)

讓我們從使用確認發佈的最簡單方法開始,即發佈消息並同步等待其確認

while (thereAreMessagesToPublish()) {
    $data = "Hello World!";
    $msg = new AMQPMessage($data);
    $channel->basic_publish($msg, 'exchange');
    // uses a 5 second timeout
    $channel->wait_for_pending_acks(5.000);
}

在前面的示例中,我們像往常一樣發佈一條消息,並使用 $channel::wait_for_pending_acks(int|float) 方法等待其確認。確認消息後,該方法將立即返回。如果消息未在超時內確認,或者它是裸的(意味着代理由於某種原因無法處理它),該方法將引發異常。異常的處理通常包括記錄錯誤消息和/或重試發送消息

不同的客戶端庫有不同的方法來同步處理髮布者確認,因此請務必仔細閱讀您正在使用的客戶端的文檔

這種技術非常簡單,但也有一個主要缺點:它大大減慢了發佈速度,因爲消息的確認會阻止所有後續消息的發佈。此方法不會提供每秒超過幾百條已發佈消息的吞吐量。儘管如此,這對於某些應用程序來說已經足夠了

Strategy #2: Publishing Messages in Batches(策略#2:批量發佈消息)

爲了改進前面的示例,我們可以發佈一批消息並等待整個批次得到確認。以下示例使用一批 100

$batch_size = 100;
$outstanding_message_count = 0;
while (thereAreMessagesToPublish()) {
    $data = ...;
    $msg = new AMQPMessage($data);
    $channel->basic_publish($msg, 'exchange');
    $outstanding_message_count++;
    if ($outstanding_message_count === $batch_size) {
        $channel->wait_for_pending_acks(5.000);
        $outstanding_message_count = 0;
    }
}
if ($outstanding_message_count > 0) {
    $channel->wait_for_pending_acks(5.000);
}

等待一批消息被確認比等待單個消息的確認大大提高了吞吐量(使用遠程 RabbitMQ 節點最多 20-30 倍)。一個缺點是,在發生故障時,我們不知道到底出了什麼問題,因此我們可能不得不在內存中保留整個批次以記錄有意義的內容或重新發布消息。並且此解決方案仍然是同步的,因此它會阻止消息的發佈

Strategy #3: Handling Publisher Confirms Asynchronously(策略 #3:處理髮布服務器異步確認)

代理異步確認已發佈的消息,只需在客戶端註冊回調即可收到這些確認的通知

$channel = $connection->channel();
$channel->confirm_select();

$channel->set_ack_handler(
    function (AMQPMessage $message){
        // code when message is confirmed
    }
);

$channel->set_nack_handler(
    function (AMQPMessage $message){
        // code when message is nack-ed
    }
);

有 2 個回調:一個用於已確認的消息,一個用於裸消息(代理可以認爲丟失的消息)。每個回調都有 AMQPMessage $message 參數和返回的消息,因此您無需處理序列號(傳遞標記)即可瞭解此回調屬於哪條消息

send.php

$channel->confirm_select();

$channel->set_ack_handler(function (AMQPMessage $message) {
    file_put_contents("./librabbitmq.log", var_export([
        'type' => 'ack', 'message' => $message->body
    ], true) . PHP_EOL , FILE_APPEND);
});

$channel->set_nack_handler(function (AMQPMessage $message) {
    file_put_contents("./librabbitmq.log", var_export([
        'type' => 'nack', 'message' => $message->body
    ], true) . PHP_EOL , FILE_APPEND);
});

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