批量请求

由于全球批量 HTTP 端点的弃用,仅针对 BigQuery API 的批量 HTTP 请求将于 2021 年 6 月 1 日停止运行。如果应用会发送批量 HTTP 请求,请在 2021 年 6 月 1 日之前使用单个 HTTP 请求替换批量 HTTP 请求

如需了解弃用,请参阅以下常见问题解答部分。如需查看有关如何批量处理 HTTP 请求的文档,请参阅批处理请求

BigQuery 批量 HTTP API 弃用常见问题解答

为何弃用 BigQuery 批量 HTTP 请求?

对全局 HTTP 批量端点的支持基于使用单个共享代理来接收所有对 API 的请求的架构。随着 Google 向分布式更强的高性能架构发展,因为在此类架构中请求会直接发送到相应的 API 服务器,我们不再支持这些全局端点

下一步是弃用 BigQuery 批量 HTTP 请求。BigQuery 服务也是分布式的。高 QPS 方法由专用后端处理。所有区域都是独立的,但批量 HTTP 请求可能导致跨区域请求扇出。这样可以提高批处理效率,并且会导致更高的处理延迟,这与批量 HTTP 请求支持的原始目标相反。

具体弃用了什么功能?

以下与 BigQuery API 交互的批量请求方法将不再适用:

如何迁移?

大多数 BigQuery 用户不使用批量 HTTP 请求。如果您仍在使用批量请求,请使用以下示例将批量 HTTP 请求替换为单个 HTTP 请求。

REST

按照 BigQuery API 参考文档部分所述,发送单个 HTTP 请求。请勿使用 /batch/v2/bigquery 路径批量合并您的请求。

JavaScript

如果您使用 JavaScript,则代码块的开头如下所示:

// Notice that the outer batch request contains inner API requests
// for two different APIs.

// Request to urlshortener API
request1 = gapi.client.urlshortener.url.get({"shortUrl":
"http://goo.gl/fbsS"});

// Request to zoo API
request2 = gapi.client.zoo.animals.list();

// Request to urlshortener API
request3 = gapi.client.urlshortener.url.get({"shortUrl":
"https://goo.gl/XYFuPH"});

// Request to zoo API
request4 = gapi.client.zoo.animal().get({"name": "giraffe"});

// Creating a batch request object
batchRequest = gapi.client.newBatch();
// adding the 4 batch requests
batchRequest.add(request1);
batchRequest.add(request2);
batchRequest.add(request3);
batchRequest.add(request4);
// print the batch request
batchRequest.then(x=>console.log(x))

将上述代码块替换为如下所示的代码块:

// Request to urlshortener API
request1 = gapi.client.urlshortener.url.get({"shortUrl": "http://goo.gl/fbsS"});

// Request to zoo API
request2 = gapi.client.zoo.animals.list();

// Request to urlshortener API
request3 = gapi.client.urlshortener.url.get({"shortUrl": "http://goo.gl/fbsS"})

// Request to zoo API
request4 = gapi.client.zoo.animals.list();

// print the 4 individual requests
Promise.all([request1, request2, request3, request4])
    .then(x=>console.log(x));

Python

如果您使用 Python,则代码块的开头如下所示:

from apiclient.http import BatchHttpRequest

def insert_animal(request_id, response, exception):
  if exception is not None: # Do something with the exception
    pass
  else: # Do something with the response
    pass

service = build('farm', 'v2')
batch = service.new_batch_http_request(callback=insert_animal)
batch.add(service.animals().insert(name="sheep"))
batch.add(service.animals().insert(name="pig"))
batch.add(service.animals().insert(name="llama"))
batch.execute(http=http)

将上述代码块替换为如下所示的代码块:

# import a new API to create a thread pool
from concurrent.futures import ThreadPoolExecutor as PoolExecutor

def run_it(request):
  print(request.execute())

service = build('farm', 'v2')
request1 = service.animals().insert(name="sheep")
request2 = service.animals().insert(name="pig")
request3 = service.animals().insert(name="llama")
with PoolExecutor(max_workers=4) as executor:
  for _ in executor.map(run_it,[request1, request2, request3]):
    pass

其他语言

与上述示例类似,将 BatchRequest 调用替换为单个请求。

获取迁移支持

如需迁移方面的帮助,您可以在 Stack Overflow 上提问。Google 工程师会对带 google-bigquery 标记的问题进行监控并给予解答。提问时请使用此标记。我们的目标是在合理的时间范围内解答所有问题。

批处理请求

本文档介绍了如何对 API 调用进行批处理以减少客户端必须建立的 HTTP 连接数量。

本文档专门介绍了如何通过发送 HTTP 请求来发出批处理请求。如果您要使用某个 Google 客户端库来发出批处理请求,请参阅该客户端库的说明文档

概览

客户端建立的每个 HTTP 连接都会产生一定的开销。BigQuery API 支持批处理,这样您的客户端就可以将多个 API 调用组合为一个 HTTP 请求。

在以下示例情况下,您可能需要使用批处理:

  • 您刚开始使用该 API,并且需要上传大量数据。
  • 用户在应用离线(已断开与互联网的连接)时对数据进行了更改,因此您的应用需要通过发出大量更新和删除请求来将其本地数据与服务器同步。

在上述每种情况下,您都可以将这些调用组合成一个 HTTP 请求,而不是单独发送每个调用。请注意,所有内部请求都必须发送到同一 Google API。

