Resolução de problemas gerais

Saiba mais sobre os passos de resolução de problemas que podem ser úteis se tiver problemas ao usar o Pub/Sub.

Não é possível criar um tópico

Verifique se tem as autorizações necessárias. Para criar um tópico do Pub/Sub, precisa da função de gestão de identidades e acessos (IAM) de editor do Pub/Sub (roles/pubsub.editor) no projeto. Se não tiver esta função, contacte o seu administrador. Para mais informações de resolução de problemas relacionadas com tópicos, consulte as seguintes páginas:

Não é possível criar uma subscrição

Verifique se fez o seguinte:

  • Verifique se tem as autorizações necessárias. Para criar uma subscrição do Pub/Sub, precisa da função IAM Editor do Pub/Sub (roles/pubsub.editor) no projeto. Se não tiver esta função, contacte o seu administrador.

  • Especificou um nome para a subscrição.

  • Especificou o nome de um tópico existente ao qual quer anexar a subscrição.

  • Se estiver a criar uma subscrição push, especifique https:// em letras minúsculas (não http:// nem HTTPS://) como o protocolo para o seu URL de receção no campo pushEndpoint.

Para mais informações de resolução de problemas sobre subscrições, consulte as seguintes páginas:

Resolva problemas de autorizações

As autorizações do Pub/Sub controlam que utilizadores e contas de serviço podem realizar ações nos seus recursos do Pub/Sub. Quando as autorizações estão configuradas incorretamente, podem originar erros de autorização recusada e interromper o fluxo de mensagens. Os registos de auditoria fornecem um registo detalhado de todas as alterações de autorizações, o que lhe permite identificar a origem destes problemas.

Para resolver problemas de autorização do Pub/Sub com registos de auditoria:

  1. Obtenha as autorizações necessárias para ver o Explorador de registos.

    Para mais informações, consulte a secção Antes de começar.

  2. Na Trusted Cloud consola, aceda à página Explorador de registos.

    Aceda ao Explorador de registos

  3. Selecione um Trusted Cloud projeto, uma pasta ou uma organização existente.

  4. Segue-se uma lista de filtros que pode usar para encontrar registos relevantes:

    • resource.type="pubsub_topic" OR resource.type="pubsub_subscription": Use esta consulta como ponto de partida quando estiver a resolver problemas que possam envolver alterações às configurações de tópicos ou subscrições, ou ao controlo de acesso. Pode combiná-lo com outros filtros para refinar ainda mais a pesquisa.

    • protoPayload.methodName="google.iam.v1.SetIamPolicy": use esta consulta quando suspeitar que um problema é causado por autorizações incorretas ou em falta. Ajuda a acompanhar quem fez alterações à política de IAM e quais foram essas alterações. Isto pode ser útil para resolver problemas, como utilizadores que não conseguem publicar em tópicos ou subscrever subscrições, aplicações com acesso negado a recursos do Pub/Sub ou alterações inesperadas no controlo de acesso.

    • protoPayload.status.code=7: use esta consulta quando encontrar erros relacionados explicitamente com autorizações. Isto ajuda a identificar as ações que estão a falhar e quem as está a tentar. Pode combinar esta consulta com as anteriores para identificar o recurso específico e a alteração da política de IAM que podem estar a causar a recusa de autorização.

  5. Analise os registos para determinar fatores como a data/hora do evento, o principal que fez a alteração e o tipo de alterações feitas.

  6. Com base nas informações recolhidas dos registos de auditoria, pode tomar medidas corretivas.

Resolva problemas de autorizações do Terraform

Quando usar o Pub/Sub com o Terraform, conceda explicitamente as funções necessárias no seu código do Terraform. Por exemplo, para a publicação, a conta de serviço da sua aplicação precisa da função roles/pubsub.publisher. Se este papel não estiver definido explicitamente no seu código do Terraform, uma futura atualização terraform apply pode removê-lo. Isto acontece frequentemente durante atualizações não relacionadas, o que faz com que uma aplicação fiável falhe subitamente com erros PERMISSION_DENIED. Definir explicitamente a função no seu código impede estas regressões acidentais.

