Anulação da união de payloads para subscrições push do Pub/Sub

Ao criar o seu sistema Pub/Sub, a anulação da união de payloads pode ajudar a estabelecer ligação a outros sistemas que não cumprem todos os requisitos do sistema de uma implementação de endpoint push do Pub/Sub padrão.

Seguem-se alguns potenciais exemplos de utilização da anulação da união da carga útil:

  • Não quer escrever código de análise de mensagens específico do Pub/Sub para os seus pontos finais de envio HTTP.
  • Prefere receber metadados de mensagens do Pub/Sub como cabeçalhos HTTP, em vez dos metadados no corpo do HTTP POST.
  • Quer enviar mensagens do Pub/Sub e excluir os metadados do Pub/Sub, por exemplo, quando envia dados para uma API de terceiros.

Como funciona a anulação da união de payloads

A anulação da união da carga útil é uma funcionalidade que remove todos os metadados das mensagens do Pub/Sub, exceto os dados das mensagens. Ao enviar dados de mensagens não processados, os subscritores podem processar a mensagem sem terem de cumprir os requisitos do sistema do Pub/Sub.

  • Com a anulação da união do payload, os dados da mensagem são entregues diretamente como o corpo HTTP.
  • Sem a anulação da união do payload, o Pub/Sub envia um objeto JSON que contém vários campos de metadados de mensagens e um campo de dados de mensagens. Neste caso, o JSON tem de ser analisado para obter os dados da mensagem e, em seguida, descodificado em base64.

Escreva metadados

Depois de ativar a anulação da união da carga útil, pode usar a opção Escrever metadados, que adiciona metadados de mensagens removidos anteriormente ao cabeçalho do pedido.

  • Escrita de metadados ativada. Adicione novamente os metadados da mensagem ao cabeçalho do pedido. Também fornece os dados de mensagens não processados e descodificados.
  • Escrita de metadados desativada. Apenas envia os dados de mensagens não processados e descodificados.

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

Antes de começar

Exemplo de mensagens com e sem wrapper

Os exemplos seguintes ilustram a diferença entre o envio de uma mensagem HTTP envolvida e não envolvida. Nestes exemplos, os dados da mensagem contêm a string {"status": "Hello there"}.

Para este exemplo, é criada uma subscrição com a funcionalidade de desencapsulamento do payload ativada e é publicada uma mensagem em mytopic. Usa uma chave de ordenação com um valor de some-key e o tipo de suporte é declarado como application/json.

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

As secções seguintes mostram a diferença entre uma mensagem com wrapper e sem wrapper.

Mensagem moldada

O exemplo seguinte mostra uma mensagem padrão do Pub/Sub envolvida. Neste caso, a anulação da união da carga útil não está ativada.

Publicar O ponto final de envio push recebe
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 não processada com metadados de escrita desativados

O exemplo seguinte mostra uma mensagem não envolvida com a opção de metadados de escrita desativada. Neste caso, os cabeçalhos x-goog-pubsub-* e os atributos das mensagens não estão incluídos.

Publicar O ponto final de envio push recebe
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 não processada com metadados de escrita ativados

O exemplo seguinte mostra uma mensagem não processada com a opção de metadados de escrita ativada. Neste caso, os cabeçalhos x-goog-pubsub-* e os atributos das mensagens são incluídos.

Publicar O ponto final de envio push recebe
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"}

Configure a anulação da união do payload

Pode ativar o envio por push da desagregação de payloads para uma subscrição através da página Google Cloud Detalhes da subscriçãoda consola, da CLI do Google Cloud ou das bibliotecas de cliente.

Consola

  1. Na Google Cloud consola, aceda à página Subscrições.

    Abra as subscrições do Pub/Sub

  2. Clique em Criar subscrição.

  3. No campo ID da subscrição, introduza um nome.

    Para obter informações sobre como atribuir um nome a uma subscrição, consulte as diretrizes para atribuir um nome a um tópico ou a uma subscrição.

  4. Selecione um tópico no menu pendente. A subscrição recebe mensagens do tópico.

  5. Para Tipo de fornecimento, selecione Push.

  6. Para ativar a anulação da união do payload, selecione Ativar anulação da união do payload.

  7. (Opcional) Para preservar os metadados das mensagens no cabeçalho do pedido, selecione Escrever metadados. Tem de ativar esta opção para definir um cabeçalho Content-Type para as suas mensagens.

  8. Especifique um URL do ponto final.

  9. Mantenha todos os outros valores predefinidos.

  10. Clique em Criar.

