Optimize: Keep-Alive follows RFC

joytom

RFC: https://www.rfc-editor.org/rfc/rfc2616

HTTP/1.0 默认是短连接,除非传 Connection: Keep-Alive 才是长连接
HTTP/1.1 默认长连接,除非传 Connection: close,才是短连接

Workerman 的不规范实现,可能会对完全遵守 RFC 工具造成影响,比如 ab 非长连接压测。

下面是可以直接测试的代码:(希望 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));
};

// 运行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();
1084 1 0
1个回答

walkor 打赏

https://github.com/walkor/workerman/pull/857#issuecomment-1362375399

非常感谢你的建议

从语义上来讲,

$connection->send(new Response(200, [], "hello:" . $request->connection->id));

应该只发送数据,不应该断开连接,如果是短连接压测应该使用$connection->close(new Response())

一个解决方案是实现一个独立的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();
        }
    }
}

// 调用时类似
$http = new HttpServer('http://0.0.0.0:1234');
$http->onRequest = function($request){
   return new Response();
};

这样层次清晰明了,但是增加了一个类加大了workerman复杂性,其它意义不大,得不偿失。

总结

$worker = new Worker('http://0.0.0.0:8080');

仅仅是创建了一个能支持http协议的容器,但是它不是完整的http服务器实现,是否关闭连接需要手动判断并调用。

关于自动化测试明年应该会加上。

  • liziyu 2022-12-22

    👍 讲究!

  • chaz6chez 2022-12-25

    正在写测试,因为事情比较多,也只能空了写一点空了写一点😭

年代过于久远,无法发表回答
×
🔝