Desencapsulamento de payload para assinaturas de push do Pub/Sub

Ao criar seu sistema Pub/Sub, o desencapsulamento de payload pode ajudar você a se conectar a outros sistemas que não atendem a todos os requisitos de uma implementação de endpoint de push padrão do Pub/Sub.

Estes são alguns possíveis casos de uso para desencapsulamento de payload:

  • Não convém escrever um código de análise de mensagens específico do Pub/Sub para os endpoints de push HTTP.
  • Você prefere receber metadados de mensagens do Pub/Sub como cabeçalhos HTTP em vez dos metadados no corpo do HTTP POST.
  • Você quer enviar mensagens do Pub/Sub e excluir os metadados do Pub/Sub, por exemplo, ao enviar dados para uma API de terceiros.

Como funciona o desencapsulamento de payload

O desencapsulamento de payload é um recurso que remove todas as mensagens do Pub/Sub de todos os metadados das mensagens, exceto os dados delas. Ao enviar dados brutos de mensagens, os assinantes podem processar a mensagem sem precisar aderir a nenhum requisito de sistema do Pub/Sub.

  • Com o desencapsulamento de payload, os dados da mensagem são entregues diretamente como o corpo HTTP.
  • Sem o desencapsulamento do payload, o Pub/Sub entrega um objeto JSON que contém vários campos de metadados de mensagem e um campo de dados da mensagem. Nesse caso, o JSON precisa ser analisado para recuperar os dados da mensagem e, em seguida, decodificado em base64.

Gravar metadados

Depois de ativar o desencapsulamento de payload, use a opção Gravar metadados, que adiciona metadados removidos das mensagens ao cabeçalho da solicitação.

  • Metadados de gravação ativados. Adicione os metadados da mensagem novamente ao cabeçalho da solicitação. Também entrega os dados brutos e decodificados da mensagem.
  • Metadados de gravação desativados. Mostra apenas os dados brutos e decodificados da mensagem.

Os metadados de gravação são expostos por meio do Pub/Sub, do argumento --push-no-wrapper-write-metadata da Google Cloud CLI e da propriedade de API NoWrapper. Por padrão, esse valor é nulo.

Antes de começar

Exemplo de mensagens com wrapper e desencapsulada

Os exemplos a seguir ilustram a diferença entre o envio de uma mensagem HTTP encapsulada e desencapsulada. Nesses exemplos, os dados da mensagem contêm a string {"status": "Hello there"}.

Neste exemplo, uma assinatura é criada com o recurso de desencapsulamento de payload ativado e publica uma mensagem em mytopic. Ela usa uma chave de ordem com um valor some-key e o tipo de mídia é declarado como application/json.

gcloud pubsub topics publish mytopic
   --message='{"status": "Hello there"}'
   --ordering-key="some-key"
   --attribute "Content-Type=application/json"

As seções a seguir mostram a diferença entre uma mensagem com wrapper e desencapsulada.

Mensagem agrupada

O exemplo a seguir mostra uma mensagem encapsulada do Pub/Sub padrão. Nesse caso, o desencapsulamento de payload não está ativado.

Publicar Recebimentos de endpoint de push
data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }
Content-Length: 361
Content-Type: application/json
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{
  "message": {
      "attributes": {
          "Content-Type": "application/json"
      },
      "data": "eyJzdGF0dXMiOiAiSGVsbG8gdGhlcmUifQ==", //  Base64 - {"status": "Hello there"}
      "messageId": "2070443601311540",
      "message_id": "2070443601311540",
      "publishTime": "2021-02-26T19:13:55.749Z",
      "publish_time": "2021-02-26T19:13:55.749Z"
  },
  "subscription": "projects/myproject/..."
}

Mensagem desencapsulada com metadados de gravação desativados

No exemplo a seguir, mostramos uma mensagem desencapsulada com a opção de gravação de metadados desativada. Nesse caso, os cabeçalhos x-goog-pubsub-* e os atributos da mensagem não são incluídos.

