【问题标题】:Guzzle TransferStats HandlerStats documentation/explanation?Guzzle TransferStats HandlerStats 文档/说明?
【发布时间】:2021-04-23 11:34:35
【问题描述】:

在 Guzzle 文档中,您可以找到名为 on_stats 的可调用 a request option,它允许您访问请求的传输统计信息并访问与您的客户端关联的处理程序的较低级别的传输详细信息。

TransferStatsobject 为您提供了一些检查请求的方法,其中之一是 getHandlerStats()。根据 docblock,此方法为您提供了一个包含所有处理程序特定传输数据的数组。

但是我找不到该数组的特定键的任何文档。有些很简单,例如primary_ipurl,但对于其他人,我有以下问题。

$handlerStats = [
    "url" => "https://example.com",
    "content_type" => "application/json; charset=utf-8",
    "http_code" => 200,
    "header_size" => 569,
    "request_size" => 731,  // is request size purely the body? and is this in bytes or kb or..?
    "filetime" => -1,
    "ssl_verify_result" => 0, // what are the options here?
    "redirect_count" => 0, 
    "total_time" => 0.33132, // is this in seconds, i guess so? this is supposed to be the same as the getTransferTime() method.
    "namelookup_time" => 0.067308, // i assume purely for the dns lookup?
    "connect_time" => 0.078286, // what is this?
    "pretransfer_time" => 0.111673, // what is this? and do some of these times overlap.
    "size_upload" => 0.0, // again are sizes in bytes?
    "size_download" => 7717.0,
    "speed_download" => 23314.0, // is this in bytes per second?
    "speed_upload" => 0.0,
    "download_content_length" => -1.0,
    "upload_content_length" => -1.0,
    "starttransfer_time" => 0.320876,
    "redirect_time" => 0.0,
    "redirect_url" => "",
    "primary_ip" => "1.1.1.1",
    "certinfo" => [],
    "primary_port" => 443,
    "local_ip" => "192.168.208.5",
    "local_port" => 39868,
    "http_version" => 2,
    "protocol" => 2,
    "ssl_verifyresult" => 0, // why is this duplicated, deprecated? 
    "scheme" => "HTTPS",
    "appconnect_time_us" => 111513,
    "connect_time_us" => 78286,
    "namelookup_time_us" => 67308,
    "pretransfer_time_us" => 111673,
    "redirect_time_us" => 0,
    "starttransfer_time_us" => 320876,
    "total_time_us" => 331320,
    "appconnect_time" => 0.111513, // what does the appconnectime do?
  ];

有没有人有文档链接或者可以解释这些键?非常感谢。

【问题讨论】:

  • 从键名上看不太明显?你不知道哪一个?
  • @Andy 确实有些明显偏离了方向,问题就在 cmets 的键后面。例如什么是 appconnect 时间或预传输时间?这就是我寻找官方文档的原因。
  • 在这里评论会占用很多空间,所以我将下一条评论移至答案...

标签: php http guzzle php-curl


【解决方案1】:

传输统计信息由 TransferStats 类提供,但是Guzzle documentation 没有描述统计信息的返回键和值。 阅读TransferStats cmets 提供了一些线索。

TransferStats 在三个类中创建:

CurlFactory

private static function invokeStats(EasyHandle $easy): void
{
    $curlStats = \curl_getinfo($easy->handle);
    $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME);
    $stats = new TransferStats(
        $easy->request,
        $easy->response,
        $curlStats['total_time'],
        $easy->errno,
        $curlStats
    );
    ($easy->options['on_stats'])($stats);
}

MockHandler

private function invokeStats(
        RequestInterface $request,
        array $options,
        ResponseInterface $response = null,
        $reason = null
): void {
    if (isset($options['on_stats'])) {
        $transferTime = $options['transfer_time'] ?? 0;
        $stats = new TransferStats($request, $response, $transferTime, $reason);
        ($options['on_stats'])($stats);
    }
}

StreamHandler

private function invokeStats(
    array $options,
    RequestInterface $request,
    ?float $startTime,
    ResponseInterface $response = null,
    \Throwable $error = null
): void {
    if (isset($options['on_stats'])) {
        $stats = new TransferStats($request, $response, Utils::currentTime() - $startTime, $error, []);
        ($options['on_stats'])($stats);
    }
}

