应对过度消耗资源的广告的干预

罗恩·梅伍德 (Rowan Merewood)
Rowan Merewood

广告在设备上消耗过多资源,会对用户体验产生负面影响,包括性能下降的明显影响和不太明显的后果,例如耗尽电池电量或耗尽带宽限额。这些广告不一而足,既有主动恶意的恶意内容(例如加密货币矿工),也包括存在无意中 bug 或性能问题的真实内容。

Chrome 针对广告可以使用的资源设置了限制,并在超出限制时卸载该广告。您可以阅读 Chromium 博客上的公告,了解更多详情。用于卸载广告的机制是密集型广告干预

密集型广告条件

如果用户未与广告互动(例如,没有点按或点击),并且满足以下任一条件,则广告被视为重型广告:

  • 使用主线程的总时长超过 60 秒
  • 在任意 30 秒窗口中使用主线程的时间超过 15 秒
  • 使用超过 4 MB 的网络带宽

广告框架的任何后代 iframe 使用的所有资源都会计入干预该广告的限制。请务必注意,主线程时间限制与自加载广告以来经过的时间不同。该限制针对的是 CPU 执行广告代码所需的时间。

测试干预措施

这项干预措施包含在 Chrome 85 中,但默认情况下,为了保护用户隐私,我们在阈值中添加了一些噪声和可变性。

chrome://flags/#heavy-ad-privacy-mitigations 设置为已停用会移除这些保护措施,这意味着系统完全根据限制来确定性地应用限制。这样应该可以简化调试和测试。

干预触发后,您应该会看到大量广告的内容显示在 iframe 中,替换为广告已移除消息。如果您点击随附的详细信息链接,就会看到一条内容为“此广告为您的设备使用的资源过多,因此 Chrome 移除了该广告”的消息。

您可以访问 heavy-ads.glitch.me,了解我们对示例内容采取的干预措施。您还可以使用此测试网站加载任意网址,以便快速测试您自己的内容。

测试时请注意,多种原因可能会导致无法应用某项干预。

  • 在同一网页中重新加载同一广告会使该组合免于干预。在此处,清除浏览记录并使用新标记打开网页可助您一臂之力。
  • 确保该网页始终获得焦点 - 该网页在后台运行(切换到另一个窗口)会暂停该网页的任务队列,因此不会触发 CPU 干预。
  • 确保您在测试时没有点按或点击广告内容,这项干预措施不会应用于收到任何用户互动的内容。

您需要做些什么?

您在自己的网站上展示来自第三方提供商的广告

您无需采取任何措施,只是要注意,用户在您的网站上看到的广告可能会超出已移除的限制。

您在自己的网站上展示第一方广告或为第三方展示广告提供广告

请继续阅读,确保您通过 Reporting API 针对大量广告干预措施实施必要的监控措施。

您制作广告内容,或维护广告内容工具

请继续阅读,确保您了解如何测试内容是否存在性能和资源使用问题。您还应参阅所选广告平台上的相关指南,因为这些平台可能会提供其他技术建议或限制,例如,请参阅 Google 展示广告素材指南。考虑直接在制作工具中构建可配置的阈值,以防止效果不佳的广告四处流传。

广告移除后会出现什么情况?

Chrome 中的一项干预通过适当命名的 Reporting API 进行报告,其报告类型为 intervention。您可以使用 Reporting API 通过向报告端点发出的 POST 请求或在 JavaScript 中接收有关干预的通知。

这些报告会在已标记广告的根 iframe 及其所有后代(即干预卸载的每个帧)上触发。这意味着,如果广告来自第三方来源(即跨网站 iframe),则由第三方(例如广告提供商)负责处理报告。

若要配置 HTTP 报告页面,响应应包含 Report-To 标头:

Report-To: { "url": "https://example.com/reports", "max_age": 86400 }

触发的 POST 请求将包含如下报告:

POST /reports HTTP/1.1
Host: example.com
…
Content-Type: application/report

[{
 "type": "intervention",
 "age": 60,
 "url": "https://example.com/url/of/ad.html",
 "body": {
   "sourceFile": null,
   "lineNumber": null,
   "columnNumber": null,
   "id": "HeavyAdIntervention",
   "message": "Ad was removed because its CPU usage exceeded the limit. See https://www.chromestatus.com/feature/4800491902992384"
 }
}]

JavaScript API 为 ReportingObserver 提供了 observe() 方法,该方法可用于针对干预触发所提供的回调。如果您想向报告附加其他信息以帮助调试,上述做法会很有用。

// callback that will handle intervention reports
function sendReports(reports) {
  for (let report of reports) {
    // Log the `report` json via your own reporting process
    navigator.sendBeacon('https://report.example/your-endpoint', report);
  }
}

// create the observer with the callback
const observer = new ReportingObserver(
  (reports, observer) => {
    sendReports(reports);
  },
  { buffered: true }
);

