應(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( !emptyempty($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( emptyempty($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( emptyempty($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'],
- );//Vevb.com
- }
- insertList( $urls_array, './query.log' );
系統(tǒng)測(cè)試結(jié)果:
模擬 1000 并發(fā)的情況,單機(jī)每秒 300+ 訂單,服務(wù)器毫無壓力。
反倒是測(cè)試機(jī)受不了了,CPU 飆升 100%。 Apache 偶爾崩潰。
不知道是 PHP 多線程和 Windows 環(huán)境的支持不好,還是 PHP 多線程本身的問題,區(qū)區(qū) 1000 線程跑不動(dòng)。多線程的地方還是比較需要 Python 和 C 出馬。
新聞熱點(diǎn)
疑難解答