PHP多線程模擬實(shí)現(xiàn)秒殺搶單
應(yīng)集團(tuán)要求給服務(wù)號(hào)做了個(gè)搶單秒殺的功能,需要對(duì)秒殺做個(gè)測(cè)試,想試試PHP多線程,就模擬了下?lián)寙喂δ堋?/p>
先說秒殺模塊的思路:
正常情況下的用戶秒殺操作
1、發(fā)起秒殺請(qǐng)求
2、進(jìn)入秒殺隊(duì)列
3、隨機(jī)滯后 1 - 2 秒進(jìn)行秒殺結(jié)果查詢請(qǐng)求(算是變相分流吧)
4、成功則生成訂單
5、返回結(jié)果
以下是模擬秒殺的代碼:
<?php
set_time_limit(0);
/**
* 線程的執(zhí)行任務(wù)
*/
class Threadrun extends Thread
{
public $url;
public $data;
public $params;
public function __construct($url, $params=[])
{
$this->url = $url;
$this->params = $params;
}
public function run()
{
if(($url = $this->url))
{
$params = [
'goods_id' => 1,
'activity_id' => 1,
'user_id' => isset($this->params['user_id']) ? $this->params['user_id'] : $this->getCurrentThreadId(),
];
$startTime = microtime(true);
$this->data = [
'id' => $params['user_id'],
'result' => model_http_curl_get( $url, $params ),
'time' => microtime(true)-$startTime,
'now' => microtime(true),
];
}
}
}
/**
* 執(zhí)行多線程
*/
function model_thread_result_get($urls_array)
{
foreach ($urls_array as $key => $value)
{
$threadPool[$key] = new Threadrun($value["url"],['user_id'=>$value['user_id']]);
$threadPool[$key]->start();
}
foreach ($threadPool as $thread_key => $thread_value)
{
while($threadPool[$thread_key]->isRunning())
{
usleep(10);
}
if($threadPool[$thread_key]->join())
{
$variable_data[$thread_key] = $threadPool[$thread_key]->data;
}
}
return $variable_data;
}
/**
* 發(fā)送 HTTP 請(qǐng)求
*/
function model_http_curl_get($url,$data=[],$userAgent="")
{
$userAgent = $userAgent ? $userAgent : 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, 5);
curl_setopt($curl, CURLOPT_USERAGENT, $userAgent);
curl_setopt($curl, CURLOPT_POST, true);
if( !empty($data) ) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
/**
* 友好的打印變量
* @param $val
*/
function dump( $val )
{
echo '<pre>';
var_dump($val);
echo '</pre>';
}
/**
* 寫日志
* @param $msg
* @param string $logPath
*/
function writeLog( $msg, $logPath='' ) {
if( empty($logPath) ) {
$logPath = date('Y_m_d').'.log';
}
if( !file_exists($logPath) ) {
$fp = fopen( $logPath,'w' );
fclose( $fp );
}
error_log( $msg.PHP_EOL, 3, $logPath);
}
/**
* 生成日志信息
* @param $result
* @param $timeDiff
* @return bool|string
*/
function createLog( $result, $timeDiff ){
if( empty($result) || !is_array($result) ) {
return false;
}
$succeed = 0;
$fail = 0;
foreach( $result as $v ) {
$times[] = $v['time'];
$v['result'] === false ? $fail++ : $succeed++;
}
$totalTime = array_sum( $times );
$maxTime = max( $times );
$minTime = min( $times );
$sum = count( $times );
$avgTime = $totalTime/$sum;
$segment = str_repeat('=',100);
$flag = $segment . PHP_EOL;
$flag .= '總共執(zhí)行時(shí)間:' . $timeDiff . PHP_EOL ;
$flag .= '最大執(zhí)行時(shí)間:' . $maxTime . PHP_EOL;
$flag .= '最小執(zhí)行時(shí)間:' . $minTime . PHP_EOL;
$flag .= '平均請(qǐng)求時(shí)間:' . $avgTime . PHP_EOL;
$flag .= '請(qǐng)求數(shù):' . $sum . PHP_EOL;
$flag .= '請(qǐng)求成功數(shù):' . $succeed . PHP_EOL;
$flag .= '請(qǐng)求失敗數(shù):' . $fail . PHP_EOL;
$flag .= $segment . PHP_EOL;
return $flag;
}
/**
* 發(fā)起秒殺請(qǐng)求
*/
function insertList( $urls, $logPath='' )
{
$t = microtime(true);
$result = model_thread_result_get($urls);
$e = microtime(true);
$timeDiff = $e-$t;
echo "總執(zhí)行時(shí)間:" . $timeDiff . PHP_EOL;
foreach( $result as $v ) {
$msg = '用戶【' . $v['id'] . '】秒殺商品, 返回結(jié)果 ' . $v['result'] . ' 用時(shí)【' . $v['time'] . ' 秒】 當(dāng)前時(shí)間【'.$v['now'].'】';
writeLog( $msg,$logPath );
}
$logStr = createLog( $result, $timeDiff);
writeLog( $logStr, $logPath );
return $result;
}
//發(fā)起秒殺請(qǐng)求
for ($i=0; $i < 1000; $i++)
{
$urls_array[] = array("name" => "baidu", "url" => "http://***.***.com/seckill/shopping/listinsert");
}
$list = insertList( $urls_array, './inset.log' );
//發(fā)起秒殺結(jié)果查詢請(qǐng)求
$urls_array = [];
foreach( $list as $v ) {
if( $v['result'] === false ) {
continue;
}
$urls_array[] = array(
"name" => "baidu",
"url" => "http://***.***.com/seckill/shopping/query",
'user_id' => $v['id'],
);
}
insertList( $urls_array, './query.log' );
測(cè)試代碼機(jī)器性能(開發(fā)機(jī)):

