Como enviar solicitações em lote

Neste documento, você verá como agrupar chamadas da API JSON em lote para reduzir o número de conexões HTTP que seu cliente precisa fazer ao acessar o Cloud Storage.

Visão geral das solicitações em lote

Cada conexão HTTP feita pelo cliente resulta em certa quantidade de sobrecarga. A API JSON do Cloud Storage oferece suporte a solicitações em lote, o que permite que seu cliente agrupe várias chamadas de API em uma única solicitação HTTP. Agrupar várias chamadas de API em uma única solicitação permite reduzir a sobrecarga e a latência da rede quando você precisa realizar muitas operações.

Exemplos de operações para as quais você pode usar solicitações em lote:

  • Atualizar metadados, como permissões, em muitos objetos
  • Excluir muitos objetos

O Cloud Storage não oferece suporte a operações em lote para upload ou download.

Estrutura da solicitação em lote

Uma solicitação em lote é uma única solicitação HTTP padrão que contém várias chamadas da API JSON do Cloud Storage. A solicitação em lote externa usa multipart/mixed no cabeçalho da solicitação Content-Type, com um corpo composto de várias partes que contêm uma subsolicitação HTTP individual.

A solicitação em lote externa é enviada ao endpoint em lote:

https://storage.googleapis.com/batch/storage/v1

Cada subsolicitação no corpo do lote especifica um caminho de solicitação de API padrão da API JSON do Cloud Storage, como:

https://storage.googleapis.com/storage/v1/BUCKET_NAME/o/OBJECT_NAME

Para conferir o formato de um exemplo de solicitação em lote, consulte Exemplo.

Não inclua mais de 100 chamadas em uma única solicitação em lote para evitar erros HTTP 400, tempos limite de gateway e conexões descartadas. Se você precisar fazer mais de 100 chamadas, use várias solicitações em lote. O payload total da solicitação precisa ser menor que 10 MB. Solicitações maiores são rejeitadas pelo Cloud Storage.

Formato da solicitação em lote

Esta seção descreve o formato de uma solicitação em lote.

No corpo da solicitação externa, cada parte começa com seu próprio cabeçalho HTTP Content-Type: application/http. A parte também pode ter um cabeçalho Content-ID opcional. Esses cabeçalhos marcam o início da parte, mas são separados da solicitação HTTP aninhada. Isso significa que, depois que o servidor desencapsula a solicitação em lote em solicitações separadas, os cabeçalhos das partes são ignorados.

O corpo de cada parte é ele próprio uma solicitação HTTP completa, com o próprio verbo, URL, cabeçalhos e corpo. Essas solicitações HTTP precisam conter apenas a parte do caminho do URL; URLs completos podem ter um comportamento indefinido.

Os cabeçalhos HTTP da solicitação em lote externa, exceto os cabeçalhos Content-, como Content-Type, também se aplicam a todas as solicitações aninhadas. No entanto, se você especificar um determinado cabeçalho HTTP na solicitação externa e em uma solicitação aninhada, o valor do cabeçalho da solicitação aninhada modificará o valor do cabeçalho da solicitação em lote externa para essa solicitação específica.

Por exemplo, se você fornecer um cabeçalho Authorization para uma solicitação aninhada específica, esse cabeçalho será aplicado somente à solicitação que o especificou. Se você fornecer um cabeçalho Authorization para a solicitação externa, esse cabeçalho será aplicado a todas as solicitações aninhadas, a menos que elas o substituam por um cabeçalho Authorization próprio.

Ao receber a solicitação em lote, o Cloud Storage aplica os parâmetros e os cabeçalhos de consulta da solicitação externa (conforme apropriado) a cada parte e trata cada parte como se fosse uma solicitação HTTP separada.

Resposta a uma solicitação em lote

A resposta do Cloud Storage é uma resposta HTTP padrão única com um tipo de conteúdo multipart/mixed; cada parte dessa resposta principal é a resposta a uma das solicitações na solicitação em lote. A ordem das respostas é a mesma das solicitações.

