本文實例講述了PHP消息隊列實現及應用。分享給大家供大家參考,具體如下:
在互聯網項目開發者經常會遇到『給用戶群發短信』、『訂單系統有大量的日志需要記錄』或者在秒殺業務的時候服務器無法承受瞬間并發的壓力。
這種情況下,我們怎么保證系統正常有效的運行呢?
這個時候,我們可以引入一個叫『消息隊列』的概念來解決上面的需求。
消息隊列的概念、原理和場景
在高并發的時候,程序往往無法做到及時的處理。我們引入一個中間的系統,來進行分流和減壓。
所以從本質上講:消息隊列就是一個隊列結構的中間件。也就是說,你把消息和內容放入這個容器之后就可以直接返回,不用等它后期處理的結果。另外會有一個程序,讀取這些數據并按照順序處理。
1、隊列結構的中間件
2、消息放入后,不必立即處理
3、由訂閱者/消費者按順序處理
也就是說:當遇到一個比較大或者耗時比較長的環節的時候,而同時你的業務又不需要立即知道這個環節的結果,使用消息隊列是好的選擇。
核心結構如下面:

消息隊列 適用場景
一、數據需要冗余的時候
比如訂單系統中,后續需要進行數據的轉換和記錄。消息隊列可以把這些數據持久化的存儲在隊列中,然后由訂單后期處理程序進行處理,處理完成之后再把這條記錄從隊列中刪除。
二、系統的解耦
消息隊列解決了2套系統之間深度耦合的問題。
使用消息隊列后,入隊的系統和出隊的系統沒有直接的關系。
入隊系統和出隊系統,其中一個崩潰之后不會影響另外一個的正常運行。
三、流量削峰
就是秒殺和搶購的時候,會出現明顯的流量劇增,對服務器的壓力非常大。
實際項目開發中,配合緩存來使用消息隊列,一種很好的方案。
四、異步通信
消息隊列本身就實現了程序的異步操作,因此只要適合于異步的場景都可以使用消息隊列
五、擴展性
比如訂單系統,訂單入隊之后,后期或許還有財務系統處理,但是如果還要加一個配貨系統。
只需要讓這個配貨系統 訂閱這個 消息隊列 即可。
六、排序保證
在有些場景下,數據的處理順序是非常重要的,隊列本身就可以做成單線程的單進單出的系統。
從而有效的保證數據按照順序進行處理。
常見 隊列實現 的優缺點
隊列介質:
Mysql:可靠性高、易實現、速度慢
Redis:速度快,單條大消息包時效率低
消息系統:專業性強、可靠,學習成本高(比如:RabbtiMQ)
消息處理的觸發機制:
死循環方式讀?。阂讓崿F,故障時無法及時恢復;
定時任務:壓力均分,有處理量上限。(最大的缺陷:定位任務時間的間隔和處理的數據需要精準把握,不能上一個任務還沒有處理完成,下一個認為就已經啟動了)
守護進程:類似于PHP-FPM和PHP-CGI,需要shell知識
解耦案列:隊列處理 訂單系統和配送系統
我們在前面了解過消息隊列的使用場景
這里,我們要來處理其中一個場景:系統的解耦。
在電商項目中,當客戶提交了一個訂單之后,客戶在個人中心可以看到訂單處于配送中。
這個時候就要參與進來一個系統,叫做『配送系統』。如果我們在做架構的時候,把訂單系統和配送系統設計在一起的話就會出現一些問題:訂單系統的壓力比較大,但是配送系統沒有必要對這些壓力做及時的反應;我們不需要訂單系統出現故障之后導致配送系統故障。
所以我們需要把這2個系統分開,通過一個中間的隊列表來實現這2個系統的溝通。
如下圖架構:

具體到我們的程序代碼大致邏輯如下圖:

大致流程:order.php來接收用戶訂單,生成訂單號并對訂單進行處理(訂單系統);在訂單系統會把配送系統所需要的數據放入隊列表中;我們的配送系統goods.php會有個定時腳本每分鐘執行一次,處理隊列表中的數據。
簡單設計隊列表order_queue:
CREATE TABLE `order_queue` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`order_id` int(11) unsigned NOT NULL COMMENT '訂單ID(從訂單系統來的)',
`user_info` varchar(255) NOT NULL DEFAULT '' COMMENT '可以是用戶手機號/用戶id等(這里只是演示)',
`created_at` datetime NOT NULL COMMENT '訂單創建時間',
`updated_at` datetime NOT NULL COMMENT '本記錄最后處理完成時間',
`status` tinyint(2) NOT NULL COMMENT '0未處理,1已處理,2處理中',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
mysql訂單隊列
前面我們已經分析清楚了邏輯,剩下的就是代碼實現了。
注意:我這里只是演示代碼,單純為了展示實現過程。
1、接收訂單,處理訂單order.php
?php
// 這個文件是用來接收用戶的訂單信息 并寫入隊列的一個文件
if(!empty($_GET['user_info'])){
// 驗證 過濾 接收的數據
// todo...
// 這里是應該首先是訂單中心的處理流程
// 因為訂單系統是一套單獨的系統 這里就不編寫這個系統了
// todo...
$order_id = rand(100000,99999); // 正常的訂單號從 訂單系統來,我們這里只是演示
// 把配送系統需要的訂單數據存入隊列表中
$insert_data = array(
'order_id'=>$order_id,
'user_info'=>$_GET['user_info'],
'created_at'=>date('Y-m-d H:i:s',time()),
'status'=>0
);
// 把上面的數據 插入到order_queue表中
// insert into order_queue
}
2、配送系統goods.php
?php
// 這個文件主要是配送系統處理隊列表中的訂單并進行標記的文件
//分析:
//第一步:先把要處理的記錄更新為『等待處理』
//第二步:選擇剛剛標記為『等待處理』的記錄,然后進行配送系統的處理
//第三步:把上面前面處理過的程序標記『已完成』
/////////////////////這里很重要,你一定要明白哦//////////////////////////////////////////////
//疑問:為什么不直接處理最后更新為『已完成』,多了先標記為『等待處理』?
//這是因為配送系統很可能不是及時完成的,它中間會有一段處理的時間,如果還在處理中有其他程序來進行讀取和操作,就沖突了。
//這樣設計其實也是一個鎖的機制
//1、
$waiting = array('status'=>0);
$lock = array('status'=>2);
//把狀態為0的記錄標記為2,每次更新3條(具體每次幾條看情況)
$sql = "update order_queue set status=2 where status=0 limit 3";
//2、
if(上面update成功){
// 選擇出要處理訂單內容
// select * from order_queue where status = 2;
// 然后由配貨系統進行處理
// todo...
//3、處理完成把訂單狀態更新為已完成
$success = array(
'status'=>1,
'updated_at'=>date('Y-m-d H:i:s',time())
);
}else{
echo 'All Finished';
}
3、linux服務器 定時任務
寫個shell腳本:goods.sh
#!/bin/bash
date "+%G-%m-%d %H:%M:%S"
cd /var/www/
php goods.php
這個腳本就是去執行orders.php這個程序的。
在linux服務器部署定時任務:
crontab -e
*/1 * * * * /var/www/goods.sh >> /var/www/goods_shell.log 2>$1
每分鐘執行一次goods.sh文件,并記錄日志到goods_shell.log文件(在對應目錄新建該文件)
更多關于PHP相關內容感興趣的讀者可查看本站專題:《PHP數據結構與算法教程》、《php程序設計算法總結》、《php字符串(string)用法總結》、《PHP數組(Array)操作技巧大全》、《PHP常用遍歷算法與技巧總結》及《PHP數學運算技巧總結》
希望本文所述對大家PHP程序設計有所幫助。
您可能感興趣的文章:- PHP使用ActiveMQ實例
- PHP使用ActiveMQ實現消息隊列的方法詳解
- php ActiveMQ的安裝與使用方法圖文教程
- PHP Beanstalkd消息隊列的安裝與使用方法實例詳解
- PHP高級編程之消息隊列原理與實現方法詳解
- PHP PDO和消息隊列的個人理解與應用實例分析
- PHP+RabbitMQ實現消息隊列的完整代碼
- PHP多進程通信-消息隊列使用
- php基于Redis消息隊列實現的消息推送的方法
- 使用PHP訪問RabbitMQ消息隊列的方法示例
- php實現通過stomp協議連接ActiveMQ操作示例