Publicar Recebimentos de endpoint de push
data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }
Content-Length: 25
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{"status": "Hello there"}

Mensagem desencapsulada com metadados de gravação ativados

No exemplo a seguir, mostramos uma mensagem desencapsulada com a opção de gravação de metadados ativada. Nesse caso, os cabeçalhos x-goog-pubsub-* e os atributos da mensagem são incluídos.

Publicar Recebimentos de endpoint de push
data="{"status": "Hello there"}"
ordering_key="some-key"
attributes=
  {
     {"Content-Type", "application/json"}
  }
x-goog-pubsub-subscription-name: "projects/myproject/..."
x-goog-pubsub-message-id: "2070443601311540"
x-goog-pubsub-publish-time: "2021-02-26T19:13:55.749Z"
x-goog-pubsub-ordering-key: "some-key"
Content-Type: application/json
Content-Length: 12
User-Agent: CloudPubSub-Google
Host: subscription-project.uc.r.appspot.com

{"status": "Hello there"}

Configurar o desencapsulamento de payload

É possível ativar a entrega por push de desencapsulamento de payload para uma assinatura usando a página Detalhes da assinatura do console do Google Cloud, a Google Cloud CLI ou as bibliotecas de cliente.

Console

  1. No Console do Google Cloud, acesse a página Assinaturas.

    Abrir assinaturas do Pub/Sub

  2. Clique em Criar assinatura.

  3. No campo ID da assinatura, digite um nome.

    Para saber como nomear uma assinatura, consulte Diretrizes para nomear um tópico ou uma assinatura.

  4. Selecione um tópico no menu suspenso. A assinatura recebe mensagens do tópico.

  5. Em Tipo de envio, selecione Enviar.

  6. Para ativar o desencapsulamento de payload, selecione Ativar desencapsulamento de payload.

  7. (Opcional) Para preservar os metadados das mensagens no cabeçalho da solicitação, selecione Gravar metadados. Ative essa opção para definir um cabeçalho Content-Type para suas mensagens.

  8. Especifique um URL de endpoint.

  9. Mantenha todos os outros valores padrão.

  10. Clique em Criar.

gcloud

Para configurar uma assinatura com desencapsulamento de payload que inclua cabeçalhos HTTP padrão, execute o seguinte comando gcloud pubsub subscriptions create:

gcloud pubsub subscriptions create SUBSCRIPTION \
  --topic TOPIC \
  --push-endpoint=PUSH_ENDPOINT \
  --push-no-wrapper

Substitua:

  • SUBSCRIPTION: o nome ou ID da assinatura de pull.
  • TOPIC: o ID do tópico.
  • PUSH_ENDPOINT: o URL a ser usado como o endpoint da assinatura. Por exemplo, https://myproject.appspot.com/myhandler
  • --push-no-wrapper: entrega os dados da mensagem diretamente como o corpo HTTP.

Para configurar uma assinatura com desencapsulamento de payload e controlar o uso de cabeçalhos x-goog-pubsub-*, execute o seguinte comando:

gcloud pubsub subscriptions create SUBSCRIPTION \
  --topic TOPIC \
  --push-endpoint=PUSH_ENDPOINT \
  --push-no-wrapper \
  --push-no-wrapper-write-metadata
  • --push-no-wrapper-write-metadata: quando verdadeiro, grava os metadados de mensagem do Pub/Sub nos cabeçalhos x-goog-pubsub-<KEY>:<VAL> da solicitação HTTP. Grava os atributos das mensagens do Pub/Sub nos cabeçalhos <KEY>:<VAL> da solicitação HTTP.

Python

Antes de tentar esse exemplo, siga as instruções de configuração do Python em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub Python.

from google.cloud import pubsub_v1

# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"
# subscription_id = "your-subscription-id"
# endpoint = "https://my-test-project.appspot.com/push"