A subscrição foi eliminada

As subscrições do Pub/Sub podem ser eliminadas de duas formas principais:

  • Um utilizador ou uma conta de serviço com autorizações suficientes elimina intencionalmente a subscrição.

  • Uma subscrição é eliminada automaticamente após um período de inatividade, que é de 31 dias por predefinição. Para mais informações acerca da política de validade da subscrição, consulte o artigo Período de validade.

Para resolver problemas relacionados com uma subscrição eliminada, siga estes passos:

  1. Na Trusted Cloud consola, aceda à página de subscrições do Pub/Sub e verifique se a subscrição já não está listada. Para mais informações sobre como listar subscrições, consulte o artigo Liste uma subscrição.

  2. Verifique os registos de auditoria. Navegue para o Explorador de registos. Use o filtro protoPayload.methodName="google.pubsub.v1.Subscriber.DeleteSubscription" para encontrar subscrições eliminadas. Examine os registos para determinar se alguém eliminou a subscrição ou se esta foi eliminada devido a inatividade. InternalExpireInactiveSubscription indica que uma subscrição foi eliminada devido a inatividade. Para mais informações sobre como usar os registos de auditoria para resolução de problemas, consulte o artigo Resolva problemas do Pub/Sub com registos de auditoria.

Erro 403 (Forbidden)

Normalmente, um erro 403 significa que não tem as autorizações corretas para realizar uma ação. Por exemplo, pode receber um erro 403 User not authorized ao tentar publicar num tópico ou extrair de uma subscrição.

Se receber este erro, faça o seguinte:

  • Certifique-se de que ativou a API Pub/Sub na Trusted Cloud consola.
  • Certifique-se de que o principal que faz o pedido tem as autorizações necessárias nos recursos da API Pub/Sub relevantes, especialmente se estiver a usar a API Pub/Sub para comunicação entre projetos.

  • Se estiver a usar o Dataflow, certifique-se de que o {PROJECT_NUMBER}@cloudservices.s3ns-system.iam.gserviceaccount.com e a conta de serviço do Compute Engine {PROJECT_NUMBER}-compute@developer.s3ns-system.iam.gserviceaccount.com têm as autorizações necessárias no recurso da API Pub/Sub relevante. Para mais informações, consulte o artigo Segurança e autorizações do Dataflow.

  • Se estiver a usar o App Engine, verifique a página de autorizações do seu projeto para ver se uma conta de serviço do App Engine está listada como editor do Pub/Sub. Se não for o caso, adicione a sua conta de serviço do App Engine como editor do Pub/Sub. Normalmente, a conta de serviço do App Engine tem o formato <project-id>@appspot.s3ns-system.iam.gserviceaccount.com.

  • Pode usar os registos de auditoria para resolver problemas de autorizações.

Outros códigos de erro comuns

Para ver uma lista de outros códigos de erro comuns relacionados com a API Pub/Sub e as respetivas descrições, consulte Códigos de erro.

Usar operações administrativas excessivas

Se verificar que está a usar demasiada da sua quota para operações administrativas, pode ter de refatorar o seu código. Como ilustração, considere o seguinte pseudocódigo. Neste exemplo, está a ser usada uma operação administrativa (GET) para verificar a presença de uma subscrição antes de tentar consumir os respetivos recursos. GET e CREATE são operações de administrador:

if !GetSubscription my-sub {
  CreateSubscription my-sub
}
Consume from subscription my-sub

Um padrão mais eficiente é tentar consumir mensagens da subscrição (partindo do princípio de que pode ter uma certeza razoável do nome da subscrição). Nesta abordagem otimista, só recebe ou cria a subscrição se houver um erro. Considere este exemplo:

try {
  Consume from subscription my-sub
} catch NotFoundError {
  CreateSubscription my-sub
  Consume from subscription my-sub
}