Note 来自文档状态:

Guzzle 不再需要 cURL 来发送 HTTP 请求。 如果 cURL,Guzzle 将使用 PHP 流包装器发送 HTTP 请求 没有安装。或者,您可以提供自己的 HTTP 处理程序 用于发送请求。请记住,cURL 仍然是必需的 发送并发请求。

因此,您从 stats 收到的键和值很可能取决于 Guzzle 是否使用 cURL。您还可以在创建 TransferStats 的示例中看到它因不同的类(CurlFactory/MockHandler/StramHandler)而异。这可能是缺少统计信息文档的原因。

请注意,您可能从 stats 收到的键和值也取决于您使用的 cURL 版本,甚至是您使用的 PHP 版本。 PHP 的 curl_getinfo 的变更日志指出:

Version     Description
7.3.0       Introduced CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, CURLINFO_CONTENT_LENGTH_UPLOAD_T (...)

所以低于 7.3.0 的 PHP 版本不应该有任何像 CURLINFO_CONTENT_LENGTH_DOWNLOAD_T 和许多其他的统计信息。

注意您可能会收到任何特定的键是个好主意,因此我始终建议将array_key_exists 与传输的状态数组一起使用。

假设 Guzzle 使用 cURL 进行请求,则统计信息由 PHP 的原生函数 curl_getinfo 获取,该函数描述了许多统计信息:

CURLINFO_EFFECTIVE_URL - Last effective URL
CURLINFO_HTTP_CODE - The last response code. As of PHP 5.5.0 and cURL 7.10.8, this is a legacy alias of CURLINFO_RESPONSE_CODE 
(...)

和可能的数组键:

"url"
"content_type"
"http_code" 
(...)

但是您可能会注意到 PHP 的预定义常量提供的排序描述不一致:

CURLINFO_HTTP_CODE - The last response code. As of PHP 5.5.0 and cURL 7.10.8, this is a legacy alias of CURLINFO_RESPONSE_CODE 

和键列表:

"content_type"

CURLINFO_HTTP_CODE 在常量列表中排名第二,与返回键列表中的第二个键 content_type 不匹配。
此外,一些预定义的常量如:APPCONNECT_TIME_T 在返回的数组键列表中没有它们各自的名称。假设APPCONNECT_TIME_T 具有数组键appconnect_time_t 是错误的,因为它实际上是appconnect_time_us


PHP 的curl_getinfo 文档不广泛也不一致。

要获取 PHP 的预定义常量和相应的数组键名,最好查看 PHP 7.4 source of ext/curl/interface.c 代码,您可能会在其中看到对本机 cURL 函数的调用:

if (curl_easy_getinfo(ch->cp, CURLINFO_HTTP_CODE, &l_code) == CURLE_OK) {
    CAAL("http_code", l_code);
}

您可以轻松地将CURLINFO_HTTP_CODE(即curl_easy_getinfo 的第二个参数)与http_code(即CAAL 的第一个参数)匹配。
这样您就可以从最可靠的来源构建您自己的匹配列表,例如
[CURLINFO_HTTP_CODE => 'http_code', <PHPConstant> => <array-key>]
,因为来自 PHP 本身的来源。

因为 PHP 使用 cURL 的 curl_easy_getinfo 然后查看以下文档:

curl_easy_getinfo - 从 curl 中提取信息

将为您带来更多细节,特别有趣的是关于TIMES的这些:

An overview of the six time values available from curl_easy_getinfo()

curl_easy_perform()
    |
    |--NAMELOOKUP
    |--|--CONNECT
    |--|--|--APPCONNECT
    |--|--|--|--PRETRANSFER
    |--|--|--|--|--STARTTRANSFER
    |--|--|--|--|--|--TOTAL
    |--|--|--|--|--|--REDIRECT

例如:

CURLINFO_APPCONNECT_TIME_T

从开始到 SSL/SSH 握手完成的时间。看 CURLINFO_APPCONNECT_TIME_T

