怎麼實現對200w用戶的即時推送,這個推送可以理解為調用第三方的接口,push,sms之類的東西。
當時先寫了一個demo 直接讀取DB然後單個推送,結果。。可想而知
於是設計一套基於redis+php多進程的方案,用著還不錯而去擴展性蠻高的,故分享之。
=============================================
具體的邏輯如下:(無視我的字體)
其實這裡還可以優化的,我的設想是如果用戶數據再多一些的話,可以在redis裡對數據進行分割采取多List,每一個List對應多個php進程這樣會更快。
下面是我實現的具體代碼:
主管理腳本:應用時啟動這個即可。
代碼如下復制代碼
<?php //push推送配置 注:使用前請確認log文件為空 2016-04-12
include_once(dirname (__FILE__)."/../../config.inc.php");
//if(exec('ps aux | grep redis_push.php | grep -v grep | wc -l') != 0) goto check;
import('push.class.php');
import('Redis.class.php');
$time=time();
$data=array("apikey"=>'xxxx',"secret"=>'xxxx');
$push=newChannel($data);
$redis=newRedisCache($Credis['host'],$Credis['port']);
if(exec('ps aux | grep redis_push.php | grep -v grep | wc -l') != 0)gotocheck;//如果有推送任務 直接執行監控代碼
/*PUSH配置項*/
$config=array(
"file"=>"test.txt",
"Title"=>"sssss",
"Content"=>"ssssssssssssssss",
"OpenType"=>"0", //1是 0否 是否跳轉鏈接
"Url"=>"", //鏈接地址
"num"=>"500", //每次推送條數
"s"=>"1" //睡眠時間 (單位:秒)
);
$num= 15; //啟動進程數量
$a=$config['OpenType']==1 ?"是":"否";
$c= json_encode($config);
$info= <<<monkey
************ 請確認信息是否有誤*10秒後啟動push任務! *************
* 文件名稱 : {$config['file']};
* 推送標題 : {$config['Title']};
* 推送內容 : {$config['Content']};
* 是否跳轉 : {$config['OpenType']};
* 進程數量 :$num;(如果為單進程無視此項)
* 睡眠時間 : {$config['s']};
* 日志目錄 : /log;
***************************************************************\n
monkey;
echo$info;
sleep(3);
$n= 1;
while($n<=10){
echo(10-$n++),"秒\n";
sleep(1);
}
echo"------------------------- 任務已啟動 -------------------------\n";
if($redis->Scount('push_getchannel_success')){
echo"隊列有未完成任務\n";
}else{
$res=exec("php redis_getchannel.php {$config['file']}");//寫入redis腳本
echo$res;
}
smtp_mail('[email protected]','推送任務已開啟','請實時監測,5秒後您的手機將接收到測試推送!');//推送監控 實現定時全自動推送
echo"\n---------------- 5秒後 test 將收到測試推送消息 ----------------\n";
sleep(5);
$re=$push->BaiduPush('xxxx','xxxxx',$config['Content'],$config['Title'],Ƈ',$config['OpenType'],$config['Url'],'xxxxx',$push);
sleep(1);
echo"\n---------------- 測試推送已發出!如未收到,請及時終止程序! 10秒後正式推送!!! ----------------\n";
$m= 1;
while($m<=10){
echo(10-$m++),"秒\n";
sleep(1);
}
echo"\n---------------- 推送任務已經開始!請耐心等待! ----------------\n";
//下面設置是否多進程
for($i=1;$i<=$num;$i++){
exec("php redis_push.php '{$c}' > /dev/null 2>&1 &");
}
check:
while(1){
if(exec('ps aux | grep redis_push.php | grep -v grep | wc -l') == 0){
echo"push 發送完成 用時",time()-$time,"秒";
die();
}
echo"當前進程數:",exec('ps aux | grep redis_push.php | grep -v grep | wc -l'),"個","\n";
echo"當前剩余推送數量:".$redis->Scount('push_getchannel_success')."\n";
sleep(10);
}
至於寫入redis和具體的推送腳本這個靠自己的想象裡就好了 我就不發了 嘿嘿
我的做法是具體的推送腳本在推送一定數量後會自動終止並調用自己本身。
因為在實際應用中發現php腳本在長時間運行之後會發生假死(可能是因為上下文切換的問題),所以我都是避免讓php腳本長時間運行。
還有就是用戶肯定不是固定的200w用戶 每天都會有一個增量,我的方案是通過定時腳本每天把增量的用戶整理進我自己設計的一個用戶表自己管理。
ps:我把所有的腳本弄到了一個我自己整理的小的php原生框架統一管理,過段時間我發出來。