【发布时间】:2011-12-23 02:53:58
【问题描述】:
我正在尝试找到一种方法来测量传入或传出基于 php+apache 的 Web 应用程序的字节数。一个问题是所有 I/O 都由原生 PHP 扩展完成,该扩展将句柄传递给 built-in streams 之一:php://input 或 php://output。
我已经研究了以下替代方案:
1.) 流包装器上的 ftell 遇到this question后,我的第一个直觉是尝试在I/O操作后在流包装句柄上使用ftell;大致:
$hOutput = fopen('php://output', 'wb');
extensionDoOutput($hOutput);
$iBytesTransferred = ftell($hOutput);
这似乎适用于输入包装器,但不适用于输出(它总是从 ftell 返回零)。
2.) 附加流过滤器 非修改流过滤器似乎是一种计算通过的字节数的合理方法。但是,文档似乎 a bit lacking 并且我还没有找到一种方法来获取长度而不像示例中那样执行迭代+复制模式:
class test_filter extends php_user_filter {
public static $iTotalBytes = 0;
function filter(&$in, &$out, &$consumed, $closing) {
while ($bucket = stream_bucket_make_writeable($in)) {
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
test_filter::$iTotalBytes += $consumed;
return PSFS_PASS_ON;
}
}
stream_filter_register("test", "test_filter")
or die("Failed to register filter");
$f = fopen("php://output", "wb");
stream_filter_append($f, "test");
// do i/o
不幸的是,随着数据被复制进出扩展,这似乎会导致吞吐量显着降低 (>50%)。
3.) 实现流包装器 自定义流包装器可用于包装其他流并累积读取/写入的字节数:
class wrapper {
var $position;
var $handle;
function stream_open($path, $mode, $options, &$opened_path)
{
$this->position = 0;
...
$this->handle = fopen($opened_path, $mode);
return $this->handle != false;
}
function stream_read($count)
{
$ret = fread($this->handle, $count);
$this->position += strlen($ret);
return $ret;
}
function stream_write($data)
{
$written = fwrite($this->handle, $data);
$this->position += $written;
return $written;
}
function stream_tell()
{
return $this->position;
}
function stream_eof()
{
return feof($this->handle);
}
...
}
stream_wrapper_register("test", "wrapper")
or die("Failed to register protocol");
$hOutput = fopen('test://output', 'wb');
extensionDoOutput($hOutput);
$iBytesTransferred = ftell($hOutput);
同样,这会降低吞吐量(约 20% 的输出,更大的输入)
4.) 带有回调的输出缓冲 可以使用ob_start 提供回调,以便在刷新输出块时调用。
$totalBytes = 0;
function cb($strBuffer) {
global $totalBytes;
$totalBytes += strlen($strBuffer);
return $strBuffer;
}
$f = fopen("php://output", "wb");
ob_start('cb', 16384);
// do output...
fclose($f);
ob_end_flush();
同样,这可行,但由于缓冲而产生了一定的吞吐量性能损失 (~25%)。
选项 #1 已被放弃,因为它似乎不适用于输出。在其余三个中,所有功能都正常工作,但由于缓冲区/复制机制而对吞吐量产生负面影响。
我可以使用 PHP(或 apache 服务器扩展)的固有特性来优雅地执行此操作,还是我需要在性能上硬着头皮?我欢迎任何关于如何实现这一点的想法。 (注意:如果可能的话,我对 PHP 应用程序级解决方案感兴趣......不是 apache 模块)
【问题讨论】:
-
您在问题中写的 “原生 PHP 扩展” 的名称是什么?
-
我建议你坚持使用
ftell作为输入,如果它有效的话——你不可能找到更好的东西,当然任何对输入和输出都有效的东西。我很想知道a)在写入php://output流时输出缓冲是否有效(可能应该),以及b)缓冲整个事物会降低多少性能,然后执行$bytesOut = ob_get_length(); ob_end_flush();- 我排序怀疑这会严重损害性能,但可能值得一试。 -
@hakre:这是一个自定义扩展。不公开,但也不是修改的候选者(这就是我在这个级别询问的原因)。我检查了界面,没有办法从扩展中获取此信息。
-
@DaveRandom:感谢您的想法。我同意输入。 a.) 它确实 b.) 这个应用程序缓冲整个输出并不总是可行的