使用服务器计时测量性能

已发表: 2022-03-10
快速总结↬ Server Timing 标头提供了一种离散且便捷的方式,可以将后端服务器性能计时与浏览器中的开发人员工具进行通信。 将计时信息添加到您的应用程序使您能够在一个地方监控后端和前端性能。

在进行任何类型的性能优化工作时,我们学到的第一件事就是,在您提高性能之前,您必须首先对其进行测量。 如果无法衡量某项工作的速度,我们就无法判断所做的更改是否正在提高性能、没有效果,或者甚至使事情变得更糟。

我们中的许多人都熟悉在某种程度上处理性能问题。 这可能就像试图弄清楚为什么页面上的 JavaScript 没有足够快地启动,或者为什么图像在糟糕的酒店 wifi 上显示时间过长一样简单。 这类问题的答案通常可以在一个非常熟悉的地方找到:浏览器的开发人员工具。

多年来,开发人员工具已得到改进,可帮助我们解决应用程序前端的此类性能问题。 浏览器现在甚至内置了性能审计。这可以帮助追踪前端问题,但是这些审计可以显示另一个我们无法在浏览器中修复的缓慢来源。 这个问题是服务器响应时间慢。

“第一个字节的时间”

几乎没有浏览器优化可以改善在服务器上构建速度很慢的页面。 该成本是在浏览器发出文件请求和接收响应之间产生的。 在开发人员工具中研究您的网络瀑布图将在“等待(TTFB)”类别下显示此延迟。 这是浏览器在发出请求和接收响应之间等待的时间。

跳跃后更多! 继续往下看↓

在性能方面,这被称为Time to First Byte - 服务器开始发送浏览器可以开始使用的内容之前所花费的时间。 包含在等待时间中的是服务器构建页面所需的一切。 对于典型的站点,这可能涉及将请求路由到应用程序的正确部分、验证请求、多次调用后端系统(如数据库等)。 它可能涉及通过模板系统运行内容,向第三方服务发出 API 调用,甚至可能涉及发送电子邮件或调整图像大小等事情。 服务器为完成请求所做的任何工作都被压缩到用户在浏览器中体验的 TTFB 等待中。

Chrome DevTools 中的 Network 面板显示了对单个页面请求的检查
检查文档请求会显示浏览器等待服务器响应所花费的时间。

那么我们如何减少时间并开始更快地将页面交付给用户呢? 嗯,这是一个大问题,答案取决于您的应用程序。 那就是性能优化本身的工作。 我们首先需要做的是衡量性能,以便可以判断任何更改的好处。

服务器计时标头

服务器计时的工作不是帮助您对服务器上的活动进行实际计时。 您需要使用后端平台提供给您的任何工具集自己进行计时。 相反,服务器计时的目的是指定如何将这些测量值传达给浏览器。

这样做的方式非常简单,对用户透明,并且对您的页面重量的影响最小。 该信息作为一组简单的 HTTP 响应标头发送。

 Server-Timing: db;dur=123, tmpl;dur=56

此示例传达名为dbtmpl的两个不同时间点。 这些不是规范的一部分——这些是我们选择的名称,在这种情况下分别代表一些数据库和模板时间。

dur属性表示操作完成所需的毫秒数。 如果我们查看开发人员工具的网络部分中的请求,我们可以看到时间已添加到图表中。

Chrome DevTools 中页面请求的 Timings 面板显示了一个新的 Server Timing 部分。
将出现一个新的 Server Timing 部分,显示使用 Server-Timing HTTP 标头设置的时间。

Server-Timing标头可以采用逗号分隔的多个指标:

 Server-Timing: metric, metric, metric

每个指标可以指定三个可能的属性

  1. 指标的短名称(例如我们示例中的db
  2. 以毫秒为单位的持续时间(表示为dur=123
  3. 描述(表示为desc="My Description"

每个属性都用分号作为分隔符分隔。 我们可以像这样向我们的示例添加描述:

 Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing"
Chrome DevTools 中页面请求的计时面板显示了用于服务器计时指标的描述。
提供时,名称将替换为描述。

唯一需要的属性是namedurdesc都是可选的,可以在需要的地方随意使用。 例如,如果您需要调试发生在一台服务器或数据中心而不是另一台服务器或数据中心上的计时问题,那么在没有相关计时的情况下将该信息添加到响应中可能会很有用。

 Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”

然后,这将与时间一起显示。

Chrome DevTools 中页面请求的计时面板显示了未设置时间的服务器计时。
显示“东海岸数据中心”值,即使它没有计时。

您可能会注意到的一件事是时间条不会以瀑布模式显示。 这仅仅是因为 Server Timing 不会尝试传达时序序列,而只是传达原始指标本身。

实现服务器计时

您自己的应用程序中的确切实现将取决于您的具体情况,但原则是相同的。 步骤总是:

  1. 时间一些操作
  2. 收集计时结果
  3. 输出 HTTP 标头

在伪代码中,响应的生成可能如下所示:

 startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())

在任何语言中,按照这些思路实现某些东西的基础都应该是直截了当的。 一个非常简单的 PHP 实现可以使用microtime()函数进行计时操作,并且可能看起来类似于以下内容。

 class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }

测试脚本将使用它如下,这里使用usleep()函数人为地在脚本运行中创建延迟,以模拟需要时间才能完成的过程。

 $Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());

运行此代码会生成一个如下所示的标头:

 Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding"
Chrome DevTools 中页面请求的计时面板显示正确显示的测试值。
示例中设置的服务器计时显示在计时面板中,并在我们的测试脚本中配置了延迟。

现有实现

考虑到 Server Timing 的方便程度,我能找到的实现相对较少。 server-timing NPM 包提供了一种在 Node 项目中使用 Server Timing 的便捷方式。

如果您使用基于中间件的 PHP 框架 tuupola/server-timing-middleware 也提供了一个方便的选项。 几个月来我一直在 Notist 的生产环境中使用它,如果你想在野外看到一个例子,我总是会启用一些基本的时间安排。

对于浏览器支持,我见过的最好的是 Chrome DevTools,这就是我在本文的屏幕截图中使用的。

注意事项

Server Timing 本身为通过网络发送回的 HTTP 响应增加了非常小的开销。 标头非常小,通常可以安全地发送,而不必担心仅针对内部用户。 即便如此,保持名称和描述简短还是值得的,这样您就不会增加不必要的开销。

更令人担忧的是您可能在服务器上为您的页面或应用程序计时所做的额外工作。 添加额外的计时和日志记录本身会对性能产生影响,因此值得实现一种在需要时打开和关闭它的方法。

使用服务器计时标头是确保应用程序前端和后端的所有计时信息都可在一个位置访问的好方法。 如果您的应用程序不太复杂,那么它可以很容易实现,并且您可以在很短的时间内启动并运行。

如果您想了解更多关于服务器时序的信息,您可以尝试以下方法:

  • W3C 服务器计时规范
  • Server Timing 上的 MDN 页面包含浏览器支持的示例和最新详细信息
  • 来自 BBC iPlayer 团队的一篇关于他们使用服务器计时的有趣文章。