RFC: https://www.rfc-editor.org/rfc/rfc2616
HTTP/1.0 默認(rèn)是短連接,除非傳 Connection: Keep-Alive 才是長(zhǎng)連接
HTTP/1.1 默認(rèn)長(zhǎng)連接,除非傳 Connection: close,才是短連接
Workerman 的不規(guī)范實(shí)現(xiàn),可能會(huì)對(duì)完全遵守 RFC 工具造成影響,比如 ab 非長(zhǎng)連接壓測(cè)。
下面是可以直接測(cè)試的代碼:(希望 Workerman 盡快引入 phpunit)
server.php:
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
require __DIR__ . '/vendor/autoload.php';
$worker = new Worker('http://0.0.0.0:8080');
$worker->onMessage = function(TcpConnection $connection, Request $request)
{
// 下面兩種都可以
// $connection->send("hello:" . $request->connection->id);
$connection->send(new Response(200, [], "hello:" . $request->connection->id));
};
// 運(yùn)行worker
Worker::runAll();
test.php:
<?php
function test1_0_close()
{
echo 'HTTP 1.0 close test:', PHP_EOL;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:8080/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
var_dump($a = curl_exec($ch));
var_dump($b = curl_exec($ch));
curl_close($ch);
if ($a === $b)
{
echo 'HTTP/1.0 close test failed', PHP_EOL, PHP_EOL;
}
}
function test1_0_keep_alive()
{
echo 'HTTP 1.0 keep-alive test:', PHP_EOL;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:8080/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Connection: Keep-Alive',
]);
var_dump($a = curl_exec($ch));
var_dump($b = curl_exec($ch));
curl_close($ch);
if ($a !== $b)
{
echo 'HTTP/1.0 keep-alive test failed', PHP_EOL, PHP_EOL;
}
}
function test1_1_close()
{
echo 'HTTP 1.1 close test:', PHP_EOL;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:8080/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Connection: close',
]);
var_dump($a = curl_exec($ch));
var_dump($b = curl_exec($ch));
curl_close($ch);
if ($a === $b)
{
echo 'HTTP/1.1 close test failed', PHP_EOL, PHP_EOL;
}
}
function test1_1_keep_alive()
{
echo 'HTTP 1.1 keep-alive test:', PHP_EOL;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:8080/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
var_dump($a = curl_exec($ch));
var_dump($b = curl_exec($ch));
curl_close($ch);
if ($a !== $b)
{
echo 'HTTP/1.1 keep-alive test failed', PHP_EOL, PHP_EOL;
}
}
test1_0_close();
test1_0_keep_alive();
test1_1_close();
test1_1_keep_alive();
https://github.com/walkor/workerman/pull/857#issuecomment-1362375399
非常感謝你的建議
從語(yǔ)義上來(lái)講,
$connection->send(new Response(200, [], "hello:" . $request->connection->id));
應(yīng)該只發(fā)送數(shù)據(jù),不應(yīng)該斷開(kāi)連接,如果是短連接壓測(cè)應(yīng)該使用$connection->close(new Response())
。
一個(gè)解決方案是實(shí)現(xiàn)一個(gè)獨(dú)立的HttpServer
Class HttpServer extend Worker
{
public $onRequest;
public function onMessage ($connction, $request)
{
$reponse = call_user_func($this->onMessate, $request);
$connection->send($response);
if ($this->shouldCloseConnection($request)) {
$connection->close();
}
}
}
// 調(diào)用時(shí)類似
$http = new HttpServer('http://0.0.0.0:1234');
$http->onRequest = function($request){
return new Response();
};
這樣層次清晰明了,但是增加了一個(gè)類加大了workerman復(fù)雜性,其它意義不大,得不償失。
總結(jié)
$worker = new Worker('http://0.0.0.0:8080');
僅僅是創(chuàng)建了一個(gè)能支持http協(xié)議的容器,但是它不是完整的http服務(wù)器實(shí)現(xiàn),是否關(guān)閉連接需要手動(dòng)判斷并調(diào)用。
關(guān)于自動(dòng)化測(cè)試明年應(yīng)該會(huì)加上。