单个批量请求最多可以包含 1000 次调用。如果必须进行更多次调用,请使用多个批量请求。

注意:BigQuery API 批处理系统使用的语法与 OData 批处理系统相同,但语义有所不同。

批量详情

批量请求就是将多个 API 调用进行合并而形成的一个 HTTP 请求,您可以将此请求发送到 API 发现文档中指定的 batchPath。默认路径为 /batch/api_name/api_version。本部分详细介绍了批处理语法,随后还会提供一个示例

注意:一组一起进行批处理的 n 个请求将按 n 个请求(而非一个请求)计入用量限额。在处理之前,系统会将批量请求拆分为一组请求。

批量请求的格式

批量请求是一个包含多个 BigQuery API 调用的标准 HTTP 请求,使用 multipart/mixed 内容类型。在此主 HTTP 请求中,每个部分都包含一个嵌套的 HTTP 请求。

各个部分都以其自身的 Content-Type: application/http 标头开头。您还可以选择添加一个 Content-ID 标头。不过,每个部分的标头仅用于标记该部分的开头,而与嵌套请求无关。在服务器将批量请求拆分为多个单独请求之后,每个部分的标头就会被忽略。

各个部分的正文是一个完整的 HTTP 请求,各自有专用的动词、网址、标头和正文。此 HTTP 请求必须仅包含网址的路径部分;不允许在批量请求中使用完整的网址。

外部批量请求的 HTTP 标头应用于批次中的每个请求,但 Content-Type 之类的 Content- 标头除外。如果您在外部请求和个别调用中都指定了特定的 HTTP 标头,则个别调用标头的值将替换外部批量请求标头的值。另请注意,单个调用的标头仅应用于该调用本身。

例如,如果您为特定调用提供了 Authorization 标头,则该标头仅应用于该调用。如果您为外部请求提供了 Authorization 标头,则该标头将应用于所有的单个调用,除非单个调用将其替换为自身的 Authorization 标头。

当服务器收到批处理请求时,会将外部请求的查询参数和标头(如果适用)应用于各部分,然后将各部分视作单独的 HTTP 请求进行处理。

对批量请求的响应

服务器的响应是一个标准的 HTTP 响应,使用 multipart/mixed 内容类型;其中的每个部分分别是对批量请求中一个请求的响应,且顺序与这些请求相同。

和请求中的各部分一样,响应中的各部分都包含一个完整的 HTTP 响应,其中包括状态代码、标头和正文。此外,和请求中的各部分一样,响应中的各部分均以 Content-Type 标头为前缀,用于标记各部分的开头。

如果请求的某个特定部分具有 Content-ID 标头,则响应的对应部分也会有相同的 Content-ID 标头,其格式为在原始值前面加上 response- 字符串,如下例所示。

注意:服务器可能会以任何顺序执行您的调用,因此不要预期这些调用将会以您指定的顺序执行。如果要确保两个调用以指定顺序执行,就不能在单个请求中发送这两个调用。正确的做法是,先单独发送第一个调用,等收到其响应之后再发送第二个。

示例

以下示例显示的是将批处理与名为 Farm API 的通用(虚构)演示 API 结合使用的情况。不过,其中一些相同的概念也适用于 BigQuery API。

批量请求示例

POST /batch/farm/v1 HTTP/1.1
Authorization: Bearer your_auth_token
Host: www.googleapis.com
Content-Type: multipart/mixed; boundary=batch_foobarbaz
Content-Length: total_content_length

--batch_foobarbaz
Content-Type: application/http
Content-ID: <item1:12930812@barnyard.example.com>

GET /farm/v1/animals/pony

--batch_foobarbaz
Content-Type: application/http
Content-ID: <item2:12930812@barnyard.example.com>

PUT /farm/v1/animals/sheep
Content-Type: application/json
Content-Length: part_content_length
If-Match: "etag/sheep"

{
  "animalName": "sheep",
  "animalAge": "5"
  "peltColor": "green",
}

--batch_foobarbaz
Content-Type: application/http
Content-ID: <item3:12930812@barnyard.example.com>

GET /farm/v1/animals
If-None-Match: "etag/animals"

--batch_foobarbaz--

批量响应示例

此部分是对上一部分中示例请求的响应。

HTTP/1.1 200
Content-Length: response_total_content_length
Content-Type: multipart/mixed; boundary=batch_foobarbaz

--batch_foobarbaz
Content-Type: application/http
Content-ID: <response-item1:12930812@barnyard.example.com>

HTTP/1.1 200 OK
Content-Type application/json
Content-Length: response_part_1_content_length
ETag: "etag/pony"

{
  "kind": "farm#animal",
  "etag": "etag/pony",
  "selfLink": "/farm/v1/animals/pony",
  "animalName": "pony",
  "animalAge": 34,
  "peltColor": "white"
}

--batch_foobarbaz
Content-Type: application/http
Content-ID: <response-item2:12930812@barnyard.example.com>

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: response_part_2_content_length
ETag: "etag/sheep"

{
  "kind": "farm#animal",
  "etag": "etag/sheep",
  "selfLink": "/farm/v1/animals/sheep",
  "animalName": "sheep",
  "animalAge": 5,
  "peltColor": "green"
}

--batch_foobarbaz
Content-Type: application/http
Content-ID: <response-item3:12930812@barnyard.example.com>

HTTP/1.1 304 Not Modified
ETag: "etag/animals"

--batch_foobarbaz--