提高性能

本文为您介绍一些可用来提高应用性能的方法和技巧。在一些情况下,我们会采用其他 API 或通用 API 中的示例来阐释所讲解的思路。不过,这些思路也同样适用于 DCM/DFA Reporting and Trafficking API。

使用 gzip 进行压缩

要降低单个请求的带宽需求,您可以选择启用 gzip 压缩,这是一种既便捷又简单的方法。虽然这种方法需要占用额外的 CPU 处理时间来对结果进行解压缩,但考虑到它对节约网络开销大有裨益,通常还是很值得一用的。

要接收采用 gzip 进行编码的响应,您必须采取两项措施:一是设置一个 Accept-Encoding 标头,二是修改您的用户代理,使其包含字符串 gzip。下例就是一个格式正确的 HTTP 标头,符合启用 gzip 压缩的条件:

Accept-Encoding: gzip
User-Agent: my program (gzip)

仅处理部分资源

提高 API 调用性能的另一方法是仅发送和接收您感兴趣的那部分数据。这样可以避免应用传输、解析和存储不需要的字段,使应用可以更高效地利用网络、CPU 和内存等资源。

这种非完整请求称作“部分请求”,具体有以下两种类型:

  • 部分响应:使用这种请求时,您需要指定响应中要包含哪些字段(使用 fields 请求参数)。
  • 修补:使用这种更新请求时,您仅需要发送要更改的字段(使用 PATCH HTTP 动词)。

有关发送部分请求的详情,请参阅以下各节。

部分响应

默认情况下,处理完请求之后,服务器会发回资源的完整表示形式。为了提高性能,您可以要求服务器仅发送您真正需要的字段,从而只接收部分响应

要请求部分响应,请使用 fields 请求参数来指定您希望返回的字段。对于任何返回响应数据的请求,您都可以使用此参数。

请注意,fields 参数仅影响响应数据;它不会影响您需要发送的数据(如果有)。要减少您在修改资源时所发送的数据量,请使用修补请求。

示例

JSON

下例显示了如何将 fields 参数用于一个名为“Demo”的通用(虚构的)API。

简单请求:下面的 HTTP GET 请求省略了 fields 参数,因而会返回完整资源。

https://www.googleapis.com/demo/v1

完整资源响应:完整资源数据包括以下字段以及其他许多字段(为简便起见,此处省略了那些字段)。

{
  "kind": "demo",
  ...
  "items": [
  {
    "title": "First title",
    "comment": "First comment.",
    "characteristics": {
      "length": "short",
      "accuracy": "high",
      "followers": ["Jo", "Will"],
    },
    "status": "active",
    ...
  },
  {
    "title": "Second title",
    "comment": "Second comment.",
    "characteristics": {
      "length": "long",
      "accuracy": "medium"
      "followers": [ ],
    },
    "status": "pending",
    ...
  },
  ...
  ]
}

只请求部分响应的情况:下面的请求针对的是同样的资源,但它使用了 fields 参数,因而大大减少了返回的数据量。

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

部分响应:服务器为响应上述请求而发回的以下响应只包含类型信息和一个经过简化的 items 数组(该数组中的每个项目只包含 HTML 标题和长度特征信息)。

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

请注意,该响应是一个只包括所选字段及其所属父对象的 JSON 对象。

接下来我们首先详细讲解如何设置 fields 参数的格式,然后会详细介绍响应中究竟返回了什么内容。

Fields 参数语法概述

fields 请求参数值的格式大体上遵循 XPath 语法。支持的语法总结如下,另外下一节还将提供更多示例。

  • 使用以逗号分隔的列表来选择多个字段。
  • 使用 a/b 选择嵌套在字段 a 内的字段 b;使用 a/b/c 选择嵌套在 b 内的字段 c

    例外情况:在使用“data”封装容器的 API 响应中(响应嵌套在 data 对象内,例如 data: { ... }),不要在 fields 规范内包含“data”。在 fields 规范中加入类似 data/a/b 的 data 对象会引发错误。因此,应该使用类似 a/bfields 规范。

  • 通过将表达式放在圆括号“( )”内,使用子选择器请求数组或对象的一组特定子字段。

    例如:fields=items(id,author/email) 只会返回 items 数组中每个元素的项目 ID 和作者的电子邮件地址。您也可以指定单个子字段,这种情况下 fields=items(id) 等同于 fields=items/id

  • 如果需要,可在选择字段时使用通配符。

    例如:使用 fields=items/pagemap/* 可选择 pagemap 中的所有对象。

有关使用 fields 参数的更多示例

下面的示例说明了 fields 参数值对响应有何影响。

注意:与所有查询参数值一样,fields 参数值也必须经过网址编码。为了便于阅读,本文中的示例省略了编码。

指定您想返回的字段,或者进行字段选择
fields 请求参数值是以逗号分隔的字段列表,并且每个字段都是相对于响应的根指定的。因此,如果您执行的是列出操作,则响应是一个集合,并且通常包含一组资源。如果您执行的是返回单一资源的操作,则字段是相对于该资源指定的。如果您选择的字段是(或属于)某个数组,则服务器会返回该数组中所有元素的选定部分。

以下是一些集合级别的示例:
示例 结果
items 返回 items 数组中的所有元素,包括每个元素中的所有字段,但不包括其他字段。
etag,items 同时返回 etag 字段和 items 数组中的所有元素。
items/title 仅返回 items 数组中所有元素的 title 字段。

每当返回嵌套字段时,响应中均包括该字段的父对象。父级字段不会包括其他任何子字段(除非已明确选择)。
context/facets/label 仅返回 facets 数组中所有成员的 label 字段,而该数组本身嵌套在 context 对象之下。
items/pagemap/*/title 对于 items 数组中的每个元素,仅返回 pagemap 的所有子对象的 title 字段(如果存在)。