Como todas as partes em uma solicitação, cada parte da resposta contém uma resposta HTTP completa, incluindo um código de status, cabeçalhos e corpo. E da mesma forma que as partes na solicitação, cada parte da resposta é precedida por um cabeçalho Content-Type que marca o início da parte. Para mais informações sobre códigos de status, consulte Códigos de erro e de status HTTP para a API JSON do Cloud Storage.

Se determinada parte da solicitação tiver um cabeçalho Content-ID, a parte correspondente da resposta terá um cabeçalho Content-ID correspondente. O cabeçalho Content-ID da resposta começa com response-, seguido pelo valor Content-ID usado na solicitação, conforme mostrado no exemplo.

Exemplo

O exemplo de lote a seguir atualiza os metadados personalizados de três objetos em example-bucket.

Exemplo de solicitação HTTP em lote

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 de cliente

C++

A biblioteca de cliente C++ não é compatível com solicitações em lote.

C#

A biblioteca de cliente C# não é compatível com solicitações em lote.

Go

A biblioteca de cliente Go não oferece suporte a solicitações em lote.

Java

Para mais informações, consulte a documentação de referência da API Cloud Storage para Java.

import com.google.api.gax.paging.Page;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageBatch;
import com.google.cloud.storage.StorageBatchResult;
import com.google.cloud.storage.StorageException;
import com.google.cloud.storage.StorageOptions;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class BatchSetObjectMetadata {
  public static void batchSetObjectMetadata(
      String projectId, String bucketName, String pathPrefix) {
    // 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 pathPrefix = "yourPath/";

    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(pathPrefix),
            Storage.BlobListOption.delimiter("/"));
    StorageBatch batchRequest = storage.batch();

    // Add all blobs with the given prefix to the batch request
    List<StorageBatchResult<Blob>> batchResults =
        blobs
            .streamAll()
            .map(blob -> batchRequest.update(blob.toBuilder().setMetadata(newMetadata).build()))
            .collect(Collectors.toList());

    // Execute the batch request
    batchRequest.submit();
    List<StorageException> failures =
        batchResults.stream()
            .map(
                r -> {
                  try {
                    BlobInfo blob = r.get();
                    return null;
                  } catch (StorageException e) {
                    return e;
                  }
                })
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

    System.out.println(
        (batchResults.size() - failures.size())
            + " blobs in bucket "
            + bucketName
            + " with prefix '"
            + pathPrefix
            + "' had their metadata updated successfully.");

    if (!failures.isEmpty()) {
      System.out.println("While processing, there were " + failures.size() + " failures");

      for (StorageException failure : failures) {
        failure.printStackTrace(System.out);
      }
    }
  }
}

Node.js

A biblioteca de cliente do Node.js não aceita solicitações em lote.

PHP

A biblioteca de cliente PHP não aceita solicitações em lote.

Python

Para mais informações, consulte a documentação de referência da API Cloud Storage para Python.


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

Para saber como fazer uma solicitação em lote usando Ruby, consulte a documentação de referência da API Cloud Storage para Ruby.

Rust

A biblioteca de cliente Rust não oferece suporte a solicitações em lote.

Exemplo de resposta HTTP em lote

Esta é a resposta à solicitação de exemplo de HTTP da seção 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=--

Se a solicitação geral não estiver formatada corretamente e o Cloud Storage não conseguir analisá-la em subsolicitações, você receberá um erro 400. Caso contrário, o Cloud Storage retornará um código de status 200, mesmo que algumas ou todas as subsolicitações falhem.

Quando a solicitação geral retorna com um código de status 200, a resposta contém resultados para cada subsolicitação, incluindo um código de status para cada uma, que indica se a subsolicitação foi bem-sucedida ou não. Por exemplo, ao excluir objetos em lote, cada subsolicitação bem-sucedida contém um código de status 204 No Content.