Entrega exatamente uma vez

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Nesta página, explicamos como receber e confirmar mensagens usando semânticas exatamente uma vez.

Limitações

Garantias de entrega exatamente uma vez

O Pub/Sub é compatível com entrega única, dentro de uma região da nuvem, com base em um ID de mensagem exclusivo definido pelo Pub/Sub.

Quando o recurso está ativado, o Pub/Sub fornece as seguintes garantias:

  • Nenhuma entrega será feita novamente depois que a mensagem for confirmada.

  • Nenhuma reenvio ocorrer enquanto uma mensagem estiver pendente. Uma mensagem será considerada pendente até que o prazo de confirmação expire ou a mensagem seja confirmada.

  • No caso de várias entregas válidas, devido à expiração do prazo de confirmação ou a confirmação negativa iniciada pelo cliente, apenas o ID de confirmação mais recente pode ser usado para confirmar a mensagem. Todas as solicitações com um ID de confirmação anterior falharão.

Reenvio x duplicado

É importante entender a diferença entre reenvios esperados e inesperados.

  • O reenvio pode ocorrer devido a uma confirmação negativa iniciada pelo cliente de uma mensagem ou quando o cliente não estender o prazo de confirmação da mensagem antes que o prazo de confirmação expire. Os resgates são considerados válidos e o sistema está funcionando conforme o esperado.

  • Uma cópia é quando uma mensagem é reenviada após uma confirmação bem-sucedida ou antes de o prazo de confirmação terminar.

O Pub/Sub garante que as assinaturas com entrega exatamente uma vez ativada não recebam entregas duplicadas.

Suporte a entrega exatamente uma vez em bibliotecas de cliente

  • As bibliotecas de cliente compatíveis têm uma interface para confirmação com resposta (exemplo: Go). Você pode usar essa interface para verificar se a solicitação de confirmação foi bem-sucedida. Se a solicitação de confirmação for bem-sucedida, os clientes terão a garantia de não receber um reenvio. Se a solicitação de confirmação falhar, o cliente pode esperar um reenvio.

  • Os clientes também podem usar as bibliotecas de cliente compatíveis sem a interface de confirmação. Nesses casos, as falhas de confirmação podem resultar em reenvios silenciosos de mensagens.

  • As bibliotecas de cliente compatíveis têm interfaces para definir o tempo mínimo da extensão de locação (exemplo: Go). Defina o valor da extensão de locação mínima como um número alto para evitar que a confirmação de rede expire. O valor máximo é definido em 600 segundos.

Crie assinaturas com entrega única

É possível criar uma assinatura com entrega única uma vez usando o console do Google Cloud, a Google Cloud CLI, a biblioteca de cliente ou a API Pub/Sub.

Assinatura de pull

Console

Para criar uma assinatura pull com entrega exatamente uma vez, siga estas etapas:

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

    Acessar "Assinaturas"

  2. Clique em Criar assinatura.

  3. Insira o ID da assinatura.

  4. Escolha ou crie um tópico no menu suspenso.

    A assinatura recebe mensagens do tópico.

  5. Na seção Entrega exatamente uma vez, selecione Ativar entrega exatamente uma vez.

  6. Clique em Criar.

gcloud

Para criar uma assinatura pull com entrega exatamente uma vez, use o comando gcloud pubsub subscriptions create com a sinalização --enable-exactly-once-delivery:

gcloud pubsub subscriptions create SUBSCRIPTION_ID \
  --topic=TOPIC_ID \
  --enable-exactly-once-delivery

Substitua:

  • SUBSCRIPTION_ID: o ID da assinatura a ser criada
  • TOPIC_ID: o ID do tópico a ser anexado à assinatura

REST

Para criar uma assinatura com entrega exatamente uma vez, use o método projects.subscriptions.create.

PUT https://pubsub.googleapis.com/v1/projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID
Authorization: Bearer $(gcloud auth print-access-token)

Substitua:

  • PROJECT_ID: o ID do projeto para criar a assinatura
  • SUBSCRIPTION_ID: o ID da assinatura a ser criada

Para criar uma assinatura de pull com entrega exatamente uma vez, especifique isso no corpo da solicitação:

{
  "topic": "projects/PROJECT_ID/topics/TOPIC_ID",
  "enableExactlyOnceDelivery": true,
}

