Envía solicitudes por lotes

En este documento, se muestra cómo agrupar llamadas a la API de JSON para reducir la cantidad de conexiones HTTP que debe hacer el cliente cuando accede a Cloud Storage.

Descripción general

Cada conexión HTTP que tu cliente realiza da como resultado una cierta cantidad de sobrecarga. La API de JSON de Cloud Storage admite el procesamiento por lotes, a fin de permitir a tu cliente colocar varias llamadas a la API en una sola solicitud HTTP.

A continuación, se detallan algunos ejemplos de situaciones en las que te sería útil usar el procesamiento por lotes:

  • Actualizar metadatos, como permisos, en muchos objetos.
  • Borrar muchos objetos.

En cada caso, en lugar de enviar cada llamada por separado, puedes agruparlas en una única solicitud HTTP. Todas las solicitudes internas deben ir en la misma API de Cloud Storage JSON.

No debes incluir más de 100 llamadas en una sola solicitud por lotes. Si necesitas hacer más llamadas, usa varias solicitudes por lotes. La carga útil por lotes total de la solicitud debe ser inferior a 10 MB.

Detalles del lote

Una solicitud por lotes consta de varias llamadas a la API combinadas en una solicitud HTTP, que pueden enviarse al extremo por lotes de Cloud Storage, que es https://storage.googleapis.com/batch/storage/v1. En esta sección, se describe la sintaxis del lote en detalle; más adelante, se muestra un ejemplo.

Formato de una solicitud por lotes

Una solicitud por lotes es una solicitud HTTP estándar que contiene varias llamadas a la API de JSON de Cloud Storage. En esta solicitud principal, se usa el tipo de contenido multipart/mixed. Dentro de la solicitud HTTP principal, hay varias partes, cada una con una solicitud HTTP anidada.

Cada parte comienza con su propio encabezado HTTP Content-Type: application/http. La parte también puede tener un encabezado opcional Content-ID. Estos encabezados marcan el comienzo de la parte, pero son independientes de la solicitud HTTP anidada. Esto significa que después de que el servidor separa la solicitud por lotes en solicitudes separadas, los encabezados de las partes se ignoran.

El cuerpo de cada parte es en sí mismo una solicitud HTTP completa, con su propio verbo, URL, encabezados y cuerpo. Estas solicitudes HTTP solo deben contener la parte de la ruta de la URL; las URL completas pueden tener un comportamiento indefinido.

Los encabezados HTTP para la solicitud por lotes externa, excepto por los encabezados Content-, como Content-Type, también se aplican a cada solicitud anidada. Sin embargo, si especificas un encabezado HTTP determinado en la solicitud externa y en una solicitud anidada, el valor del encabezado de la solicitud anidada anula el valor del encabezado de la solicitud por lotes externa en esa solicitud específica.

Por ejemplo, si proporcionas un encabezado Authorization para una solicitud anidada específica, ese encabezado se aplica solo a la solicitud que lo especificó. Si proporcionas un encabezado Authorization para la solicitud externa, dicho encabezado se aplica a todas las solicitudes anidadas, a menos que lo anulen con un encabezado Authorization propio.

Cuando el Cloud Storage recibe la solicitud por lotes, aplica los encabezados y parámetros de consulta de la solicitud externa (según corresponda) a cada parte y, a continuación, trata cada parte como una solicitud HTTP separada.

Respuesta a una solicitud por lotes

La respuesta de Cloud Storage es una sola respuesta HTTP estándar con un tipo de contenido multipart/mixed; cada parte de esta respuesta principal es la respuesta a una de las solicitudes de la solicitud por lotes. El orden de las respuestas es el mismo que el de las solicitudes.

Como todas las partes de una solicitud, cada parte de respuesta contiene una respuesta HTTP completa, que incluye un código de estado, encabezados y un cuerpo. Además, cada parte de respuesta está precedida por un encabezado Content-Type que marca el comienzo de la parte. Si deseas obtener más información sobre los códigos de estado, consulta Estado de HTTP y códigos de error para la API de Cloud Storage JSON.

Si una parte determinada de la solicitud tenía un encabezado Content-ID, la parte correspondiente de la respuesta tendrá un encabezado Content-ID que coincida. El encabezado Content-ID de la respuesta comienza con response-, seguido del valor Content-ID que se usa en la solicitud, como se muestra en el ejemplo.

Ejemplo

En el siguiente ejemplo de lote, se actualizan los metadatos personalizados para tres objetos en example-bucket.

Ejemplo de solicitud HTTP por lotes

HTTP

POST /batch/storage/v1 HTTP/1.1
Host: storage.googleapis.com
Content-Length: 960
Content-Type: multipart/mixed; boundary="===============7330845974216740156=="
Authorization: Bearer ya29.AHES6ZRVmB7fkLtd1XTmq6mo0S1wqZZi3-Lh_s-6Uw7p8vtgSwg

