反思作为关键监控工具的服务器定时问题
在HTTP头的世界里,有一个头我认为应该得到更多的时间,那就是Server-Timing 头。对我来说,这是在任何有真实用户监控(RUM)的项目中必须使用的。令我惊讶的是,网络性能监控的对话很少涉及到Server-Timing ,或者对其应用的理解非常肤浅--尽管它已经存在了很多年。
部分原因是由于人们认为它只用于跟踪服务器上的时间的局限性--它可以提供更多的价值!让我们重新思考一下我们如何利用这个表头。在这篇文章中,我们将更深入地展示Server-Timing 头部的独特功能,通过用这个头来解决具有挑战性的监控问题来展示一些实际的例子,并通过将这个技术与服务工作者相结合来激发一些创造性的灵感。
Server-Timing 头是独一无二的强大,因为它是唯一支持为特定资源设置自由格式值的HTTP响应头,并使它们可以从独立于请求/响应引用本身的JavaScript浏览器API中访问。这允许资源请求,包括HTML文档本身,在其生命周期内被充实数据,并且这些信息可以被检查以测量该资源的属性
唯一与这种能力接近的其他头是HTTP Set-Cookie/ Cookie标头。与Cookie 头信息不同的是,Server-Timing 只在特定资源的响应上,而Cookies 在所有资源的请求和响应上发送,在它们被设置和未到期时。将这些数据绑定到单一的资源响应上是比较好的,因为它可以防止关于所有响应的短暂数据变得模糊不清,并有助于在页面加载期间为其余资源发送越来越多的cookies。
设置Server-Timing
这个头可以设置在任何网络资源的响应上,如XHR、fetch、图像、HTML、样式表等。任何服务器或代理都可以在请求中添加这个头,以提供可检查的数据。该标头是通过一个带有可选的描述和/或度量值的名称构建的。唯一需要的字段是名称。此外,在同一个响应中可以设置许多Server-Timing 头信息,这些头信息将被合并,并通过逗号分开。
几个简单的例子:
Server-Timing: cdn_process;desc=”cach_hit";dur=123
Server-Timing: cdn_process;desc=”cach_hit", server_process; dur=42;
Server-Timing: cdn_cache_hit
Server-Timing: cdn_cache_hit; dur=123
重要提示:对于跨源资源,Server-Timing 和其他潜在的敏感时间值不会暴露给消费者。为了允许这些功能,我们还需要包括我们的起源或* 值的Timing-Allow-Origin 头。
对于这篇文章,这就是我们开始暴露值的全部需要,并留下其他更具体的文章来深入研究。MDN文档。
跳转后有更多内容!在下面继续阅读 ↓
消耗Server-Timing
网络浏览器暴露了一个全局性能时间线API,以检查在页面生命周期内发生的特定指标/事件的细节。从这个API中,我们可以访问内置的性能API扩展,这些扩展以下列形式暴露了时间线 PerformanceEntries.
有一些不同的条目子类型,但在本文的范围内,我们将关注PerformanceResourceTiming 和PerformanceNavigationTiming 子类型。这些子类型是目前唯一与网络请求有关的子类型,因此暴露了Server-Timing 信息。
对于顶级的HTML文档,它是在用户导航时获取的,但仍然是一个资源请求。因此,PerformanceNavigationTiming ,而不是为导航和资源方面设置不同的PerformanceEntries ,而是提供资源加载数据以及额外的导航特定数据。因为我们看的只是资源加载数据,所以我们将专门把请求(导航文档或其他)简单地称为资源。
为了查询性能条目,我们有3个可以调用的API。performance.getEntries(),performance.getEntriesByType(),performance.getEntriesByName() 。每个API都会返回一个性能条目数组,其具体内容越来越多。
const navResources = performance.getEntriesByType('navigation');
const allOtherResources = performance.getEntriesByType('resource');
最后,这些资源中的每一个都会有一个serverTiming 字段,这是一个从Server-Timing 头部提供的信息中映射出来的对象数组,其中 PerformanceEntryServerTiming是支持的(见下面的注意事项)。这个数组中的对象的形状是由接口定义的。 PerformanceEntryServerTiming接口定义的,它基本上映射了各自的Server-Timing 头部的度量选项。name,description, 和duration 。
让我们在一个完整的例子中看看这个。
向我们的数据端点发出了一个请求,在头信息中,我们发回了以下内容:
Server-Timing: lookup_time; dur=42, db_cache; desc=”hit”;
在客户端,让我们假设这是我们在这个页面上加载的唯一资源:
const dataEndpointEntry = performance.getEntriesByName('resource')[0];
console.log( dataEndpointEntry.serverTiming );
// outputs:
// [
// { name: “lookup_time”, description: undefined, duration: 42 },
// { name: “db_cache”, description:”hit”, duration: 0.0 },
// ]
这涵盖了用于访问资源条目的基本API,以及从Server-Timing 标头提供的信息。关于这些API的更多细节的链接,见底部的资源部分。
现在我们已经掌握了如何设置和使用这个标头/API组合的基本原理,让我们深入研究一下有趣的东西。
这不仅仅是时间问题
从我与其他开发者的对话和工作中,"服务器计时 "这个名字给人留下了强烈的印象,即这是一个用来跟踪时间跨度或专门跟踪时间跨度的细节的工具。这个名字和这个功能的意图是完全合理的。然而,这个标头的规范非常灵活;允许数值和表达的信息可能与时间或性能没有任何关系。甚至duration 字段也没有预定的测量单位--你可以在该字段中输入任何数字(双倍)。退一步讲,意识到可用的字段与特定类型的数据没有特殊的绑定关系,我们可以看到这种技术也是一种有效的传递机制,用于任何任意的数据,允许许多有趣的可能性。
你可以发送非时间信息的例子。HTTP响应状态代码、区域、请求ID等--任何适合你需要的自由形式的数据。在某些情况下,我们可能会发送多余的信息,这些信息可能已经在其他头文件中出现了,但这也没关系。正如我们将介绍的那样,访问其他头信息以获取资源往往是不可能的,如果它有监控价值,那么它是多余的就可以了。
不需要引用
由于网络浏览器API的设计,目前没有任何机制可以在事后查询请求及其相关响应。这一点很重要,因为需要管理内存。要读取关于一个请求或其各自的响应的信息,我们必须对这些对象有一个直接的引用。所有与我们合作的网络性能监控软件都提供了RUM客户端,在页面上放置了额外的猴子补丁层,以保持对正在发出的请求或回来的响应的直接访问。这就是他们如何提供对所有请求的落地监测,而不需要我们改变我们的代码来监测一个请求。这也是为什么这些客户端要求我们把客户端放在我们想要监控的任何请求之前。对所有不同的网络API及其链接功能进行修补的复杂性很快就会变得非常复杂。如果有一个简单的访问机制来拉取关于一个请求的相关资源/请求信息,我们当然更愿意在监控方面这样做。
更加困难的是,这种猴子打补丁的模式只适用于直接用JavaScript来启动网络的资源。对于图片、样式表、JS文件、HTML文件等,监控请求/响应细节的方法非常有限,因为通常没有直接的参考。
这就是性能时间线API提供巨大价值的地方。正如我们前面所看到的,它很明显是一个所发出的请求的列表,以及分别关于每个请求的一些数据。每个性能条目的数据非常少,几乎完全限于时间信息和一些字段,根据它们的价值,这些字段将影响一个资源的性能相对于其他资源的衡量方式。在计时字段中,我们可以直接访问serverTiming 数据。
把所有的碎片放在一起,资源在其网络响应中可以有Server-Timing headers,其中包含任意的数据。然后,这些资源可以很容易地被查询,并且可以访问Server-Timing 数据,而不需要直接引用请求/响应本身。有了这个,你能否访问/管理一个资源的引用并不重要,所有的资源都可以用任意的数据来充实,可以从一个易于使用的网络浏览器API中访问。这是一个非常独特和强大的能力!
接下来,让我们把这种模式应用于一些传统上难以衡量的挑战。
解决方案1:检查图像和其他资产的反应
图像、样式表、JavaScript文件等通常不是通过使用直接引用网络API的信息来创建的,而是通过这些请求的信息来创建的。例如,我们几乎总是通过在HTML中放置一个img 元素来触发图片下载。有一些加载这些资产的技术,需要使用JavaScriptfetch/xhr APIs来拉取数据,并直接将其推送到资产引用中。虽然这种替代技术使它们更容易被监控,但在大多数情况下,它对性能是灾难性的。挑战是我们如何在没有直接网络API引用的情况下检查这些资源?
为了将其与现实世界的用例联系起来,重要的是要问为什么我们要检查和捕获这些资源的响应信息?这里有几个原因:
- 例如,丢失图片(404)与处理返回服务器错误(500)的图片可能是完全不同的问题和工作类型。
- 通常,团队将这些类型的资产卸载到CDN来存储和交付给用户。如果他们有问题,团队如何快速检测到问题?
- 资源的运行时间或按需变化已经成为更普遍的技术
例如,图像大小的调整,CDN上脚本的自动填充等--这些系统可能有许多限制和原因,它们可能无法创建或交付一个变化。如果你期望100%的用户都能检索到某一类型的资产变体,那么能够确认这一点是很有价值的。
我以前工作的一家公司就出现了这种情况,按需调整图像的大小被用于缩略图。由于供应商的限制,大量的用户会因为在应该出现缩略图的地方加载全尺寸的图片而得到更糟糕的体验。因此,在我们认为>99%的用户会得到最佳图像的地方,>30%的用户会遇到性能问题,因为图像没有调整大小。
现在我们对可能促使我们检查这些资源的原因有了一些了解,让我们看看如何利用Server-Timing 来进行检查。
图像HTML:
<img src="/user-rsrc/12345?resize=true&height=80&width=80&format=webp" alt="..."/>
图像响应头信息:
Status: 200
…
Server-Timing: status_code; dur=200;, resizing; desc=”failed”; dur=1200; req_id; desc=”zyx4321”
检查图像响应信息:
const imgPerfEntry = performance.getEntriesByName('/user-rsrc/12345?resize=true&height=80&width=80&format=webp')[0];
// filter/capture entry data as needed
console.log(imgPerfEntry.serverTiming);
// outputs:
// [
// { name: “status_code”, description: undefined, duration: 200 },
// { name: “resizing”, description:”failed”, duration: 1200 },
// { name: “req_id”, description:”zyx4321”, duration: 0.0 },
// ]
这个指标非常有价值,因为尽管返回了 "愉快的 "响应(200s),但我们的图像没有调整大小,而且可能没有转换为正确的格式,等等。除了下载时间等其他性能信息外,我们看到状态是200 (没有触发元素上的onerror处理程序),在花了1.2s 试图调整大小后,调整失败了,而且我们有一个request-id,可以用来在其他工具中调试这个。通过向我们的RUM提供者发送这些数据,我们可以汇总并主动监测这些情况的发生频率。
解决方案2:检查JS运行前返回的资源
用于监控资源的代码(fetch、XHR、图像、样式表、脚本、HTML等)需要JavaScript代码来聚合,然后将信息发送到某个地方。这几乎总是意味着监测代码要在被监测的资源之前运行,这是一种期望。前面介绍的用于自动监测获取请求的基本猴子补丁的例子就是一个很好的例子。这段代码必须在任何需要被监控的fetch请求之前运行。然而,在很多情况下,从性能到技术限制,我们可能无法或根本不应该改变资源的请求顺序,以使其更容易被监控。
另一个非常常见的监测技术是在页面上放置事件监听器,以捕获可能具有监测价值的事件。这通常以元素上的onload 或onerror 处理程序的形式出现,或者更抽象地使用addEventListener 。这种技术要求JS在事件发生之前,或者在监听器本身被连接之前就已经设置好了。因此,这种方法仍然带有这样的特点,即在监控JS运行后,只对事件进行监控,因此要求JS在需要测量的资源之前执行。
将此映射到现实世界的使用案例中,电子商务网站非常强调 "折叠上方 "内容的快速渲染--通常是尽可能地推迟JS。也就是说,可能有一些资源是需要测量的,比如成功交付产品图片。在其他情况下,我们也可能决定,由于页面重量,监控库本身不应该在关键路径中。有什么办法可以追溯性地检查这些请求?
该技术与解决方案#1相同!这是有可能的,因为浏览器会自动维护一个所有性能条目的缓冲区(受缓冲区大小限制,可以改变)。这使得我们可以将JS推迟到页面加载周期的后期,而不需要在资源的前面添加监听器。
与其重复解决方案#1的例子,不如让我们看看追溯和未来检查性能条目的情况,以显示它们可以被利用的不同之处。请注意,虽然我们在这些例子中检查的是图像,但我们可以为任何资源类型做这个。
为这段代码设置上下文,我们的需求是,我们必须确保我们的产品图片被成功交付。让我们假设所有的网站图像都返回这个Server-Timing 头部结构。我们的一些重要的图片可以发生在我们的监控脚本之前,而随着用户的浏览,更多的图片将继续加载。我们如何处理这两者?
图像响应头:
Status: 200
…
Server-Timing: status_code; dur=200;, resizing; desc="success"; dur=30; req_id; desc="randomId"
我们的监控逻辑。我们希望这个在页面的关键路径内容之后运行。
检查图像响应信息:
function monitorImages(perfEntries){
perfEntries.forEach((perfEntry)=>{
// monitoring for the performance entries
console.log(perfEntry.serverTiming);
})
}
const alreadyLoadedImageEntries = performance.getEntriesByType('resource').filter(({ initiatorType })=> initiatorType === 'img');
monitorImages( alreadyLoadedImageEntries );
const imgObserver = new PerformanceObserver(function(entriesList) {
const newlyLoadedImageEntries = entriesList.getEntriesByType('resource').filter(({ initiatorType })=> initiatorType === 'img');
monitorImages( newlyLoadedImageEntries );
});
imgObserver.observe({entryTypes: ["resource"]});
尽管我们的监控脚本推迟到了关键路径之外,但我们正在捕获所有在我们的脚本之前加载的图像的数据,并将继续监控它们,因为用户继续使用网站。
解决方案3:检查HTML文件
我们要看的最后一个解决方案的例子与最终的 "JS运行前 "资源有关--HTML文档本身。如果我们的监控方案是通过HTML加载JS,那么我们如何监控HTML文档的交付?
在监控HTML文档的交付方面有一些先例。对于监控响应数据,最常见的设置是使用服务器日志/指标/跟踪来捕获这些信息。这是一个很好的解决方案,但是根据工具的不同,数据可能与RUM数据脱钩,导致我们需要多种工具来检查我们的用户体验。此外,这种做法还可能会错过元数据(例如页面实例标识符),这些元数据允许我们对特定页面加载的信息进行汇总和关联--例如,当文档返回某些文档响应代码时,关联异步请求失败。
做这项工作的一个常见模式是把内容放在HTML内容本身里面。这必须放在HTML内容中,因为基于JS的监控逻辑无法访问之前的HTML请求头。这就把我们的HTML文档变成了一个动态的文档内容。这对我们的需求来说可能没有问题,并允许我们采取该信息并提供给我们的RUM工具。但是,如果我们的HTML传输系统不在我们的控制范围内,或者系统对HTML的传输方式有一些假设,这就会成为一个挑战。这方面的例子可能是,期望HTML是完全静态的,这样我们就能以某种确定的方式对其进行下游缓存--"部分动态 "的HTML体更有可能被缓存逻辑错误地处理。
在HTML的交付过程中,也可能有我们想要了解的额外数据,比如在整个链条中哪些数据中心处理了请求。我们可能有一个CDN边缘处理程序,代理来自原点的请求。在这种情况下,我们不能指望每一层都能/应该处理和注入HTML内容。Server-Timing 头信息如何帮助我们?
在解决方案1和解决方案2的概念基础上,我们可以在这里捕获关于HTML文档本身的有价值的数据。请记住,堆栈的任何部分都可以在响应中添加一个Server-Timing 头,它将在最终的头值中被连在一起。
让我们假设我们有一个CDN边缘处理程序和一个可以处理该文档的原点。
CDN添加了响应头:
Status: 200
…
Server-Timing: cdn_status_code; dur=200;, cdn_cache; desc=”expired”; dur=15; cdn_datacenter; desc=”ATL”; cdn_req_id; desc=”zyx321abc789”; cdn_time; dur=120;
原点添加了响应头信息:
Status: 200
…
Server-Timing: origin_status_code; dur=200;, origin_time; dur=30; origin_region; desc=”us-west”; origin_req_id; desc="qwerty321ytrewq789";
检查HTML响应信息:
// as mentioned earlier, the HTML document is a 'navigation' type of Performance Entry
// that has a superset of information related to the resource and the navigation-specific info
const htmlPerfEntry = performance.getEntriesByType('navigation')[0];
// filter/capture entry data as needed
console.log(htmlPerfEntry.serverTiming);
// outputs:
// [
// { name: “cdn_status_code”, description: undefined, duration: 200 },
// { name: “cdn_cache”, description:”expired”, duration: 0.0},
// { name: “cdn_datacenter”, description:”ATL”, duration: 0.0 },
// { name: “cdn_req_id”, description:”zyx321abc789”, duration: 0.0 },
// { name: “cdn_time”, description: undefined, duration: 120 },
// { name: “origin_status_code”, description: undefined, duration: 200 },
// { name: “origin_time”, description: undefined, duration: 30 },
// { name: “origin_region”, description:”us-west”, duration: 0.0 },
// { name: “origin_req_id”, description:”qwerty321ytrewq789”, duration: 0.0 },
// ]
从这些信息中,我们的监控JavaScript(可能是后来加载的)可以汇总HTML处理发生的位置、来自不同服务器的状态代码(可能因为合法的原因而不同--或者是错误),以及请求标识符(如果他们需要将其与服务器日志联系起来)。它还知道通过cdn_time 持续时间在 "服务器 "上花费了多少时间--"服务器 "时间是指从我们提供的第一个非用户代理/服务器开始的总时间。使用该cdn_time ,已经可以访问的HTML第一字节时间值和origin_time ,我们可以更准确地确定延迟部分,如用户延迟,cdn 到原点的延迟等。这在优化这样一个关键的交付点和保护它不被退回方面是非常强大的。
将服务器定时与服务工作者相结合
服务工作者是由网站初始化的脚本,位于网站、浏览器和网络(如果有的话)之间。当作为代理时,它们可以被用来读取和修改来自网站的请求和返回网站的响应。鉴于服务工作者的功能如此丰富,我们不会试图在本文中深入介绍它们--简单的网络搜索将产生大量关于其功能的信息。在这篇文章中,我们将重点讨论服务工作者的代理能力--它处理请求/响应的能力。
结合这些工具的关键是知道Server-Timing 头和其各自的PerformanceEntry 是在服务工作者代理发生后计算的。这允许我们使用服务工作者在响应中添加Server-Timing 标头,这些标头可以提供关于请求本身的有价值信息。
我们可能想在服务工作者中捕获什么类型的信息?如前所述,服务工作者有很多能力,其中任何一个动作都可以产生有价值的东西来捕获。以下是我想到的一些情况:
- 这个请求是由服务工作者的缓存提供的吗?
- 这个请求是在离线时从服务工作者那里得到的吗?
- 对于这种请求类型,正在使用哪种服务工作者策略?
- 使用的是什么版本的服务工作者?
这有助于检查我们对服务工作者失效的假设。 - 从其他头信息中获取数值,并将其放入
Server-Timing头信息中,以便进行下游聚合。
当我们不能选择改变请求中的头信息,但希望在RUM中检查它们时,这很有价值--CDN提供商通常就是这种情况。 - 一个资源在服务工作者的缓存中已经有多长时间了?
服务工作者必须在网站上进行初始化,这本身就是一个异步过程。此外,服务工作者只处理定义范围内的请求。因此,即使是 "这个请求是由服务工作者处理的吗?"这样的基本问题也可以推动有趣的对话,了解我们在多大程度上依靠服务工作者的能力来推动伟大的体验。
让我们深入了解这在代码中是如何体现的。
网站上用于初始化服务工作者的基本JS逻辑:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function (registration) {
registration.update(); // immediately start using this sw
});
}
在/service-worker.js ,基本的请求/响应代理:
const CACHE_NAME = 'sw-cached-files-v1';
self.addEventListener('fetch', function (event) {
event.respondWith(
// check to see if this request is cached
caches.match(event.request)
.then(function (response) {
// Cache hit - return response
if (response) {
const updatedHeaders = new Headers(response.headers);
updatedHeaders.append('Server-Timing', 'sw_cache; desc="hit";');
const updatedResponse = new Response(response.body, {
...response,
headers: updatedHeaders
});
return updatedResponse;
}
return fetch(event.request).then(function (response) {
// depending on the scope where we load our service worker,
// we might need to filter our responses to only process our
// first-party requests/responses
// Regex match on the event.request.url hostname should
const updatedHeaders = new Headers(response.headers);
updatedHeaders.append('Server-Timing', `status_code;desc=${response.status};, sw_cache; desc="miss";`)
const modifiableResponse = new Response(response.body, {
...response,
headers: updatedHeaders
});
// only cache known good state responses
if (!response || response.status !== 200 || response.type !== 'basic' || response.headers.get('Content-Type').includes('text/html')) {
return modifiableResponse;
}
const responseToCache = modifiableResponse.clone();
caches.open(CACHE_NAME).then(function (cache) {
cache.put(event.request, responseToCache);
});
return modifiableResponse;
}
);
})
);
});
从服务工处理的请求,现在将有一个Server-Timing 头附加到他们的响应上。这使得我们可以通过性能时间线API检查这些数据,正如我们在之前的所有例子中所展示的那样。在实践中,我们很可能不是为了这个单一的需求而添加服务工作者的--这意味着我们已经有了处理请求的工具。在2个地方添加一个头,使我们能够测量所有请求的状态代码,基于服务工作者的缓存命中率,以及服务工作者处理请求的频率。
如果我们有服务工作者,为什么要使用Server-Timing ?
这是在讨论结合这些技术时出现的一个重要问题。如果服务工作者可以抓取所有的头和内容信息,为什么我们还需要一个不同的工具来汇总这些信息?
测量时间和其他关于请求的任意元数据的工作几乎都是,这样我们就可以将这些信息发送给RUM提供商进行分析、警报等。所有主要的RUM客户端都有1或2个窗口,我们可以充实关于请求的数据--何时发生响应,以及何时检测到PerformanceEntry 。例如,如果我们提出一个获取请求,RUM客户端会捕捉到请求/响应的细节并发送出去。如果观察到一个PerformanceEntry ,客户端也会发送该信息--如果可能的话,试图将其与之前的请求联系起来。如果RUM客户端提供了添加该请求/条目信息的能力,这些是唯一可以做的窗口。
在实践中,一个服务工作者可能还没有被激活,一个请求/响应可能还没有处理服务工作者,所有服务工作者的数据共享都需要通过postMessage() API向网站发送异步信息。所有这些方面都引入了竞赛条件,使服务工作者处于活动状态,能够捕获数据,然后及时发送数据,以便由RUM客户端进行充实。
与Server-Timing 相比,处理性能时间线API的RUM客户端将立即访问Server-Timing 上的任何数据集PerformanceEntry 。
鉴于对服务工作者在可靠地丰富请求/响应数据方面的挑战的评估,我的建议是,服务工作者被用来提供更多的数据和背景,而不是作为在主线程上向RUM客户端提供数据的唯一机制。也就是说,使用Server-Timing ,并在需要时,使用服务工作者来增加更多的上下文,或者在不支持Server-Timing 的情况下--如果需要的话。在这种情况下,我们可能会创建自定义事件/度量,而不是丰富原始的请求/响应数据聚合,因为我们将假设提到的竞赛条件将导致错过一般RUM客户端丰富的窗口。
Server-Timing 使用的考虑因素
尽管它有独特的强大功能,但也不是没有重要的考虑因素。下面是在写这篇文章时基于当前实现的注意事项清单:
- 浏览器支持- Safari不支持将
Server-Timing数据放到性能时间线API中(他们确实在DevTools中显示了它)。
然而,这是一个耻辱,鉴于这不是关于用户的功能,而是关于改进性能监测的能力 - 我赞成这不是一个阻塞问题。对于基于浏览器的监控,我们从来没有期望测量100%的浏览器/用户。目前,这意味着我们希望得到基于全球浏览器使用数据的~70-75%的支持。这通常足以让我们确信,我们的指标正在向我们显示关于健康和性能或我们系统的良好信号。如前所述,Server-Timing有时是可靠地获得这些指标的唯一途径,所以我们应该对利用这个工具感到有信心。
如前所述,如果我们绝对必须拥有Safari的这些数据,我们可以探索对Safari用户使用基于cookie的解决方案。这里的任何解决方案都必须经过大量的测试,以确保它们不会妨碍性能。 - 如果我们想提高性能,我们要避免给我们的响应增加大量的重量,包括头信息。这是一个额外的权重与增值的元数据的权衡。我的建议是,如果你的
Server-Timing头部不在500字节以上的范围内,我不会担心。如果你担心,可以尝试不同的长度并测量其影响 - 当在一个响应上附加多个
Server-Timing标头时,有可能会出现重复的Server-Timing度量名称。浏览器会在PerformanceEntry上的serverTiming数组中浮现所有的名称。最好是通过特定的或命名的方式来确保避免这种情况。如果不能避免,那么我们会分解添加每个标头的事件的顺序,并定义一个我们可以信任的约定。否则,我们可以创建一个工具,不盲目地添加Server-Timing,但也会更新现有的条目,如果它们已经在响应中。 - 尽量避免误记响应也会缓存
Server-Timing值的错误。在某些情况下,你可能想过滤掉缓存响应的时间相关数据,在它们被缓存之前,它们在服务器上花费了时间。有不同的方法来检测请求是否去了网络上的数据PerformanceEntry,如entry.transferSize > 0,或entry.decodedBodySize > 0,或entry.duration > 40。我们也可以靠我们所学到的Server-Timing,在头文件上设置一个时间戳进行比较。
总结
我们已经深入研究了Server-Timing 头的应用,这些应用并不与该头通常所涉及的 "时间 "使用情况相一致。我们已经看到了它的威力,它可以添加关于资源的自由形式的数据,并在不需要引用用于制造数据的网络API的情况下访问这些数据。这是一种非常独特的能力,我们利用它来测量各种类型的资源,对它们进行追溯检查,甚至捕获关于HTML文档本身的数据。将这种技术与服务工作者相结合,我们可以从服务工作者本身添加更多的信息,或者将不受控制的服务器响应信息映射到Server-Timing ,以便于访问。
我相信Server-Timing ,它的独特之处令人印象深刻,应该被更多地使用,但我也相信,它不应该被用于所有事情。在过去,这一直是我所做的性能仪表项目的必备工具,以提供无法访问的资源数据,并确定哪里发生了延迟。如果你没有从拥有这个头的数据中获得价值,或者它不适合你的需要--就没有理由使用它。这篇文章的目的是为你提供一个新的视角,即Server-Timing ,作为一个工具,即使你不测量时间,也可以达到。