Substitua:

  • PROJECT_ID: o ID do projeto com o tópico
  • TOPIC_ID: o ID do tópico a ser anexado à assinatura

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;
[](pubsub::SubscriptionAdminClient client, std::string const& project_id,
   std::string const& topic_id, std::string const& subscription_id) {
  auto sub = client.CreateSubscription(
      pubsub::Topic(project_id, topic_id),
      pubsub::Subscription(project_id, subscription_id),
      pubsub::SubscriptionBuilder{}.enable_exactly_once_delivery(true));
  if (sub.status().code() == google::cloud::StatusCode::kAlreadyExists) {
    std::cout << "The subscription already exists\n";
    return;
  }
  if (!sub) throw std::runtime_error(sub.status().message());

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

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#.


using Google.Cloud.PubSub.V1;
using Grpc.Core;

public class CreateSubscriptionWithExactlyOnceDeliverySample
{
    public Subscription CreateSubscriptionWithExactlyOnceDelivery(string projectId, string topicId, string subscriptionId)
    {
        SubscriberServiceApiClient subscriber = SubscriberServiceApiClient.Create();
        TopicName topicName = TopicName.FromProjectTopic(projectId, topicId);
        SubscriptionName subscriptionName = SubscriptionName.FromProjectSubscription(projectId, subscriptionId);

        var subscriptionRequest = new Subscription
        {
            SubscriptionName = subscriptionName,
            TopicAsTopicName = topicName,
            EnableExactlyOnceDelivery = true
        };

        Subscription subscription = null;

        try
        {
            subscription = subscriber.CreateSubscription(subscriptionRequest);
        }
        catch (RpcException e) when (e.Status.StatusCode == StatusCode.AlreadyExists)
        {
            // Already exists.  That's fine.
        }
        return subscription;
    }
}

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"

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

func createSubscriptionWithExactlyOnceDelivery(w io.Writer, projectID, subID string, topic *pubsub.Topic) error {
	// projectID := "my-project-id"
	// subID := "my-sub"
	// topic of type https://godoc.org/cloud.google.com/go/pubsub#Topic
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %v", err)
	}
	defer client.Close()

	sub, err := client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
		Topic:                     topic,
		EnableExactlyOnceDelivery: true,
	})
	if err != nil {
		return err
	}
	fmt.Fprintf(w, "Created a subscription with exactly once delivery enabled: %v\n", sub)
	return nil
}

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.

import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.Subscription;
import java.io.IOException;

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

    createSubscriptionWithExactlyOnceDeliveryExample(projectId, topicId, subscriptionId);
  }

  public static void createSubscriptionWithExactlyOnceDeliveryExample(
      String projectId, String topicId, String subscriptionId) throws IOException {
    try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) {

      ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
      ProjectSubscriptionName subscriptionName =
          ProjectSubscriptionName.of(projectId, subscriptionId);

      Subscription subscription =
          subscriptionAdminClient.createSubscription(
              Subscription.newBuilder()
                  .setName(subscriptionName.toString())
                  .setTopic(topicName.toString())
                  // Enable exactly once delivery in the subscription.
                  .setEnableExactlyOnceDelivery(true)
                  .build());

      System.out.println(
          "Created a subscription with exactly once delivery enabled: "
              + subscription.getAllFields());
    }
  }
}

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): Choose an existing topic.
# project_id = "your-project-id"
# topic_id = "your-topic-id"
# subscription_id = "your-subscription-id"

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)

with subscriber:
    subscription = subscriber.create_subscription(
        request={
            "name": subscription_path,
            "topic": topic_path,
            "enable_exactly_once_delivery": True,
        }
    )
    print(
        f"Created subscription with exactly once delivery enabled: {subscription}"
    )

Ruby

Antes de tentar esse exemplo, siga as instruções de configuração do Ruby 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 Ruby.

require "google/cloud/pubsub"

# Shows how to create a new subscription with exactly once delivery enabled
class PubsubCreateSubscriptionWithExactlyOnceDelivery
  def create_subscription_with_exactly_once_delivery project_id:, topic_id:, subscription_id:
    pubsub = Google::Cloud::Pubsub.new project_id: project_id
    topic = pubsub.topic topic_id
    subscription = topic.subscribe subscription_id, enable_exactly_once_delivery: true
    puts "Created subscription with exactly once delivery enabled: #{subscription_id}"
  end

  def self.run
    # TODO(developer): Replace these variables before running the sample.
    project_id = "your-project-id"
    topic_id = "your-topic-id"
    subscription_id = "id-for-new-subcription"
    pubsub_create_subscription_with_exactly_once_delivery = PubsubCreateSubscriptionWithExactlyOnceDelivery.new
    pubsub_create_subscription_with_exactly_once_delivery.create_subscription_with_exactly_once_delivery(
      project_id: project_id,
      topic_id: topic_id,
      subscription_id: subscription_id
    )
  end
end

if $PROGRAM_NAME == __FILE__
  PubsubCreateSubscriptionWithExactlyOnceDelivery.run
end

PHP

Antes de tentar esse exemplo, siga as instruções de configuração do PHP 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 PHP.

use Google\Cloud\PubSub\PubSubClient;

/**
 * Creates a Pub/Sub subscription with `Exactly Once Delivery` enabled.
 *
 * @param string $projectId  The Google project ID.
 * @param string $topicName  The Pub/Sub topic name.
 * @param string $subscriptionName  The Pub/Sub subscription name.
 */