訂單代碼機(jī)器性能(測(cè)試機(jī)):

系統(tǒng)測(cè)試結(jié)果:
模擬 1000 并發(fā)的情況,單機(jī)每秒 300+ 訂單,服務(wù)器毫無(wú)壓力。
反倒是測(cè)試機(jī)受不了了,CPU 飆升 100%。 Apache 偶爾崩潰。
不知道是 PHP 多線程和 Windows 環(huán)境的支持不好,還是 PHP 多線程本身的問題,區(qū)區(qū) 1000 線程跑不動(dòng)。多線程的地方還是比較需要 Python 和 C 出馬。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Yii框架引用插件和ckeditor中body與P標(biāo)簽去除的方法
這篇文章主要介紹了Yii框架引用插件和ckeditor中body與P標(biāo)簽去除的方法,結(jié)合實(shí)例形式分析了Yii框架中引入插件的步驟、實(shí)現(xiàn)方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-01-01
PHP 7安裝使用體驗(yàn)之性能大提升,兼容性強(qiáng),擴(kuò)展支持不夠(升級(jí)PHP要謹(jǐn)慎)
這篇文章主要介紹了PHP 7安裝使用體驗(yàn)之性能大提升,兼容性強(qiáng),擴(kuò)展支持不夠(升級(jí)PHP要謹(jǐn)慎),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-07-07
PHP用Session實(shí)現(xiàn)用戶登陸功能
Session 是存儲(chǔ)在服務(wù)器端的會(huì)話,相對(duì)安全,并且不像 Cookie 那樣有存儲(chǔ)長(zhǎng)度限制。由于 Session 是以文本文件形式存儲(chǔ)在服務(wù)器端的,所以不怕遠(yuǎn)程用戶修改 Session 內(nèi)容。這篇文章主要介紹了PHP用Session實(shí)現(xiàn)用戶登陸功能,需要的朋友可以參考下2022-12-12
php控制linux服務(wù)器常用功能 關(guān)機(jī) 重啟 開新站點(diǎn)等
服務(wù)器主機(jī)是CentOS 的,每次重啟、關(guān)機(jī)都得通過客戶端連接,想問下能否通過PHP頁(yè)面實(shí)現(xiàn)對(duì)系統(tǒng)的 關(guān)機(jī)、重啟操作2012-09-09

