redis可以做消息隊列嗎

redis可以做消息隊列嗎

應用場景:

例如秒殺。瞬時大量寫入訂單到數(shù)據(jù)庫,導致數(shù)據(jù)庫無法及時響應。此時可以采用Redis做消息隊列,把所有需要寫入的數(shù)據(jù)先寫入Redis消息隊列中,然后同時在服務器開啟php-cli進程循環(huán)讀取隊列中的數(shù)據(jù),異步寫入數(shù)據(jù)庫。使用redis做消息隊列可能會出現(xiàn)消息丟失的情況,因為沒有消息接收的確認機制。大型程序,應該使用類似RabitMQ來做專業(yè)消息隊列。

1、使用publish/subscribe方式作為消息隊列

特點:一個消息發(fā)布者(生產(chǎn)者),可以對應多個消息訂閱者(消費者)。當消息發(fā)布到消息隊列的時候,所有消息訂閱者都可以收到消息。適用于分布式消息分發(fā)。client以阻塞的方式等待publish端的消息。多個消費者不能加快消息消費速度。

消息生產(chǎn):

$params?=json_encode(['x_uid'?=>?$x_uid,?'phone'?=>?$phone]); $redis->publish('test',$params);?//test表示發(fā)布的頻道名字

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

$redis?=?new?Redis();?$redis->pconnect('127.0.0.1');?//必須用pconnect長連接 //設(shè)置redis連接永遠不超時。默認60s超時斷開連接?$redis->setOption(Redis::OPT_READ_TIMEOUT,?-1); ? $redis->subscribe(array('test'),?'callback');?//test表示頻道名字,callback?回調(diào)函數(shù)名 functioncallback($redis,?$chan,?$msg){?//對收到的消息進行處理函數(shù) $params?=?json_decode($msg,true); .... }

pconnect和connect區(qū)別:

connect:腳本結(jié)束之后連接就釋放了。

pconnect:腳本結(jié)束之后連接不釋放,連接保持在php-fpm進程中。

所以使用pconnect代替connect,可以減少頻繁建立redis連接的消耗。

2、使用list作為redis消息隊列

特點:一個消息生產(chǎn)者,對應一個消息消費者。多個消費者可以加快消息消費速度。

消息生產(chǎn):

$redis?=newRedis(); $redis->connect('127.0.0.1');? //將需要寫入數(shù)據(jù)庫的數(shù)據(jù)全部push到隊列(復雜數(shù)據(jù)可以先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消費數(shù)據(jù)如果沒有成功寫入數(shù)據(jù)庫,會導致數(shù)據(jù)丟失。強烈要求生產(chǎn)數(shù)據(jù)時,二次備份到redis或文件中。

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

注意:MySQL不主動關(guān)閉連接的情況下,一次連接最長八小時后自動斷開。

<?php //鏈接數(shù)據(jù)庫 $conn = mysqli_connect("localhost","root","root"); if(!$conn){ die("連接數(shù)據(jù)庫失敗:". mysqli_error()); } mysqli_select_db($conn,"api"); //字符轉(zhuǎn)換,讀庫 mysqli_query($conn,"set character set &#39;utf8&#39;"); //寫庫 mysqli_query($conn,"set names &#39;utf8&#39;");   //連接本地的 Redis 服務 $redis =newRedis(); $redis->connect('127.0.0.1',6379); //設(shè)置redis連接永遠不超時。默認60s超時斷開連接 $redis-&gt;setOption(Redis::OPT_READ_TIMEOUT,-1); echo?'Listening...'; $i?=1; while(true){ $data?=?$redis-&gt;brpop('winer',0);?//?0表示沒有接收到參數(shù)的情況下,永遠不超時斷開 $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']; //將數(shù)組寫入數(shù)據(jù)庫、訂單 $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++; } ?&gt;

其他:

秒殺場景防止商品超賣:

1、數(shù)據(jù)庫中設(shè)置商品數(shù)量為無符號型,即不允許負數(shù)。當更新商品數(shù)量到負數(shù)時,返回false。

2、商品數(shù)量存在Redis的list隊列中,每次搶購就pop刪除一個元素出隊列。

//存放商品數(shù)量的隊列 for($j?=1;?$j?<p>判斷商品數(shù)量邏輯</p><pre class="brush:php;toolbar:false">$count=Redis::lpop('gooods_count'); //$count?=?Redis::llen('gooods_count');?//llen判斷隊列長度 if(!$count){ return'已經(jīng)搶光了哦'; }

更多Redis相關(guān)知識,請訪問Redis使用教程欄目!

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊12 分享