--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+1>

PATCH /storage/v1/b/example-bucket/o/obj1 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 31

{"metadata": {"type": "tabby"}}
--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+2>

PATCH /storage/v1/b/example-bucket/o/obj2 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 32

{"metadata": {"type": "tuxedo"}}
--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+3>

PATCH /storage/v1/b/example-bucket/o/obj3 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 32

{"metadata": {"type": "calico"}}
--===============7330845974216740156==--

Bibliotecas cliente

C++

La biblioteca cliente de C++ no admite solicitudes por lotes.

C#

La biblioteca cliente de C# no admite solicitudes por lotes.

Go

La biblioteca cliente de Go no admite solicitudes por lotes.

Java

Si deseas obtener más información, consulta la documentación de referencia de la API de Cloud Storage para Java.

import com.google.api.gax.paging.Page;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageBatch;
import com.google.cloud.storage.StorageOptions;
import java.util.HashMap;
import java.util.Map;

public class BatchSetObjectMetadata {
  public static void batchSetObjectMetadata(
      String projectId, String bucketName, String directoryPrefix) {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The directory prefix. All objects in the bucket with this prefix will have their metadata
    // updated
    // String directoryPrefix = "yourDirectory/";

    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    Map<String, String> newMetadata = new HashMap<>();
    newMetadata.put("keyToAddOrUpdate", "value");
    Page<Blob> blobs =
        storage.list(
            bucketName,
            Storage.BlobListOption.prefix(directoryPrefix),
            Storage.BlobListOption.currentDirectory());
    StorageBatch batchRequest = storage.batch();

    // Add all blobs with the given prefix to the batch request
    for (Blob blob : blobs.iterateAll()) {
      batchRequest.update(blob.toBuilder().setMetadata(newMetadata).build());
    }

    // Execute the batch request
    batchRequest.submit();

    System.out.println(
        "All blobs in bucket "
            + bucketName
            + " with prefix '"
            + directoryPrefix
            + "' had their metadata updated.");
  }
}

Node.js

La biblioteca cliente de Node.js no admite solicitudes por lotes.

PHP

La biblioteca cliente de PHP no admite solicitudes por lotes.

Python

Si deseas obtener más información, consulta la documentación de referencia de la API de Cloud Storage para Ruby.


from google.cloud import storage

def batch_request(bucket_name, prefix=None):
    """
    Use a batch request to patch a list of objects with the given prefix in a bucket.

    Note that Cloud Storage does not support batch operations for uploading or downloading.
    Additionally, the current batch design does not support library methods whose return values
    depend on the response payload.
    See https://cloud.google.com/python/docs/reference/storage/latest/google.cloud.storage.batch
    """
    # The ID of your GCS bucket
    # bucket_name = "my-bucket"
    # The prefix of the object paths
    # prefix = "directory-prefix/"

    client = storage.Client()
    bucket = client.bucket(bucket_name)

    # Accumulate in a list the objects with a given prefix.
    blobs_to_patch = [blob for blob in bucket.list_blobs(prefix=prefix)]

    # Use a batch context manager to edit metadata in the list of blobs.
    # The batch request is sent out when the context manager closes.
    # No more than 100 calls should be included in a single batch request.
    with client.batch():
        for blob in blobs_to_patch:
            metadata = {"your-metadata-key": "your-metadata-value"}
            blob.metadata = metadata
            blob.patch()

    print(
        f"Batch request edited metadata for all objects with the given prefix in {bucket.name}."
    )

Ruby

Si deseas obtener información para realizar una solicitud por lotes con Ruby, consulta la documentación de referencia de la API de Cloud Storage para Ruby.

Ejemplo de respuesta HTTP por lotes

Esta es la respuesta a la solicitud de ejemplo HTTP de la sección anterior.

HTTP/1.1 200 OK
Content-Type: multipart/mixed; boundary=batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 3767

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+1>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/V43j6azD55CPRGb9b6uytDYl61Y"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj1/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "tabby"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+2>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/91POdd-sxSAkJnS8Dm7wMxBSDKk"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj2/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "tuxedo"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+3>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/d2Z1F1_ZVbB1dC0YKM9rX5VAgIQ"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj3/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "calico"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=--

Si la solicitud general no tiene el formato correcto y Cloud Storage no puede analizarla en subsolicitudes, recibirás un error 400. De lo contrario, Cloud Storage muestra un código de estado 200, incluso si algunas o todas las subsolicitudes fallan.

Cuando la solicitud general muestra un código de estado 200, la respuesta contiene los resultados de cada solicitud secundaria, incluido un código de estado para cada uno, que indica si la solicitud secundaria se realizó de forma correcta o no.