575 lines
14 KiB
PHP
Executable File
575 lines
14 KiB
PHP
Executable File
<?php
|
||
require __DIR__.'/../examples/websocket/WebSocketClient.php';
|
||
|
||
//关闭错误输出
|
||
//error_reporting(0);
|
||
$shortopts = "c:";
|
||
$shortopts .= "n:";
|
||
$shortopts .= "s:";
|
||
$shortopts .= "f:";
|
||
$shortopts .= "p::";
|
||
|
||
$opt = getopt($shortopts);
|
||
//并发数量
|
||
if(!isset($opt['c'])) exit("require -c [process_num]. ep: -c 100\n");
|
||
if(!isset($opt['n'])) exit("require -n [request_num]. ep: -n 10000\n");
|
||
if(!isset($opt['s'])) exit("require -s [server_url]. ep: -s tcp://127.0.0.1:9999\n");
|
||
if(!isset($opt['f'])) exit("require -f [test_function]. ep: -f short_tcp\n");
|
||
|
||
$bc = new Swoole_Benchmark(trim($opt['f']));
|
||
$bc->process_num = (int)$opt['c'];
|
||
$bc->request_num = (int)$opt['n'];
|
||
$bc->server_url = trim($opt['s']);
|
||
$bc->server_config = parse_url($bc->server_url);
|
||
$bc->send_data = "GET / HTTP/1.1\r\n";
|
||
$bc->send_data .= "Host: www.baidu.com\r\n";
|
||
$bc->send_data .= "Connection: keep-alive\r\n";
|
||
$bc->send_data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n";
|
||
$bc->send_data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\r\n\r\n";
|
||
|
||
$bc->read_len = 65536;
|
||
if(!empty($opt['p'])) $bc->show_detail = true;
|
||
|
||
function eof(Swoole_Benchmark $bc)
|
||
{
|
||
static $client = null;
|
||
static $i;
|
||
$start = microtime(true);
|
||
if (empty($client)) {
|
||
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
|
||
$client->set(array('open_eof_check' => true, "package_eof" => "\r\n\r\n"));
|
||
$end = microtime(true);
|
||
$conn_use = $end - $start;
|
||
$bc->max_conn_time = $conn_use;
|
||
$i = 0;
|
||
//echo "connect {$bc->server_url} \n";
|
||
if (!$client->connect($bc->server_config['host'], $bc->server_config['port'], 2)) {
|
||
error:
|
||
echo "Error: " . swoole_strerror($client->errCode) . "[{$client->errCode}]\n";
|
||
$client = null;
|
||
return false;
|
||
}
|
||
$start = $end;
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
$data = str_repeat('A', rand(100, 200))."\r\n\r\n";
|
||
if (!$client->send($data))
|
||
{
|
||
goto error;
|
||
}
|
||
$end = microtime(true);
|
||
$write_use = $end - $start;
|
||
if ($write_use > $bc->max_write_time) $bc->max_write_time = $write_use;
|
||
$start = $end;
|
||
/*--------读取Sokcet-------*/
|
||
$i ++;
|
||
$ret = $client->recv();
|
||
if (empty($ret))
|
||
{
|
||
echo $bc->pid, "#$i", " is lost\n";
|
||
return false;
|
||
}
|
||
elseif(strlen($ret) != strlen($data))
|
||
{
|
||
echo "#$i\tlength error\n";
|
||
var_dump($ret);
|
||
echo "-----------------------------------\n";
|
||
var_dump($data);
|
||
}
|
||
$end = microtime(true);
|
||
$read_use = $end - $start;
|
||
if ($read_use > $bc->max_read_time) $bc->max_read_time = $read_use;
|
||
return true;
|
||
}
|
||
|
||
function long_tcp(Swoole_Benchmark $bc)
|
||
{
|
||
static $fp = null;
|
||
static $i;
|
||
$start = microtime(true);
|
||
if(empty($fp))
|
||
{
|
||
$fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
|
||
$end = microtime(true);
|
||
$conn_use = $end-$start;
|
||
$bc->max_conn_time = $conn_use;
|
||
$i = 0;
|
||
//echo "connect {$bc->server_url} \n";
|
||
if (!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 2))
|
||
{
|
||
error:
|
||
echo "Error: ".swoole_strerror($fp->errCode)."[{$fp->errCode}]\n";
|
||
$fp = null;
|
||
return false;
|
||
}
|
||
$start = $end;
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
if (!$fp->send($bc->send_data))
|
||
{
|
||
goto error;
|
||
}
|
||
$end = microtime(true);
|
||
$write_use = $end - $start;
|
||
if ($write_use > $bc->max_write_time)
|
||
{
|
||
$bc->max_write_time = $write_use;
|
||
}
|
||
$start = $end;
|
||
/*--------读取Sokcet-------*/
|
||
while(true)
|
||
{
|
||
$ret = $fp->recv(65530);
|
||
if (empty($ret) or substr($ret, -1, 1) == "\n")
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
//var_dump($ret);
|
||
$i++;
|
||
if (empty($ret))
|
||
{
|
||
echo $bc->pid, "#$i@", " is lost\n";
|
||
return false;
|
||
}
|
||
$end = microtime(true);
|
||
$read_use = $end - $start;
|
||
if ($read_use > $bc->max_read_time)
|
||
{
|
||
$bc->max_read_time = $read_use;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function websocket(Swoole_Benchmark $bc)
|
||
{
|
||
static $client = null;
|
||
static $i;
|
||
$start = microtime(true);
|
||
|
||
if (empty($client))
|
||
{
|
||
$client = new WebSocketClient($bc->server_config['host'], $bc->server_config['port']);
|
||
if (!$client->connect())
|
||
{
|
||
echo "connect failed\n";
|
||
return false;
|
||
}
|
||
|
||
$end = microtime(true);
|
||
$conn_use = $end - $start;
|
||
$bc->max_conn_time = $conn_use;
|
||
$i = 0;
|
||
$start = $end;
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
if (!$client->send($bc->send_data))
|
||
{
|
||
echo "send failed\n";
|
||
return false;
|
||
}
|
||
$end = microtime(true);
|
||
$write_use = $end - $start;
|
||
if ($write_use > $bc->max_write_time)
|
||
{
|
||
$bc->max_write_time = $write_use;
|
||
}
|
||
$start = $end;
|
||
/*--------读取Sokcet-------*/
|
||
$ret = $client->recv();
|
||
//var_dump($ret);
|
||
$i++;
|
||
if (empty($ret))
|
||
{
|
||
echo $bc->pid, "#$i@", " is lost\n";
|
||
return false;
|
||
}
|
||
$end = microtime(true);
|
||
$read_use = $end - $start;
|
||
if ($read_use > $bc->max_read_time)
|
||
{
|
||
$bc->max_read_time = $read_use;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 去掉计时信息的UDP
|
||
* @param $bc
|
||
* @return bool
|
||
*/
|
||
function udp(Swoole_Benchmark $bc)
|
||
{
|
||
static $fp;
|
||
if (empty($fp))
|
||
{
|
||
$fp = stream_socket_client($bc->server_url, $errno, $errstr, 1);
|
||
if (!$fp)
|
||
{
|
||
echo "{$errstr}[{$errno}]\n";
|
||
return false;
|
||
}
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
fwrite($fp, $bc->send_data);
|
||
/*--------读取Sokcet-------*/
|
||
$ret = fread($fp, $bc->read_len);
|
||
if (empty($ret))
|
||
{
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function udp2(Swoole_Benchmark $bc)
|
||
{
|
||
static $fp;
|
||
$start = microtime(true);
|
||
if (empty($fp))
|
||
{
|
||
$u = parse_url($bc->server_url);
|
||
$fp = new swoole_client(SWOOLE_SOCK_UDP);
|
||
$fp->connect($u['host'], $u['port'], 0.5, 0);
|
||
$end = microtime(true);
|
||
$conn_use = $end - $start;
|
||
$bc->max_conn_time = $conn_use;
|
||
$start = $end;
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
$fp->send($bc->send_data);
|
||
$end = microtime(true);
|
||
$write_use = $end - $start;
|
||
if ($write_use > $bc->max_write_time)
|
||
{
|
||
$bc->max_write_time = $write_use;
|
||
}
|
||
$start = $end;
|
||
/*--------读取Sokcet-------*/
|
||
$ret = $fp->recv();
|
||
if (empty($ret))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
$end = microtime(true);
|
||
$read_use = $end - $start;
|
||
if ($read_use > $bc->max_read_time)
|
||
{
|
||
$bc->max_read_time = $read_use;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function short_tcp($bc)
|
||
{
|
||
$fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
|
||
if(!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 1))
|
||
{
|
||
error:
|
||
echo "Error: ".socket_strerror($fp->errCode)."[{$fp->errCode}]\n";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if(!$fp->send($bc->send_data))
|
||
{
|
||
goto error;
|
||
}
|
||
$ret = $fp->recv();
|
||
$fp->close();
|
||
if(!empty($ret)) return true;
|
||
else return false;
|
||
}
|
||
}
|
||
|
||
function long_socks5($bc)
|
||
{
|
||
static $fp = null;
|
||
static $i;
|
||
$start = microtime(true);
|
||
if(empty($fp))
|
||
{
|
||
$fp = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC,5);
|
||
$end = microtime(true);
|
||
$conn_use = $end-$start;
|
||
$bc->max_conn_time = $conn_use;
|
||
$i = 0;
|
||
//echo "connect {$bc->server_url} \n";
|
||
if (!$fp->connect($bc->server_config['host'], $bc->server_config['port'], 2))
|
||
{
|
||
error:
|
||
echo "Error: ".swoole_strerror($fp->errCode)."[{$fp->errCode}]\n";
|
||
$fp = null;
|
||
return false;
|
||
}
|
||
|
||
$fp->send(pack("C3", 0x05, 0x01, 0x00));//greet
|
||
$data = $fp->recv();
|
||
$response = unpack("Cversion/Cmethod", $data);
|
||
if ($response['version'] != 0x05)
|
||
{
|
||
exit('SOCKS version is not supported.');
|
||
}
|
||
$headers = getHeader($bc->send_data);
|
||
if (empty($headers['port'])) {
|
||
$headers['port'] = 80;
|
||
}
|
||
$g = pack("C5", 0x05, 0x01, 0x00, 0x03, strlen($headers['host'])) . $headers['host'] . pack("n", $headers['port']);
|
||
$fp->send($g);
|
||
$data = $fp->recv();
|
||
$response = unpack("Cversion/Cresult/Creg/Ctype/Lip/Sport", $data);
|
||
if ($response['result'] != 0x00)
|
||
{
|
||
echo 'SOCKS connection request failed: ' . getSocksRefusalMsg($response['result']), $response['result'];exit;
|
||
}
|
||
|
||
$start = $end;
|
||
}
|
||
/*--------写入Sokcet-------*/
|
||
if (!$fp->send($bc->send_data))
|
||
{
|
||
goto error;
|
||
}
|
||
$end = microtime(true);
|
||
$write_use = $end - $start;
|
||
if ($write_use > $bc->max_write_time)
|
||
{
|
||
$bc->max_write_time = $write_use;
|
||
}
|
||
$start = $end;
|
||
/*--------读取Sokcet-------*/
|
||
while(true)
|
||
{
|
||
$ret = $fp->recv(65530);
|
||
if (empty($ret) or substr($ret, -1, 1) == "\n")
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
//var_dump($ret);
|
||
$i++;
|
||
if (empty($ret))
|
||
{
|
||
echo $bc->pid, "#$i@", " is lost\n";
|
||
return false;
|
||
}
|
||
$end = microtime(true);
|
||
$read_use = $end - $start;
|
||
if ($read_use > $bc->max_read_time)
|
||
{
|
||
$bc->max_read_time = $read_use;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function getHeader($message)
|
||
{
|
||
// 标准每行应该以"\r\n"行终止,这里兼容以"\n"作为行终止的情况,所以按"\n"分割行
|
||
$lines = explode("\n", $message);
|
||
foreach ($lines as &$line)
|
||
{
|
||
// 按"\n"分割行以后,某些行末可能存在"\r"字符,这里将其过滤掉
|
||
$line = rtrim($line, "\r");
|
||
}
|
||
unset($line);
|
||
|
||
if (count($lines) <= 0)
|
||
{
|
||
return false;
|
||
}
|
||
$headers = [];
|
||
|
||
foreach ($lines as $line)
|
||
{
|
||
$pos = strpos($line, ':');
|
||
// 非标准首部,抛弃
|
||
if ($pos === false)
|
||
{
|
||
continue;
|
||
}
|
||
$field = trim(substr($line, 0, $pos));
|
||
$value = trim(substr($line, $pos + 1));
|
||
|
||
// 如果有host头部,重新设置host和port
|
||
if (strtolower($field) === 'host')
|
||
{
|
||
$segments = explode(':', $value);
|
||
$host = $segments[0];
|
||
$headers['host'] = $host;
|
||
if (isset($segments[1]))
|
||
{
|
||
$port = intval($segments[1]);
|
||
$headers['port'] = $port;
|
||
}
|
||
}
|
||
$headers[$field] = $value;
|
||
}
|
||
return $headers;
|
||
}
|
||
//请求数量最好是进程数的倍数
|
||
$bc->process_req_num = intval($bc->request_num/$bc->process_num);
|
||
$bc->run();
|
||
$bc->report();
|
||
$bc->end();
|
||
|
||
class Swoole_Benchmark
|
||
{
|
||
public $test_func;
|
||
public $process_num;
|
||
public $request_num;
|
||
public $server_url;
|
||
public $server_config;
|
||
public $send_data;
|
||
public $read_len;
|
||
|
||
public $time_end;
|
||
private $shm_key;
|
||
public $main_pid;
|
||
public $child_pid = array();
|
||
|
||
public $show_detail = false;
|
||
public $max_write_time = 0;
|
||
public $max_read_time = 0;
|
||
public $max_conn_time = 0;
|
||
|
||
public $pid;
|
||
|
||
protected $tmp_dir = '/tmp/swoole_bench/';
|
||
|
||
function __construct($func)
|
||
{
|
||
if (!function_exists($func))
|
||
{
|
||
exit(__CLASS__ . ": function[$func] not exists\n");
|
||
}
|
||
if (!is_dir($this->tmp_dir))
|
||
{
|
||
mkdir($this->tmp_dir);
|
||
}
|
||
$this->test_func = $func;
|
||
}
|
||
function end()
|
||
{
|
||
unlink($this->shm_key);
|
||
foreach($this->child_pid as $pid)
|
||
{
|
||
$f = $this->tmp_dir . 'lost_' . $pid . '.log';
|
||
if (is_file($f)) unlink($f);
|
||
}
|
||
}
|
||
|
||
function run()
|
||
{
|
||
$this->main_pid = posix_getpid();
|
||
$this->shm_key = $this->tmp_dir.'t.log';
|
||
for ($i = 0; $i < $this->process_num; $i++)
|
||
{
|
||
$this->child_pid[] = $this->start(array($this, 'worker'));
|
||
}
|
||
for ($i = 0; $i < $this->process_num; $i++)
|
||
{
|
||
$status = 0;
|
||
$pid = pcntl_wait($status);
|
||
}
|
||
$this->time_end = microtime(true);
|
||
}
|
||
|
||
function init_signal()
|
||
{
|
||
pcntl_signal(SIGUSR1,array($this, "sig_handle"));
|
||
}
|
||
|
||
function sig_handle($sig)
|
||
{
|
||
switch ($sig)
|
||
{
|
||
case SIGUSR1:
|
||
return;
|
||
}
|
||
$this->init_signal();
|
||
}
|
||
|
||
function start($func)
|
||
{
|
||
$pid = pcntl_fork();
|
||
if($pid>0)
|
||
{
|
||
return $pid;
|
||
}
|
||
elseif($pid==0)
|
||
{
|
||
$this->worker();
|
||
}
|
||
else
|
||
{
|
||
echo "Error:fork fail\n";
|
||
}
|
||
}
|
||
function worker()
|
||
{
|
||
$lost = 0;
|
||
if(!file_exists($this->shm_key))
|
||
{
|
||
file_put_contents($this->shm_key,microtime(true));
|
||
}
|
||
if($this->show_detail) $start = microtime(true);
|
||
$this->pid = posix_getpid();
|
||
|
||
for ($i = 0; $i < $this->process_req_num; $i++)
|
||
{
|
||
$func = $this->test_func;
|
||
if(!$func($this)) $lost++;
|
||
}
|
||
if ($this->show_detail)
|
||
{
|
||
$log = $this->pid . "#\ttotal_use(s):" . substr(microtime(true) - $start, 0, 5);
|
||
$log .= "\tconnect(ms):" . substr($this->max_conn_time * 1000, 0, 5);
|
||
$log .= "\twrite(ms):" . substr($this->max_write_time * 1000, 0, 5);
|
||
$log .= "\tread(ms):" . substr($this->max_read_time * 1000, 0, 5);
|
||
file_put_contents($this->tmp_dir.'lost_' . $this->pid . '.log', $lost . "\n" . $log);
|
||
}
|
||
else
|
||
{
|
||
file_put_contents($this->tmp_dir.'lost_'.$this->pid.'.log', $lost);
|
||
}
|
||
exit(0);
|
||
}
|
||
|
||
function report()
|
||
{
|
||
$time_start = file_get_contents($this->shm_key);
|
||
$usetime = $this->time_end - $time_start;
|
||
$lost = 0;
|
||
|
||
foreach ($this->child_pid as $f)
|
||
{
|
||
$file = $this->tmp_dir.'lost_'.$f.'.log';
|
||
if (is_file($file))
|
||
{
|
||
$_lost = file_get_contents($file);
|
||
$log = explode("\n",$_lost,2);
|
||
}
|
||
if (!empty($log))
|
||
{
|
||
$lost += intval($log[0]);
|
||
if ($this->show_detail) echo $log[1], "\n";
|
||
}
|
||
}
|
||
//并发量
|
||
echo "concurrency:\t".$this->process_num,"\n";
|
||
//请求量
|
||
echo "request num:\t".$this->request_num,"\n";
|
||
//请求量
|
||
echo "lost num:\t".$lost,"\n";
|
||
//请求量
|
||
echo "success num:\t".($this->request_num-$lost),"\n";
|
||
//总时间
|
||
echo "total time:\t".substr($usetime,0,5),"\n";
|
||
//每秒处理能力
|
||
echo "req per second:\t".intval($this->request_num/$usetime),"\n";
|
||
//每次请求平均时间ms
|
||
echo "one req use(ms):\t".substr($usetime/$this->request_num*1000,0,5),"\n";
|
||
}
|
||
}
|
||
|