以下是一些资源级别的示例:
示例 结果
title 返回所请求资源的 title 字段。
author/uri 返回所请求资源中 author 对象的 uri 子字段。
links/*/href
返回 links 的所有子对象的 href 字段。
使用“子选择”仅请求特定字段的相关部分
默认情况下,如果您的请求指定具体字段,则服务器会完整地返回相应的对象或数组元素。您可以指定一个仅包含某些子字段的响应。如下例所示,可以使用“( )”子选择语法来实现此目的。
示例 结果
items(title,author/uri) 仅返回 items 数组中每个元素的 title 和作者的 uri 的值。

仅处理部分响应

处理完含有 fields 查询参数的有效请求之后,服务器将发回一个 HTTP 200 OK 状态代码以及所请求的数据。如果 fields 查询参数存在错误或因其他原因而无效,服务器会返回 HTTP 400 Bad Request 状态代码以及一条错误消息,让用户知道他们在字段选择方面存在什么问题(例如 "Invalid field selection a/b")。

以下是上文简介部分所提到的部分响应的示例。该请求使用 fields 参数来指定要返回的字段。

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

部分响应如下所示:

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

注意:对于支持使用查询参数进行数据分页(例如 maxResultsnextPageToken)的 API,请使用这些参数将每个查询的结果缩减为易于管理的大小。否则,可能无法实现本应通过部分响应获得的性能提升。

Atom

DCM/DFA Reporting and Trafficking API 不支持 Atom 数据格式。

修补(部分更新)

在修改资源时,也可以避免发送不必要的数据。如果仅为您要更改的具体字段发送更新数据,请使用 HTTP PATCH 动词。本文所述的修补语义不同于采用 GData 实现的旧版部分更新方案(并且较之更为简单)。

下面的简短示例显示了使用修补方式如何最大限度减少进行少许更新时需要发送的数据。

示例

JSON

下例显示的是一个简单修补请求,用于仅更新一个名为“Demo”的通用(虚构的)API 资源的标题。该资源还包含一条注释、一组特征、一种状态以及许多其他字段,但由于 title 字段是唯一要修改的字段,所以此请求会仅发送该字段:

PATCH https://www.googleapis.com/demo/v1/324
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "title": "New title"
}

响应:

200 OK
{
  "title": "New title",
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "accuracy": "high",
    "followers": ["Jo", "Will"],
  },
  "status": "active",
  ...
}

服务器将返回 200 OK 状态代码以及更新后的资源的完整表示形式。由于修补请求中仅包含 title 字段,因此只有该值会与之前的值有所不同。

注意:如果您将部分响应 fields 参数与修补请求配合使用,则可以进一步提高更新请求的效率。修补请求只会减少请求的大小。部分响应可以减少响应的大小。因此,要同时从这两个方向减少数据发送量,请结合使用修补请求与 fields 参数。

修补请求的语义

修补请求的主体中仅包含您要修改的资源字段。指定字段时,必须将所属的任何父对象也包括在内,因为所属父对象会与部分响应一起返回。您发送的已修改数据会合并到父对象的数据中(如果有)。

  • 添加:如需添加目前并不存在的字段,请指定这个新字段及其值。
  • 修改:如需更改现有字段的值,请指定该字段并为其设置新值。
  • 删除:如需删除字段,请指定该字段并将其设为 null。例如:"comment": null。此外,您还可以删除整个对象(如果它是可变的),只需将其设为 null 即可。如果您使用的是 Java API 客户端库,请改用 Data.NULL_STRING;如需了解详情,请参阅 JSON null

数组备注:包含数组的修补请求将使用您提供的数组替换现有数组。您不能逐个修改、添加或删除数组中的项目。

在读取-修改-写入周期中使用修补

一种比较实用的做法是,先提取包含您要修改的数据的部分响应。这对于使用 ETag 的资源来说尤为重要,因为您必须在 If-Match HTTP 标头中提供当前的 ETag 值,才能成功更新资源。获取数据之后,您就可以修改自己想要更改的值,并将已修改的部分表示形式与修补请求一起发回。以下示例假设 Demo 资源使用 ETag:

GET https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token

以下是部分响应:

200 OK
{
  "etag": "ETagString"
  "title": "New title"
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "level": "5",
    "followers": ["Jo", "Will"],
  }
}

以下修补请求基于该响应。如下所示,该请求还使用 fields 参数对修补响应中返回的数据进行限制:

PATCH https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json
If-Match: "ETagString"
{
  "etag": "ETagString"
  "title": "",                  /* Clear the value of the title by setting it to the empty string. */
  "comment": null,              /* Delete the comment by replacing its value with null. */
  "characteristics": {
    "length": "short",
    "level": "10",              /* Modify the level value. */
    "followers": ["Jo", "Liz"], /* Replace the followers array to delete Will and add Liz. */
    "accuracy": "high"          /* Add a new characteristic. */
  },
}

