在上次學xattr的時候用它簡單實現一箇中間件,我去了解了一下rabbitmq這個消息中間件,感覺理論上還是挺好用的,給一般併發量的系統用足夠了。
首先安裝這個服務。
sudo apt search rabbitmq
發現了這個
rabbitmq-server/focal-updates,focal-updates,focal-security,focal-security,now 3.8.2-0ubuntu1.3 all AMQP server written in Erlang
好,然後安裝它
sudo apt-get install rabbitmq-server
等他跑完就行了,要用PHP調用的話需要這個php組件
composer require php-amqplib/php-amqplib
跑完後創建兩個文件 receive.php 和 send.php
receive.php
<?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 message. ctrl+c to stop', "\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(); } $channel->close(); $connection->close();
send.php
<?php require_once __DIR__ . '/vendor/autoload.php'; use PhpAmqpLib\Connection\AMQPStreamConnection; use PhpAmqpLib\Message\AMQPMessage; $start_time = microtime(true); $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('hello', false, false, false, false); $msg = new AMQPMessage('Hello world!'); $channel->basic_publish($msg, '', 'hello'); // echo " [x] Send 'Hello world'\n"; $channel->close(); $connection->close(); $end_time = microtime(true); echo "use time " . ($end_time - $start_time) * 1000 . " ms \n";
先運行 php receive.php 它就一直在監聽消息
[*] waiting for message. ctrl+c to stop
再運行 php send.php 進行投遞消息
就可以看到效果了。
[*] waiting for message. ctrl+c to stop [x] Received Hello world!
關於這個例子 這篇文章介紹了很詳細 https://www.rabbitmq.com/tutorials/tutorial-one-php.html
git倉庫在這裏 https://github.com/php-amqplib/php-amqplib
除了這個composer之外 還可以安裝PHP的擴展
sudo apt-get install php7.4-amqp
安裝後用 php -m |grep amqp 來驗證一下擴展是不是真的安裝上了。
如果我不運行receive.php 直接運行send.php會怎麼樣呢?
我發現它會把消息堆積放在中間介質裏面(當然這個應該是rabbitmq server服務裏面的一部分)
只運行 send.php 不運行receive.php 發現有消息堆積 我們可以用 sudo rabbitmqctl list_queues 來查看列表
sudo rabbitmqctl list_queues Timeout: 60.0 seconds ... Listing queues for vhost / ... name messages hello 3
我執行了send.php3次留下來3條消息,在倉庫列表裏面等待處理。
此時再運行receive.php 然後進來就有消息輸出 再看看列表,發現沒有消息堆積了
sudo rabbitmqctl list_queues Timeout: 60.0 seconds ... Listing queues for vhost / ... name messages hello 0
這說明生產者是可以隨時生產投遞到中間存儲的,消費者啓動的時候會自己去消費堆積任務。
如果生產者消費者都在運行,那麼生產者投遞的消息馬上就被消費者處理了。
當你運行兩個receive.php的話
然後讓 send.php 執行10個消息投遞
for ($i=0; $i < 10; $i++) { $msg = new AMQPMessage('Hello world!'.$i); $channel->basic_publish($msg, '', 'hello'); }
1個消費者打印
[*] waiting for message. ctrl+c to stop [x] Received Hello world!0 [x] Received Hello world!2 [x] Received Hello world!4 [x] Received Hello world!6 [x] Received Hello world!8
另1個消費者打印
[*] waiting for message. ctrl+c to stop [x] Received Hello world!1 [x] Received Hello world!3 [x] Received Hello world!5 [x] Received Hello world!7 [x] Received Hello world!9
看來它倆分配了消息
然後對於這個效率的話,我做了一個測試
投遞 1 萬條消息耗費 228 ms 合約 43859 條/秒
投遞 10 萬條消息耗費 1512 ms 合約 66137 條/秒
投遞 100 萬條消息耗費 18785 ms 合約 54990 條/秒
for ($i=0; $i < 10000; $i++) { $msg = new AMQPMessage('Hello world!'.$i); $channel->basic_publish($msg, '', 'hello'); } for ($i=0; $i < 100000; $i++) { $msg = new AMQPMessage('Hello world!'.$i); $channel->basic_publish($msg, '', 'hello'); } for ($i=0; $i < 1000000; $i++) { $msg = new AMQPMessage('Hello world!'.$i); $channel->basic_publish($msg, '', 'hello'); }
可以看出這個投遞極限也就是單個進程每秒5-6萬條左右,總體而言效率還算不錯,已經能滿足很多系統了,畢竟業務上也沒有那麼多消息要傳遞,如果非要更快,那就需要考慮用更快的xattr或者共享內存之類的了。