gcloud

Para configurar uma subscrição com a anulação da união de payloads 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 o seguinte:

  • SUBSCRIPTION: o nome ou o ID da sua subscrição de obtenção.
  • TOPIC: o ID do tópico.
  • PUSH_ENDPOINT: o URL a usar como ponto final para esta subscrição. Por exemplo, https://myproject.appspot.com/myhandler
  • --push-no-wrapper: envia os dados da mensagem diretamente como o corpo HTTP.

Para configurar uma subscrição com a anulação da união da carga útil e controlar a utilização 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, escreve os metadados da mensagem do Pub/Sub nos cabeçalhos do pedido HTTP.x-goog-pubsub-<KEY>:<VAL> Escreve os atributos da mensagem do Pub/Sub nos cabeçalhos <KEY>:<VAL> do pedido HTTP.

Python

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

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 experimentar este exemplo, siga as instruções de configuração do Java no artigo Início rápido: usar bibliotecas cliente. Para mais informações, consulte a documentação de referência da API Java do Pub/Sub.

/*
 * 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 experimentar este exemplo, siga as instruções de configuração do C++ no artigo Início rápido: usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API C++ do Pub/Sub.

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";
}

Ir

O exemplo seguinte usa a versão principal da biblioteca de cliente Go Pub/Sub (v2). Se ainda estiver a usar a biblioteca v1, consulte o guia de migração para a v2. Para ver uma lista de exemplos de código da v1, consulte os exemplos de código descontinuados.

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

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/pubsub/v2"
	"cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
)

// createPushNoWrapperSubscription creates a push subscription where messages are delivered in the HTTP body.
func createPushNoWrapperSubscription(w io.Writer, projectID, topic, subscription, endpoint string) error {
	// projectID := "my-project-id"
	// topic := "projects/my-project-id/topics/my-topic"
	// subscription := "projects/my-project/subscriptions/my-sub"
	// 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()
	pbSubscription := &pubsubpb.Subscription{
		Name:               subscription,
		Topic:              topic,
		AckDeadlineSeconds: 10,
		PushConfig: &pubsubpb.PushConfig{
			PushEndpoint: endpoint,
			Wrapper: &pubsubpb.PushConfig_NoWrapper_{
				NoWrapper: &pubsubpb.PushConfig_NoWrapper{
					// Determines if message metadata is added to the HTTP headers of
					// the delivered message.
					WriteMetadata: true,
				},
			},
		},
	}
	sub, err := client.SubscriptionAdminClient.CreateSubscription(ctx, pbSubscription)
	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 experimentar este exemplo, siga as instruções de configuração do Node.js em Início rápido: usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Node.js do Pub/Sub.

/**
 * 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 experimentar este exemplo, siga as instruções de configuração do Node.js em Início rápido: usar bibliotecas de cliente. Para mais informações, consulte a documentação de referência da API Node.js do Pub/Sub.

/**
 * 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.`);
}

Defina um cabeçalho content-type na sua mensagem

Depois de ativar a desagregação da carga útil, o Pub/Sub não define automaticamente um campo de cabeçalho do tipo de suporte no seu pedido. Se não definir explicitamente um campo de cabeçalho Content-Type, o servidor Web que processa o seu pedido pode definir um valor predefinido de application/octet-stream ou interpretar o pedido de uma forma inesperada.

Se precisar de um cabeçalho Content-Type, certifique-se de que o declara explicitamente no momento da publicação para cada mensagem publicada individualmente. Para o fazer, tem de ativar primeiro a opção Escrever metadados. Este resultado da ativação da opção Escrever metadados é apresentado nos exemplos fornecidos.

O que se segue?