publisher = pubsub_v1.PublisherClient()
subscriber = pubsub_v1.SubscriberClient()
topic_path = publisher.topic_path(project_id, topic_id)
subscription_path = subscriber.subscription_path(project_id, subscription_id)

no_wrapper = pubsub_v1.types.PushConfig.NoWrapper(write_metadata=True)
push_config = pubsub_v1.types.PushConfig(
    push_endpoint=endpoint, no_wrapper=no_wrapper
)

# Wrap the subscriber in a 'with' block to automatically call close() to
# close the underlying gRPC channel when done.
with subscriber:
    subscription = subscriber.create_subscription(
        request={
            "name": subscription_path,
            "topic": topic_path,
            "push_config": push_config,
        }
    )

print(f"Push no wrapper subscription created: {subscription}.")
print(f"Endpoint for subscription is: {endpoint}")
print(f"No wrapper configuration for subscription is: {no_wrapper}")

Java

Antes de tentar essa amostra, siga as instruções de configuração do Java em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub Java.

/*
 * Copyright 2016 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pubsub;


import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.pubsub.v1.PushConfig;
import com.google.pubsub.v1.PushConfig.NoWrapper;
import com.google.pubsub.v1.Subscription;
import com.google.pubsub.v1.SubscriptionName;
import com.google.pubsub.v1.TopicName;
import java.io.IOException;

public class CreateUnwrappedPushSubscriptionExample {
  public static void main(String... args) throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String subscriptionId = "your-subscription-id";
    String topicId = "your-topic-id";
    String pushEndpoint = "https://my-test-project.appspot.com/push";

    createPushSubscriptionExample(projectId, subscriptionId, topicId, pushEndpoint);
  }

  public static void createPushSubscriptionExample(
      String projectId, String subscriptionId, String topicId, String pushEndpoint)
      throws IOException {
    try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) {
      TopicName topicName = TopicName.of(projectId, topicId);
      SubscriptionName subscriptionName = SubscriptionName.of(projectId, subscriptionId);
      NoWrapper noWrapper =
          NoWrapper.newBuilder()
              // Determines if message metadata is added to the HTTP headers of
              // the delivered message.
              .setWriteMetadata(true)
              .build();
      PushConfig pushConfig =
          PushConfig.newBuilder().setPushEndpoint(pushEndpoint).setNoWrapper(noWrapper).build();

      // Create a push subscription with default acknowledgement deadline of 10 seconds.
      // Messages not successfully acknowledged within 10 seconds will get resent by the server.
      Subscription subscription =
          subscriptionAdminClient.createSubscription(subscriptionName, topicName, pushConfig, 10);
      System.out.println("Created push subscription: " + subscription.getName());
    }
  }
}

C++

Antes de tentar esse exemplo, siga as instruções de configuração do C++ em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub C++.

namespace pubsub = ::google::cloud::pubsub;
namespace pubsub_admin = ::google::cloud::pubsub_admin;
[](pubsub_admin::SubscriptionAdminClient client,
   std::string const& project_id, std::string const& topic_id,
   std::string const& subscription_id, std::string const& endpoint) {
  google::pubsub::v1::Subscription request;
  request.set_name(
      pubsub::Subscription(project_id, subscription_id).FullName());
  request.set_topic(pubsub::Topic(project_id, topic_id).FullName());
  request.mutable_push_config()->set_push_endpoint(endpoint);
  request.mutable_push_config()->mutable_no_wrapper()->set_write_metadata(
      true);
  auto sub = client.CreateSubscription(request);
  if (sub.status().code() == google::cloud::StatusCode::kAlreadyExists) {
    std::cout << "The subscription already exists\n";
    return;
  }
  if (!sub) throw std::move(sub).status();

  std::cout << "The subscription was successfully created: "
            << sub->DebugString() << "\n";
}

Go

Antes de tentar esse exemplo, siga as instruções de configuração do Go em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub Go.

import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/pubsub"
)

// createPushNoWrapperSubscription creates a push subscription where messages are delivered in the HTTP body.
func createPushNoWrapperSubscription(w io.Writer, projectID, subID string, topic *pubsub.Topic, endpoint string) error {
	// projectID := "my-project-id"
	// subID := "my-sub"
	// topic of type https://godoc.org/cloud.google.com/go/pubsub#Topic
	// endpoint := "https://my-test-project.appspot.com/push"
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %w", err)
	}
	defer client.Close()

	sub, err := client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
		Topic:       topic,
		AckDeadline: 10 * time.Second,
		PushConfig: pubsub.PushConfig{
			Endpoint: endpoint,
			Wrapper: &pubsub.NoWrapper{
				// Determines if message metadata is added to the HTTP headers of
				// the delivered message.
				WriteMetadata: true,
			},
		},
	})
	if err != nil {
		return fmt.Errorf("CreateSubscription: %w", err)
	}
	fmt.Fprintf(w, "Created push no wrapper subscription: %v\n", sub)
	return nil
}

Node.js

Antes de tentar essa amostra, siga as instruções de configuração do Node.js em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub Node.js.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const pushEndpoint = 'YOUR_ENDPOINT_URL';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
const {PubSub} = require('@google-cloud/pubsub');

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createPushSubscriptionNoWrapper(
  pushEndpoint,
  topicNameOrId,
  subscriptionNameOrId
) {
  const options = {
    pushConfig: {
      // Set to an HTTPS endpoint of your choice. If necessary, register
      // (authorize) the domain on which the server is hosted.
      pushEndpoint,
      // When true, writes the Pub/Sub message metadata to
      // `x-goog-pubsub-<KEY>:<VAL>` headers of the HTTP request. Writes the
      // Pub/Sub message attributes to `<KEY>:<VAL>` headers of the HTTP request.
      noWrapper: {
        writeMetadata: true,
      },
    },
  };

  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, options);
  console.log(`Subscription ${subscriptionNameOrId} created.`);
}

Node.js

Antes de tentar essa amostra, siga as instruções de configuração do Node.js em Guia de início rápido: como usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Pub/Sub Node.js.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const pushEndpoint = 'YOUR_ENDPOINT_URL';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
import {PubSub, CreateSubscriptionOptions} from '@google-cloud/pubsub';

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createPushSubscriptionNoWrapper(
  pushEndpoint: string,
  topicNameOrId: string,
  subscriptionNameOrId: string
) {
  const options: CreateSubscriptionOptions = {
    pushConfig: {
      // Set to an HTTPS endpoint of your choice. If necessary, register
      // (authorize) the domain on which the server is hosted.
      pushEndpoint,
      // When true, writes the Pub/Sub message metadata to
      // `x-goog-pubsub-<KEY>:<VAL>` headers of the HTTP request. Writes the
      // Pub/Sub message attributes to `<KEY>:<VAL>` headers of the HTTP request.
      noWrapper: {
        writeMetadata: true,
      },
    },
  };

  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, options);
  console.log(`Subscription ${subscriptionNameOrId} created.`);
}

Definir um cabeçalho de tipo de conteúdo na sua mensagem

Depois de ativar o desencapsulamento de payload, o Pub/Sub não define automaticamente um campo de cabeçalho de tipo de mídia na solicitação. Se você não definir explicitamente um campo de cabeçalho Content-Type, o servidor da Web que processa a solicitação poderá definir um valor padrão application/octet-stream ou interpretar a solicitação de maneira inesperada.

Se você precisar de um cabeçalho Content-Type, declare-o explicitamente no momento da publicação para cada mensagem publicada individual. Para isso, ative Gravar metadados primeiro. O resultado da ativação de Gravar metadados é mostrado nos exemplos fornecidos.

A seguir