2019-09-06 23:53:10 +08:00

575 lines
14 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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";
}
}