Pode usar os seguintes exemplos de código para implementar este padrão no idioma da sua escolha:

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"
	"errors"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/pubsub/v2"
	"cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// optimisticSubscribe shows the recommended pattern for optimistically
// assuming a subscription exists prior to receiving messages.
func optimisticSubscribe(w io.Writer, projectID, topic, subscriptionName string) error {
	// projectID := "my-project-id"
	// topic := "projects/my-project-id/topics/my-topic"
	// subscription := "projects/my-project/subscriptions/my-sub"
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %w", err)
	}
	defer client.Close()

	// client.Subscriber can be passed a subscription ID (e.g. "my-sub") or
	// a fully qualified name (e.g. "projects/my-project/subscriptions/my-sub").
	// If a subscription ID is provided, the project ID from the client is used.
	sub := client.Subscriber(subscriptionName)

	// Receive messages for 10 seconds, which simplifies testing.
	// Comment this out in production, since `Receive` should
	// be used as a long running operation.
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	// Instead of checking if the subscription exists, optimistically try to
	// receive from the subscription assuming it exists.
	err = sub.Receive(ctx, func(_ context.Context, msg *pubsub.Message) {
		fmt.Fprintf(w, "Got from existing subscription: %q\n", string(msg.Data))
		msg.Ack()
	})
	if err != nil {
		if st, ok := status.FromError(err); ok {
			if st.Code() == codes.NotFound {
				// If the subscription does not exist, then create the subscription.
				subscription, err := client.SubscriptionAdminClient.CreateSubscription(ctx, &pubsubpb.Subscription{
					Name:  subscriptionName,
					Topic: topic,
				})
				if err != nil {
					return err
				}
				fmt.Fprintf(w, "Created subscription: %q\n", subscriptionName)

				// client.Subscriber can be passed a subscription ID (e.g. "my-sub") or
				// a fully qualified name (e.g. "projects/my-project/subscriptions/my-sub").
				// If a subscription ID is provided, the project ID from the client is used.
				sub = client.Subscriber(subscription.GetName())
				err = sub.Receive(ctx, func(ctx context.Context, msg *pubsub.Message) {
					fmt.Fprintf(w, "Got from new subscription: %q\n", string(msg.Data))
					msg.Ack()
				})
				if err != nil && !errors.Is(err, context.Canceled) {
					return err
				}
			}
		}
	}
	return nil
}

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.


import com.google.api.gax.rpc.NotFoundException;
import com.google.cloud.pubsub.v1.AckReplyConsumer;
import com.google.cloud.pubsub.v1.MessageReceiver;
import com.google.cloud.pubsub.v1.Subscriber;
import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.PubsubMessage;
import com.google.pubsub.v1.PushConfig;
import com.google.pubsub.v1.Subscription;
import com.google.pubsub.v1.TopicName;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class OptimisticSubscribeExample {
  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";

    optimisticSubscribeExample(projectId, subscriptionId, topicId);
  }

  public static void optimisticSubscribeExample(
      String projectId, String subscriptionId, String topicId) throws IOException {
    ProjectSubscriptionName subscriptionName =
        ProjectSubscriptionName.of(projectId, subscriptionId);

    // Instantiate an asynchronous message receiver.
    MessageReceiver receiver =
        (PubsubMessage message, AckReplyConsumer consumer) -> {
          // Handle incoming message, then ack the received message.
          System.out.println("Id: " + message.getMessageId());
          System.out.println("Data: " + message.getData().toStringUtf8());
          consumer.ack();
        };

    Subscriber subscriber = null;
    try {
      subscriber = Subscriber.newBuilder(subscriptionName, receiver).build();

      // Listen for resource NOT_FOUND errors and rebuild the  subscriber and restart subscribing
      // when the current subscriber encounters these errors.
      subscriber.addListener(
          new Subscriber.Listener() {
            public void failed(Subscriber.State from, Throwable failure) {
              System.out.println(failure.getStackTrace());
              if (failure instanceof NotFoundException) {
                try (SubscriptionAdminClient subscriptionAdminClient =
                    SubscriptionAdminClient.create()) {
                  TopicName topicName = TopicName.of(projectId, topicId);
                  // Create a pull subscription with default acknowledgement deadline of 10 seconds.
                  // The client library will automatically extend acknowledgement deadlines.
                  Subscription subscription =
                      subscriptionAdminClient.createSubscription(
                          subscriptionName, topicName, PushConfig.getDefaultInstance(), 10);
                  System.out.println("Created pull subscription: " + subscription.getName());
                  optimisticSubscribeExample(projectId, subscriptionId, topicId);
                } catch (IOException err) {
                  System.out.println("Failed to create pull subscription: " + err.getMessage());
                }
              }
            }
          },
          MoreExecutors.directExecutor());

      subscriber.startAsync().awaitRunning();
      System.out.printf("Listening for messages on %s:\n", subscriptionName.toString());
      subscriber.awaitTerminated(30, TimeUnit.SECONDS);
    } catch (IllegalStateException e) {
      // Prevent an exception from being thrown if it is the expected NotFoundException
      if (!(subscriber.failureCause() instanceof NotFoundException)) {
        throw e;
      }
    } catch (TimeoutException e) {
      subscriber.stopAsync();
    }
  }
}

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 subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const timeout = 60;

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

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