// start watching for interventions
observer.observe();

但是,由于干预会确实从 iframe 中移除网页,因此您应添加故障安全机制,以确保系统可以在网页完全消失之前捕获报告(例如,iframe 中的广告)。为此,您可以将同一回调挂接到 pagehide 事件。

window.addEventListener('pagehide', (event) => {
  // pull all pending reports from the queue
  let reports = observer.takeRecords();
  sendReports(reports);
});

请注意,为了保护用户体验,pagehide 事件会限制其中可以执行的工作量。例如,如果尝试随报告发送 fetch() 请求,则会导致该请求被取消。您应该使用 navigator.sendBeacon() 发送该报告,即便如此,浏览器也只能尽力而为,无法保证这种行为。

JavaScript 生成的 JSON 与 POST 请求发送的 JSON 类似:

[
  {
    type: 'intervention',
    url: 'https://example.com/url/of/ad.html',
    body: {
      sourceFile: null,
      lineNumber: null,
      columnNumber: null,
      id: 'HeavyAdIntervention',
      message:
        'Ad was removed because its network usage exceeded the limit. See https://www.chromestatus.com/feature/4800491902992384',
    },
  },
];

诊断干预措施的原因

广告内容就是 Web 内容,因此请使用 Lighthouse 等工具审核内容的整体效果。审核结果会提供改进方面的内嵌指导。您也可以参考 web.dev/fast 集合。

您可能会发现在更独立的环境中测试广告很有帮助。您可以使用 https://heavy-ads.glitch.me 上的自定义网址选项,使用现成且带有广告标记的 iframe 对其进行测试。您可以使用 Chrome 开发者工具验证内容是否已标记为广告。在呈现面板(可通过三点状 菜单以及更多工具 > 呈现进行访问)中选择“突出显示广告框架”。如果在顶级窗口或其他上下文中测试内容,但未将内容标记为广告,则不会触发干预,但您仍然可以根据阈值手动进行检查。

框架的广告状态也会显示在 Elements 窗格中,在该窗格中,系统会在起始 <iframe> 标记后面添加 ad 注解。这在 Frames 部分下的 Application 面板中也可见,其中由广告标记的帧将包含一个“Ad Status”属性。

网络用量

打开 Chrome 开发者工具中的 Network 面板,查看广告的整体网络活动。您需要确保勾选“停用缓存”选项,以便在重复加载时获得一致的结果。

开发者工具中的 Network 面板。
开发者工具中的“Network”面板

页面底部的转移值会显示针对整个页面转移的金额。考虑使用顶部的过滤条件输入,将请求限制为仅与广告相关的请求。

如果您找到广告的初始请求(例如 iframe 的来源),还可以在请求中使用发起者标签页来查看它触发的所有请求。

请求的“发起方”标签页。
请求的“发起方”标签页。

按大小对请求的总体列表进行排序是一种发现过大资源的好方法。常见原因包括未经优化的图片和视频。

按响应大小对请求进行排序。
按响应大小对请求进行排序。

此外,按名称排序也有助于发现重复的请求。这可能不是触发干预的单个大型资源,而是有大量反复超出限制的重复请求。

CPU 使用率

开发者工具中的 Performance 面板将有助于诊断 CPU 使用问题。第一步是打开“拍摄设置”菜单。使用 CPU 下拉菜单尽可能降低 CPU 速度。与高端开发机器相比,对 CPU 的干预更有可能在低功耗设备上触发。

在“性能”面板中启用网络和 CPU 节流。
在“性能”面板中启用网络和 CPU 节流。

接下来,点击 Record(记录)按钮以开始记录活动。您最好尝试一下记录的时间和时长,因为较长的跟踪记录可能需要一段时间才能加载完毕。加载录制内容后,您可以使用顶部时间轴选择录制内容的一部分。重点关注图表中表示脚本、渲染和绘制的纯黄色、紫色或绿色区域。

“Performance”面板中的跟踪记录摘要。
“性能”面板中的跟踪记录摘要。

浏览底部的 Bottom-UpCall TreeEvent Log 标签页。按自用时间总时间对这些列进行排序有助于识别代码中的瓶颈。

在“Bottom-Up”标签页中按“Self Time”排序。
在“Bottom-Up”标签页中按“Self Time”排序。

关联的源文件也链接到此处,因此您可以转到“Sources”面板查看每行的费用。

“Source”面板中显示的执行时间。
“Source”面板中显示的执行时间。

这里需要注意的常见问题是:优化不足的动画会触发所包含库内隐藏的连续布局和绘制或代价高昂的操作。

如何报告错误的干预措施

Chrome 通过将资源请求与过滤器列表进行匹配,将内容标记为广告。如果标记了非广告内容,请考虑更改该代码,以避免与过滤规则匹配。如果您怀疑某项干预措施的应用有误,可以通过此模板提出问题。请确保您已捕获干预报告的示例,并具有可重现问题的示例网址。