服务器将返回 200 OK HTTP 状态代码,以及更新后的资源的部分表示形式:

200 OK
{
  "etag": "newETagString"
  "title": "",                 /* Title is cleared; deleted comment field is missing. */
  "characteristics": {
    "length": "short",
    "level": "10",             /* Value is updated.*/
    "followers": ["Jo" "Liz"], /* New follower Liz is present; deleted Will is missing. */
    "accuracy": "high"         /* New characteristic is present. */
  }
}

直接构建修补请求

对于某些修补请求,您需要以您之前提取过的数据为基础来构建它们。例如,如果您希望向数组中添加项目,并且不希望丢失任何现有的数组元素,则需要首先获取现有的数据。同样,如果 API 使用 ETag,您需要将之前的 ETag 值与您的请求一起发送,才能成功更新资源。

注意:在使用 ETag 时,您可以使用 "If-Match: *" HTTP 标头强制完成修补。如果您执行了以上操作,就无需在写入之前执行读取操作。

不过,在其他一些情况下,您可以直接构建修补请求,无需首先提取现有数据。例如,您可以轻松创建一个修补请求,用以将某个字段更新为新值或添加一个新字段。示例如下:

PATCH https://www.googleapis.com/demo/v1/324?fields=comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "comment": "A new comment",
  "characteristics": {
    "volume": "loud",
    "accuracy": null
  }
}

对于该请求,如果 comment 字段已有值,则新值会覆盖该值;否则,系统会将该字段设置为新值。同样,如果有音量特征,则该值会被覆盖;如果没有,则会新建一个。accuracy 字段(如果已设置)会被移除。

Atom

DCM/DFA Reporting and Trafficking API 不支持 Atom 数据格式。

处理对修补请求的响应

处理有效修补请求之后,API 会返回 200 OK HTTP 响应代码以及修改后的资源的完整表示形式。如果 API 使用了 ETag,则服务器会在成功处理修补请求后更新 ETag 值,正如使用 PUT 时那样。

修补请求会返回资源的完整表示形式,除非您使用 fields 参数减少请求返回的数据量。

如果修补请求导致的新资源状态在语法或语义上是无效的,则服务器会返回 400 Bad Request 或 422 Unprocessable Entity HTTP 状态代码,并且资源状态会保持不变。例如,如果您尝试删除必填字段的值,服务器就会返回错误。

不支持 PATCH HTTP 动词时的备用表示法

如果您的防火墙不允许 HTTP PATCH 请求,请执行 HTTP POST 请求并且将替代标头设为 PATCH,如下所示:

POST https://www.googleapis.com/...
X-HTTP-Method-Override: PATCH
...

修补与更新之间的区别

实际上,在为使用了 HTTP PUT 动词的更新请求发送数据时,您只需发送相应的必需或可选字段;如果您发送服务器所设置的字段值,这些值将会被忽略。尽管该方法看起来好像是执行部分更新的另一方法,但有一些局限性。对于使用 HTTP PUT 动词的更新,如果您没有提供必需参数,则请求会失败;同样,如果您没有提供可选参数,则请求会清除之前设置的数据。

出于以上原因考虑,使用修补是更安全的选择。您只需为自己想要修改的字段提供数据;系统不会清除您省略的字段。此规则的唯一例外情况是存在重复的元素或数组时:如果您省略所有重复的元素和数组,它们将会保持原样;如果您提供其中任何元素或数组,系统会将所有元素或数组替换为您提供的元素或数组。