function create_subscription_with_exactly_once_delivery(
    string $projectId,
    string $topicName,
    string $subscriptionName
): void {
    $pubsub = new PubSubClient([
        'projectId' => $projectId,
    ]);
    $topic = $pubsub->topic($topicName);
    $subscription = $topic->subscription($subscriptionName);
    $subscription->create([
        'enableExactlyOnceDelivery' => true
    ]);

    // Exactly Once Delivery status for the subscription
    $status = $subscription->info()['enableExactlyOnceDelivery'];

    printf('Subscription created with exactly once delivery status: %s' . PHP_EOL, $status ? 'true' : 'false');
}

Monitorar assinaturas de entrega exatamente uma vez

A métrica subscription/exactly_once_warning_count registra o número de eventos que podem levar a possíveis resgates (válidos ou duplicados). Essa métrica contabiliza as vezes que o Pub/Sub falha ao processar solicitações associadas a IDs de confirmação (pedido de modificaçãoAckDeadline ou Acconfirmarledgment). Os motivos da falha podem ser baseados no servidor ou no cliente. Por exemplo, se a camada de persistência usada para manter as informações de entrega exatamente uma vez indisponíveis, for um evento baseado no servidor. Se o cliente tentar confirmar uma mensagem com um ID de confirmação inválido, será um evento baseado no cliente.

Entenda a métrica

A subscription/exactly_once_warning_count captura eventos que podem ou não levar os resgates reais e podem fazer barulho com base no comportamento do cliente. Por exemplo: solicitações repetidas de "Acawareledgment" ou "ModificarAckDeadline" com IDs de confirmação inválidos incrementam a métrica repetidamente.

As métricas a seguir também são úteis para entender o comportamento do cliente:

  • A métrica subscription/expired_ack_deadlines_count mostra o número de expirações de IDs de confirmação. As expirações de ID de confirmação podem levar a falhas nas solicitações ModifiqueAckDeadline e no reconhecimento.

  • A métrica service.serviceruntime.googleapis.com/api/request_count pode ser usada para capturar falhas de RequestAckDeadline ou para solicitações de confirmação nos casos em que as solicitações chegam ao Google Cloud, mas não alcançam o Pub/Sub. Há falhas que essa métrica não capturará, por exemplo, quando os clientes são desconectados do Google Cloud.

Na maioria dos casos de eventos de falha que podem ser repetidos, as bibliotecas de cliente compatíveis repetem a solicitação automaticamente.

Cotas

As assinaturas de entrega exatamente uma vez estão sujeitas a outros requisitos de cota. Essas cotas são aplicadas em:

  • Número de mensagens consumidas das assinaturas com entrega exatamente uma vez ativada por região.
  • Número de mensagens confirmadas ou com um prazo estendido ao usar assinaturas com entrega única uma vez ativada por região.

Para mais informações sobre essas cotas, consulte a tabela no tópico Cotas.

Entrega exatamente uma vez e push

O Pub/Sub é compatível com entrega exatamente uma vez apenas com assinaturas Pull.

As assinaturas de push reconhecem mensagens respondendo às solicitações de push com uma resposta bem-sucedida. No entanto, os clientes não sabem se o Pub/Sub recebeu a resposta e a processou. Isso é diferente das assinaturas de pull, em que as solicitações de confirmação são iniciadas pelos clientes e o Pub/Sub responde se a solicitação foi processada com êxito. Por isso, as semânticas de entrega exatamente uma vez não se alinham bem às assinaturas de push.

Suporte para chaves de pedido

A entrega exatamente uma vez não é compatível com a entrega solicitada.

Informações úteis

  • Se o prazo de confirmação não for especificado no momento da CreateSubscription, as assinaturas ativadas para entrega terão um prazo padrão de confirmação de 60 segundos.

  • Prazos de confirmação padrão mais longos são benéficos para evitar o reenvio causado por eventos de rede. As bibliotecas de cliente compatíveis não usam o prazo de confirmação de assinatura padrão.

  • As assinaturas de entrega exatamente uma vez têm uma latência de publicação para assinatura significativamente maior em comparação com as assinaturas normais.

  • Uma assinatura pode receber várias cópias da mesma mensagem devido à duplicação de publicação, mesmo com a entrega exatamente uma vez ativada. As duplicações da publicação podem ocorrer devido a várias tentativas únicas de publicação pelo cliente de publicação ou pelo serviço do Pub/Sub. Quando um cliente publica várias publicações únicas, entre tentativas, leva a resgates com diferentes IDs de mensagens. Várias publicações únicas pelo serviço Pub/Sub, para responder a uma solicitação de publicação do cliente, leva a resgates com os mesmos IDs de mensagem.

  • Falhas além dos IDs de confirmação inválidos em subscription/exactly_once_warning_count são repetíveis, e as bibliotecas de cliente compatíveis farão novas tentativas automaticamente.