function optimisticSubscribe(subscriptionNameOrId, topicNameOrId, timeout) {
  // Try using an existing subscription
  let subscription = pubSubClient.subscription(subscriptionNameOrId);

  // Create an event handler to handle messages
  let messageCount = 0;
  const messageHandler = message => {
    console.log(`Received message ${message.id}:`);
    console.log(`\tData: ${message.data}`);
    console.log(`\tAttributes: ${message.attributes}`);
    messageCount += 1;

    // "Ack" (acknowledge receipt of) the message
    message.ack();
  };

  // Set an error handler so that we're notified if the subscription doesn't
  // already exist.
  subscription.on('error', async e => {
    // Resource Not Found
    if (e.code === 5) {
      console.log('Subscription not found, creating it');
      await pubSubClient.createSubscription(
        topicNameOrId,
        subscriptionNameOrId,
      );

      // Refresh our subscriber object and re-attach the message handler.
      subscription = pubSubClient.subscription(subscriptionNameOrId);
      subscription.on('message', messageHandler);
    }
  });

  // Listen for new messages until timeout is hit; this will attempt to
  // open the actual subscriber streams. If it fails, the error handler
  // above will be called.
  subscription.on('message', messageHandler);

  // Wait a while for the subscription to run. (Part of the sample only.)
  setTimeout(() => {
    subscription.removeListener('message', messageHandler);
    console.log(`${messageCount} message(s) received.`);
  }, timeout * 1000);
}

Node.ts

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 subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const timeout = 60;

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

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

function optimisticSubscribe(
  subscriptionNameOrId: string,
  topicNameOrId: string,
  timeout: number,
) {
  // Try using an existing subscription
  let subscription = pubSubClient.subscription(subscriptionNameOrId);

  // Create an event handler to handle messages
  let messageCount = 0;
  const messageHandler = (message: Message) => {
    console.log(`Received message ${message.id}:`);
    console.log(`\tData: ${message.data}`);
    console.log(`\tAttributes: ${message.attributes}`);
    messageCount += 1;

    // "Ack" (acknowledge receipt of) the message
    message.ack();
  };

  // Set an error handler so that we're notified if the subscription doesn't
  // already exist.
  subscription.on('error', async (e: StatusError) => {
    // Resource Not Found
    if (e.code === 5) {
      console.log('Subscription not found, creating it');
      await pubSubClient.createSubscription(
        topicNameOrId,
        subscriptionNameOrId,
      );

      // Refresh our subscriber object and re-attach the message handler.
      subscription = pubSubClient.subscription(subscriptionNameOrId);
      subscription.on('message', messageHandler);
    }
  });

  // Listen for new messages until timeout is hit; this will attempt to
  // open the actual subscriber streams. If it fails, the error handler
  // above will be called.
  subscription.on('message', messageHandler);

  // Wait a while for the subscription to run. (Part of the sample only.)
  setTimeout(() => {
    subscription.removeListener('message', messageHandler);
    console.log(`${messageCount} message(s) received.`);
  }, timeout * 1000);
}

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.api_core.exceptions import NotFound
from google.cloud import pubsub_v1
from concurrent.futures import TimeoutError

