【Redis】redis作消息隊列

應用場景:

例如秒殺。瞬時大量寫入訂單到數據庫,致使數據庫沒法及時響應。此時能夠採用Redis作消息隊列,把全部須要寫入的數據先寫入Redis消息隊列中,而後同時在服務器開啓php-cli進程循環讀取隊列中的數據,異步寫入數據庫。使用redis作消息隊列可能會出現消息丟失的狀況,由於沒有消息接收的確認機制。大型程序,應該使用相似RabitMQ來作專業消息隊列。

 

一、使用publish/subscribe方式做爲消息隊列

特色:一個消息發佈者(生產者),能夠對應多個消息訂閱者(消費者)。當消息發佈到消息隊列的時候,全部消息訂閱者均可以收到消息。適用於分佈式消息分發。client以阻塞的方式等待publish端的消息。多個消費者不能加快消息消費速度。
消息生產:
$params =json_encode(['x_uid' => $x_uid, 'phone' => $phone]);
$redis->publish('test',$params); //test表示發佈的頻道名字
消息消費(php-cli模式運行):
$redis = new Redis(); $redis->pconnect('127.0.0.1'); //必須用pconnect長鏈接
//設置redis鏈接永遠不超時。默認60s超時斷開鏈接 $redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
 
$redis->subscribe(array('test'), 'callback'); //test表示頻道名字,callback 回調函數名
functioncallback($redis, $chan, $msg){ //對收到的消息進行處理函數
$params = json_decode($msg,true);
....
}
pconnect和connect區別:
connect:腳本結束以後鏈接就釋放了。
pconnect:腳本結束以後鏈接不釋放,鏈接保持在php-fpm進程中。
因此使用pconnect代替connect,能夠減小頻繁創建redis鏈接的消耗。
 

二、使用list做爲redis消息隊列

特色:一個消息生產者,對應一個消息消費者。多個消費者能夠加快消息消費速度。
消息生產:
$redis =newRedis();
$redis->connect('127.0.0.1'); 
//將須要寫入數據庫的數據所有push到隊列(複雜數據能夠先json編碼成字符串)
$list = json_encode(['x_uid' => $x_uid, 'phone' => $phone, 'goods_id' => $goodsId, 
'add_time' => time(), 'num_field' => $num_field]);
$redis->lpush('winer',$list);
注意:brpop消費數據若是沒有成功寫入數據庫,會致使數據丟失。強烈要求生產數據時,二次備份到redis或文件中。
 

消息消費(php-cli模式運行):

注意:MySQL不主動關閉鏈接的狀況下,一次鏈接最長八小時後自動斷開。
<?php
//連接數據庫
$conn = mysqli_connect("localhost","root","root");
if(!$conn){
die("鏈接數據庫失敗:". mysqli_error());
}
mysqli_select_db($conn,"api");
//字符轉換,讀庫
mysqli_query($conn,"set character set 'utf8'");
//寫庫
mysqli_query($conn,"set names 'utf8'");
 
//鏈接本地的 Redis 服務
$redis =newRedis();
$redis->connect('127.0.0.1',6379);
//設置redis鏈接永遠不超時。默認60s超時斷開鏈接
$redis->setOption(Redis::OPT_READ_TIMEOUT,-1);
echo 'Listening...';
$i =1;
while(true){
$data = $redis->brpop('winer',0); // 0表示沒有接收到參數的狀況下,永遠不超時斷開
$info = json_decode($data[1],true);
$x_uid = $info['x_uid'];
$phone = $info['phone'];
$goods_id = $info['goods_id'];
$add_time = $info['add_time'];
$num_field = $info['num_field'];
//將數組寫入數據庫、訂單
$sql = "insert into hd_hengda11_order (`x_uid`,`phone`,`goods_id`,`add_time`) 
values ($x_uid,$phone,$goods_id,$add_time)"
$re = mysqli_query($conn,$sql);

echo $i.'_ok||';
$i++;
}
?>

 

其餘:
秒殺場景防止商品超賣:
一、數據庫中設置商品數量爲無符號型,即不容許負數。當更新商品數量到負數時,返回false。
二、商品數量存在Redis的list隊列中,每次搶購就pop刪除一個元素出隊列。
//存放商品數量的隊列
for($j =1; $j <=10; $j++){ /設置商品數量爲10
$re =Redis::lpush(gooods_count,1);
}
判斷商品數量邏輯
$count=Redis::lpop('gooods_count');
//$count = Redis::llen('gooods_count'); //llen判斷隊列長度
if(!$count){
return'已經搶光了哦';
}
相關文章
相關標籤/搜索