点击该链接,您可以获得更详细的解释:

(...) 这个时间通常非常接近 CURLINFO_PRETRANSFER_TIME_T时间,HTTP等情况除外 由于等待,预传输时间可能会延迟的流水线 管道线等。

当遵循重定向时,会添加每个请求的时间 一起。 (...)

我希望整个知识结合起来回答你的问题,并且你喜欢我揭示的整个挖掘过程,而不是在没有任何证据的情况下说 X 是 Y。

现在让我们玩得开心:)

运行此代码:

<?php

error_reporting(E_ALL);
header("Content-Type: text/plain");

$urlPhpSource = 'https://raw.githubusercontent.com/php/php-src/5caaf40b43303887a38d738a9c2a2f4cf6dc9b1a/ext/curl/interface.c';
$phpSourceArr = explode(\PHP_EOL, file_get_contents($urlPhpSource));

$option = '';
$optionNameArr = [];
foreach ($phpSourceArr as $line) {

    $line = trim($line);
    if (preg_match('/curl_easy_getinfo.+(CURLINFO_[A-Z_]+)/', $line, $matches)) {
        $option = $matches[1];
    }

    if (!empty($option) && preg_match('/^CAA.+\"(.+)\"/', $line, $matches)) {
        $optionNameArr[$option] = $matches[1];
        $option = '';
    }
}

$urlDescriptions = 'https://curl.se/libcurl/c/easy_getinfo_options.html';
$descriptionsArr = explode(\PHP_EOL, file_get_contents($urlDescriptions));

$descriptionResultArr = [];
foreach ($descriptionsArr as $line) {
    $pattern = '/(CURLINFO_[A-Z_]+).*';
    $pattern .= '<\/td><td>(.*)<\/td><\/tr>/';
    if (preg_match($pattern, $line, $matches)) {
        $descriptionResultArr[trim($matches[1])] = ucfirst(trim($matches[2]));
    }
}

$resultCombinedArr = [];
foreach($optionNameArr as $option => $name) {
    if (!key_exists($option, $descriptionResultArr)) {
        $desciption = '<not found>';
    } else {
        $desciption = $descriptionResultArr[$option];
    }

    $tmpArr = [];
    $tmpArr['name'] = $name;
    $tmpArr['option'] = $option;
    if (defined($option)) {
        $tmpArr['optionValue'] = constant('\\' . $option);
    }
    $tmpArr['desciption'] = $desciption;
    $resultCombinedArr[$name] = $tmpArr;
}

echo str_repeat("-", 80) . \PHP_EOL . \PHP_EOL;
echo "All Options:\n";
print_r($resultCombinedArr);
echo str_repeat("-", 80) . \PHP_EOL . \PHP_EOL;

echo "Available Options:\n";
print_r(array_filter($resultCombinedArr, function($data) {
    return key_exists('optionValue', $data);
}));
echo str_repeat("-", 80) . \PHP_EOL . \PHP_EOL;

echo "Not available Options:\n";
print_r(array_filter($resultCombinedArr, function($data) {
    return !key_exists('optionValue', $data);
}));

将给出所有可能的列表

  • 预定义常量(选项)
  • 选项的 int 值 (optionValue)
  • 选项各自的数组键(名称)
  • 选项的描述(描述)

这样:

All Options:
Array
(
    [url] => Array
        (
            [name] => url
            [option] => CURLINFO_EFFECTIVE_URL
            [optionValue] => 1048577
            [desciption] => Get the last used URL
        )

    [content_type] => Array
        (
            [name] => content_type
            [option] => CURLINFO_CONTENT_TYPE
            [optionValue] => 1048594
            [desciption] => Get Content-Type
        )

    [http_code] => Array
        (
            [name] => http_code
            [option] => CURLINFO_HTTP_CODE
            [optionValue] => 2097154
            [desciption] => <not found>
        )

        (...)

受您的 PHP 选项支持:

Available Options:
Array
(
    [url] => Array
        (
            [name] => url
            [option] => CURLINFO_EFFECTIVE_URL
            [optionValue] => 1048577
            [desciption] => Get the last used URL
        )

    [content_type] => Array
        (
            [name] => content_type
            [option] => CURLINFO_CONTENT_TYPE
            [optionValue] => 1048594
            [desciption] => Get Content-Type
        )

    [http_code] => Array
        (
            [name] => http_code
            [option] => CURLINFO_HTTP_CODE
            [optionValue] => 2097154
            [desciption] => <not found>
        )

     (...)

不支持:

Not available Options:
Array
(
    [appconnect_time_us] => Array
        (
            [name] => appconnect_time_us
            [option] => CURLINFO_APPCONNECT_TIME_T
            [desciption] => Get the time until the SSL/SSH handshake is completed
        )

    [connect_time_us] => Array
        (
            [name] => connect_time_us
            [option] => CURLINFO_CONNECT_TIME_T
            [desciption] => Get the time until connect
        )

    [namelookup_time_us] => Array
        (
            [name] => namelookup_time_us
            [option] => CURLINFO_NAMELOOKUP_TIME_T
            [desciption] => Get the name lookup time in microseconds
        )

    (...)

请注意,如果未在 PHP 中定义像 CURLINFO_APPCONNECT_TIME_T 这样的预定义常量,则认为不支持选项 CURLINFO_APPCONNECT_TIME_T 但这并不一定意味着您将获得没有相应密钥 appconnect_time_us 或密钥的传输统计信息appconnect_time_us 会有一些未定义的值。

另一个奇怪的事情是我为 PHP 版本运行了这个 scipt:

PHP 7.3.27-9+ubuntu18.04.1+deb.sury.org+1 (cli)(构建时间:2021 年 2 月 23 日 15:10:08) (NTS)

我没有定义这些常量:

Array
(
    [0] => CURLINFO_APPCONNECT_TIME_T
    [1] => CURLINFO_CONNECT_TIME_T
    [2] => CURLINFO_NAMELOOKUP_TIME_T
    [3] => CURLINFO_PRETRANSFER_TIME_T
    [4] => CURLINFO_REDIRECT_TIME_T
    [5] => CURLINFO_STARTTRANSFER_TIME_T
    [6] => CURLINFO_TOTAL_TIME_T
)

这与 curl_getinfo 文档中的声明互斥,该声明在 Changelog 部分下声明所有这些声明都应该已经在 PHP 7.3.0 及更高版本中引入。要么我做错了什么,要么curl_getinfo 的文档再次被证明是不准确的。

不过以我自己的官方形象为准

PHP 7.3.8 (cli)(构建时间:2019 年 8 月 2 日 05:16:32)(NTS)

Docker 镜像每个选项都可用。

这里是生成的参数的非官方文档,您可以复制并粘贴到您的项目中:

<?php

array (
  'url' => 
  array (
    'name' => 'url',
    'option' => 'CURLINFO_EFFECTIVE_URL',
    'optionValue' => 1048577,
    'desciption' => 'Get the last used URL',
  ),
  'content_type' => 
  array (
    'name' => 'content_type',
    'option' => 'CURLINFO_CONTENT_TYPE',
    'optionValue' => 1048594,
    'desciption' => 'Get Content-Type',
  ),
  'http_code' => 
  array (
    'name' => 'http_code',
    'option' => 'CURLINFO_HTTP_CODE',
    'optionValue' => 2097154,
    'desciption' => '<not found>',
  ),
  'header_size' => 
  array (
    'name' => 'header_size',
    'option' => 'CURLINFO_HEADER_SIZE',
    'optionValue' => 2097163,
    'desciption' => 'Get size of retrieved headers',
  ),
  'request_size' => 
  array (
    'name' => 'request_size',
    'option' => 'CURLINFO_REQUEST_SIZE',
    'optionValue' => 2097164,
    'desciption' => 'Get size of sent request',
  ),
  'filetime' => 
  array (
    'name' => 'filetime',
    'option' => 'CURLINFO_FILETIME',
    'optionValue' => 2097166,
    'desciption' => 'Get the remote time of the retrieved document',
  ),
  'ssl_verify_result' => 
  array (
    'name' => 'ssl_verify_result',
    'option' => 'CURLINFO_SSL_VERIFYRESULT',
    'optionValue' => 2097165,
    'desciption' => 'Get the result of the certificate verification',
  ),
  'redirect_count' => 
  array (
    'name' => 'redirect_count',
    'option' => 'CURLINFO_REDIRECT_COUNT',
    'optionValue' => 2097172,
    'desciption' => 'Get the number of redirects',
  ),
  'total_time' => 
  array (
    'name' => 'total_time',
    'option' => 'CURLINFO_TOTAL_TIME',
    'optionValue' => 3145731,
    'desciption' => 'Get total time of previous transfer',
  ),
  'namelookup_time' => 
  array (
    'name' => 'namelookup_time',
    'option' => 'CURLINFO_NAMELOOKUP_TIME',
    'optionValue' => 3145732,
    'desciption' => 'Get the name lookup time',
  ),
  'connect_time' => 
  array (
    'name' => 'connect_time',
    'option' => 'CURLINFO_CONNECT_TIME',
    'optionValue' => 3145733,
    'desciption' => 'Get the time until connect',
  ),
  'pretransfer_time' => 
  array (
    'name' => 'pretransfer_time',
    'option' => 'CURLINFO_PRETRANSFER_TIME',
    'optionValue' => 3145734,
    'desciption' => 'Get the time until the file transfer start',
  ),
  'size_upload' => 
  array (
    'name' => 'size_upload',
    'option' => 'CURLINFO_SIZE_UPLOAD',
    'optionValue' => 3145735,
    'desciption' => 'Get the number of uploaded bytes',
  ),
  'size_download' => 
  array (
    'name' => 'size_download',
    'option' => 'CURLINFO_SIZE_DOWNLOAD',
    'optionValue' => 3145736,
    'desciption' => 'Get the number of downloaded bytes',
  ),
  'speed_download' => 
  array (
    'name' => 'speed_download',
    'option' => 'CURLINFO_SPEED_DOWNLOAD',
    'optionValue' => 3145737,
    'desciption' => 'Get download speed',
  ),
  'speed_upload' => 
  array (
    'name' => 'speed_upload',
    'option' => 'CURLINFO_SPEED_UPLOAD',
    'optionValue' => 3145738,
    'desciption' => 'Get upload speed',
  ),
  'download_content_length' => 
  array (
    'name' => 'download_content_length',
    'option' => 'CURLINFO_CONTENT_LENGTH_DOWNLOAD',
    'optionValue' => 3145743,
    'desciption' => 'Get content-length of download',
  ),
  'upload_content_length' => 
  array (
    'name' => 'upload_content_length',
    'option' => 'CURLINFO_CONTENT_LENGTH_UPLOAD',
    'optionValue' => 3145744,
    'desciption' => 'Get the specified size of the upload',
  ),
  'starttransfer_time' => 
  array (
    'name' => 'starttransfer_time',
    'option' => 'CURLINFO_STARTTRANSFER_TIME',
    'optionValue' => 3145745,
    'desciption' => 'Get the time until the first byte is received',
  ),
  'redirect_time' => 
  array (
    'name' => 'redirect_time',
    'option' => 'CURLINFO_REDIRECT_TIME',
    'optionValue' => 3145747,
    'desciption' => 'Get the time for all redirection steps',
  ),
  'redirect_url' => 
  array (
    'name' => 'redirect_url',
    'option' => 'CURLINFO_REDIRECT_URL',
    'optionValue' => 1048607,
    'desciption' => 'Get the URL a redirect would go to',
  ),
  'primary_ip' => 
  array (
    'name' => 'primary_ip',
    'option' => 'CURLINFO_PRIMARY_IP',
    'optionValue' => 1048608,
    'desciption' => 'Get IP address of last connection',
  ),
  'certinfo' => 
  array (
    'name' => 'certinfo',
    'option' => 'CURLINFO_CERTINFO',
    'optionValue' => 4194338,
    'desciption' => 'Get the TLS certificate chain',
  ),
  'primary_port' => 
  array (
    'name' => 'primary_port',
    'option' => 'CURLINFO_PRIMARY_PORT',
    'optionValue' => 2097192,
    'desciption' => 'Get the latest destination port number',
  ),
  'local_ip' => 
  array (
    'name' => 'local_ip',
    'option' => 'CURLINFO_LOCAL_IP',
    'optionValue' => 1048617,
    'desciption' => 'Get local IP address of last connection',
  ),
  'local_port' => 
  array (
    'name' => 'local_port',
    'option' => 'CURLINFO_LOCAL_PORT',
    'optionValue' => 2097194,
    'desciption' => 'Get the latest local port number',
  ),
  'http_version' => 
  array (
    'name' => 'http_version',
    'option' => 'CURLINFO_HTTP_VERSION',
    'optionValue' => 2097198,
    'desciption' => 'Get the http version used in the connection',
  ),
  'protocol' => 
  array (
    'name' => 'protocol',
    'option' => 'CURLINFO_PROTOCOL',
    'optionValue' => 2097200,
    'desciption' => 'Get the protocol used in the connection',
  ),
  'ssl_verifyresult' => 
  array (
    'name' => 'ssl_verifyresult',
    'option' => 'CURLINFO_PROXY_SSL_VERIFYRESULT',
    'optionValue' => 2097199,
    'desciption' => 'Get the result of the proxy certificate verification',
  ),
  'scheme' => 
  array (
    'name' => 'scheme',
    'option' => 'CURLINFO_SCHEME',
    'optionValue' => 1048625,
    'desciption' => 'Get the URL scheme (sometimes called protocol) used in the connection',
  ),
  'appconnect_time_us' => 
  array (
    'name' => 'appconnect_time_us',
    'option' => 'CURLINFO_APPCONNECT_TIME_T',
    'optionValue' => 6291512,
    'desciption' => 'Get the time until the SSL/SSH handshake is completed',
  ),
  'connect_time_us' => 
  array (
    'name' => 'connect_time_us',
    'option' => 'CURLINFO_CONNECT_TIME_T',
    'optionValue' => 6291508,
    'desciption' => 'Get the time until connect',
  ),
  'namelookup_time_us' => 
  array (
    'name' => 'namelookup_time_us',
    'option' => 'CURLINFO_NAMELOOKUP_TIME_T',
    'optionValue' => 6291507,
    'desciption' => 'Get the name lookup time in microseconds',
  ),
  'pretransfer_time_us' => 
  array (
    'name' => 'pretransfer_time_us',
    'option' => 'CURLINFO_PRETRANSFER_TIME_T',
    'optionValue' => 6291509,
    'desciption' => 'Get the time until the file transfer start',
  ),
  'redirect_time_us' => 
  array (
    'name' => 'redirect_time_us',
    'option' => 'CURLINFO_REDIRECT_TIME_T',
    'optionValue' => 6291511,
    'desciption' => 'Get the time for all redirection steps',
  ),
  'starttransfer_time_us' => 
  array (
    'name' => 'starttransfer_time_us',
    'option' => 'CURLINFO_STARTTRANSFER_TIME_T',
    'optionValue' => 6291510,
    'desciption' => 'Get the time until the first byte is received',
  ),
  'total_time_us' => 
  array (
    'name' => 'total_time_us',
    'option' => 'CURLINFO_TOTAL_TIME_T',
    'optionValue' => 6291506,
    'desciption' => 'Get total time of previous transfer in microseconds',
  ),
)

【讨论】:

  • 这太棒了,而且比我想要的还要多。很高兴看到您实际上可以根据环境测试哪些键可用以及它们代表什么。至少我现在知道如何解释这个问题和赏金目标的关键。
  • PS:如果您向下滚动 curl_getinfo 文档,还有所有可用 ssl_verify_result 选项的列表。不错:-) php.net/manual/en/function.curl-getinfo.php#111678
【解决方案2】:

appconnect:从开始到完成与远程主机的 SSL/SSH/etc 连接/握手所用的时间(以秒为单位)

预传输:从开始到文件传输即将开始所用的时间(以秒为单位)。这包括特定于所涉及的特定协议的所有预传输命令和协商。

...好吧,我不确定其他人,你能分享你想描述的键吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 2019-03-12
    相关资源
    最近更新 更多