# TODO(developer)
# project_id = "your-project-id"
# subscription_id = "your-subscription-id"
# Number of seconds the subscriber should listen for messages
# timeout = 5.0
# topic_id = "your-topic-id"

# Create a subscriber client.
subscriber = pubsub_v1.SubscriberClient()

# The `subscription_path` method creates a fully qualified identifier
# in the form `projects/{project_id}/subscriptions/{subscription_id}`
subscription_path = subscriber.subscription_path(project_id, subscription_id)

# Define callback to be called when a message is received.
def callback(message: pubsub_v1.subscriber.message.Message) -> None:
    # Ack message after processing it.
    message.ack()

# Wrap subscriber in a 'with' block to automatically call close() when done.
with subscriber:
    try:
        # Optimistically subscribe to messages on the subscription.
        streaming_pull_future = subscriber.subscribe(
            subscription_path, callback=callback
        )
        streaming_pull_future.result(timeout=timeout)
    except TimeoutError:
        print("Successfully subscribed until the timeout passed.")
        streaming_pull_future.cancel()  # Trigger the shutdown.
        streaming_pull_future.result()  # Block until the shutdown is complete.
    except NotFound:
        print(f"Subscription {subscription_path} not found, creating it.")

        try:
            # If the subscription does not exist, then create it.
            publisher = pubsub_v1.PublisherClient()
            topic_path = publisher.topic_path(project_id, topic_id)
            subscription = subscriber.create_subscription(
                request={"name": subscription_path, "topic": topic_path}
            )

            if subscription:
                print(f"Subscription {subscription.name} created")
            else:
                raise ValueError("Subscription creation failed.")

            # Subscribe on the created subscription.
            try:
                streaming_pull_future = subscriber.subscribe(
                    subscription.name, callback=callback
                )
                streaming_pull_future.result(timeout=timeout)
            except TimeoutError:
                streaming_pull_future.cancel()  # Trigger the shutdown.
                streaming_pull_future.result()  # Block until the shutdown is complete.
        except Exception as e:
            print(
                f"Exception occurred when creating subscription and subscribing to it: {e}"
            )
    except Exception as e:
        print(f"Exception occurred when attempting optimistic subscribe: {e}")

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.

auto process_response = [](gc::StatusOr<pubsub::PullResponse> response) {
  if (response) {
    std::cout << "Received message " << response->message << "\n";
    std::move(response->handler).ack();
    return gc::Status();
  }
  if (response.status().code() == gc::StatusCode::kUnavailable &&
      response.status().message() == "no messages returned") {
    std::cout << "No messages returned from Pull()\n";
    return gc::Status();
  }
  return response.status();
};

// Instead of checking if the subscription exists, optimistically try to
// consume from the subscription.
auto status = process_response(subscriber.Pull());
if (status.ok()) return;
if (status.code() != gc::StatusCode::kNotFound) throw std::move(status);

// Since the subscription does not exist, create the subscription.
pubsub_admin::SubscriptionAdminClient subscription_admin_client(
    pubsub_admin::MakeSubscriptionAdminConnection());
google::pubsub::v1::Subscription request;
request.set_name(
    pubsub::Subscription(project_id, subscription_id).FullName());
request.set_topic(
    pubsub::Topic(project_id, std::move(topic_id)).FullName());
auto sub = subscription_admin_client.CreateSubscription(request);
if (!sub) throw std::move(sub).status();

// Consume from the new subscription.
status = process_response(subscriber.Pull());
if (!status.ok()) throw std::move(status);