Ordenar mensajes

El ordenamiento de mensajes es una función de Pub/Sub que te permite recibir mensajes en tus clientes suscriptores en el orden en que los publicaron los clientes publicador.

Por ejemplo, supongamos que un cliente publicador en una región publica los mensajes 1, 2 y 3 en orden. Con el ordenamiento de los mensajes, el cliente suscriptor recibe los mensajes publicados en el mismo orden. Para que se entreguen en orden, el cliente publicador debe publicar los mensajes en la misma región.

El ordenamiento de mensajes es una función útil para situaciones como la captura de cambios de la base de datos, el seguimiento de las sesiones de usuario y las aplicaciones de transmisión en las que es importante preservar la cronología de los eventos.

En esta página, se explica el concepto de ordenar mensajes y cómo configurar tus clientes suscriptores para recibir mensajes en orden. Si deseas configurar los clientes publicador para ordenar los mensajes, consulta Usa claves de ordenamiento para publicar un mensaje.

Descripción general del orden de los mensajes

Los pedidos en Pub/Sub se determinan de la siguiente manera:

  • Clave de pedido: Es una string que se usa en los metadatos de mensajes de Pub/Sub y representa la entidad para la que se deben ordenar los mensajes. La clave de ordenamiento puede tener hasta 1 KB. Para recibir un conjunto de mensajes ordenados en una región, debes publicar todos los mensajes con la misma clave de ordenamiento en la misma región. Algunos ejemplos de claves de pedido son los ID de cliente y la clave primaria de una fila en una base de datos.

    La capacidad de procesamiento de publicación en cada clave de ordenamiento está limitada a 1 MBps. La capacidad de procesamiento de todas las claves de ordenamiento de un tema se limita a la cuota disponible en una región de publicación. Este límite se puede aumentar a muchas unidades de GBps.

    Una clave de ordenamiento no equivale a una partición en un sistema de mensajería basado en particiones, ya que se espera que las claves de ordenamiento tengan una cardinalidad mucho mayor que las particiones.

  • Habilitar el ordenamiento de mensajes: Esta es una configuración de suscripción. Cuando una suscripción tiene habilitado el ordenamiento de mensajes, los clientes suscriptores reciben mensajes publicados en la misma región con la misma clave de ordenamiento en el orden en que el servicio los recibió. Debes habilitar esta configuración en la suscripción.

    Supongamos que tienes dos suscripciones A y B vinculadas al mismo tema T. La suscripción A se configura con el ordenamiento de mensajes habilitado y la suscripción B se configura sin el ordenamiento de mensajes habilitado. En esta arquitectura, las suscripciones A y B reciben el mismo conjunto de mensajes del tema T. Si publicas mensajes con claves de ordenamiento en la misma región, la suscripción A recibe los mensajes en el orden en que se publicaron. En cambio, la suscripción B recibe los mensajes sin ningún orden previsto.

En general, si tu solución requiere que los clientes publicadores envíen mensajes ordenados y desordenados, crea temas separados: uno para mensajes ordenados y otro para mensajes no ordenados.

Consideraciones para usar mensajes ordenados

La siguiente lista contiene información importante sobre el comportamiento de la mensajería ordenada en Pub/Sub:

  • Orden dentro de la clave: Se espera que los mensajes publicados con la misma clave de ordenamiento se reciban en orden. Supongamos que para ordenar la clave A, publicas los mensajes 1, 2 y 3. Si se habilitan los pedidos, se espera que 1 se entregue antes que 2, y que 2 se entregue antes de las 3.

  • Orden entre claves: No se espera que los mensajes publicados con diferentes claves de ordenamiento se reciban en orden. Supongamos que tienes las claves de pedido A y B. Para la clave de ordenamiento A, los mensajes 1 y 2 se publican en orden. Para la clave de orden B, los mensajes 3 y 4 se publican en orden. Sin embargo, el mensaje 1 podría llegar antes o después del mensaje 4.

  • Reenvío de mensajes: Pub/Sub entrega cada mensaje al menos una vez, por lo que el servicio de Pub/Sub podría volver a entregarlos. Los reintentos de envío de un mensaje activan la entrega de todos los mensajes posteriores para esa clave, incluso de los confirmados. Supongamos que un cliente suscriptor recibe los mensajes 1, 2 y 3 de una clave de ordenamiento específica. Si se vuelve a entregar el mensaje 2 (porque el plazo de confirmación venció o no se mantuvo la confirmación de mejor esfuerzo en Pub/Sub), también se vuelve a entregar el mensaje 3. Si el ordenamiento de los mensajes y un tema de mensajes no entregados están habilitados en una suscripción, es posible que este comportamiento no sea cierto, ya que Pub/Sub reenvía mensajes a temas de mensajes no entregados según el criterio del mejor esfuerzo.

  • Demoras en la confirmación de recepción y temas de mensajes no entregados: Los mensajes no confirmados para una clave de ordenamiento determinada pueden retrasar la entrega de los mensajes de otras claves de ordenamiento, en especial durante los reinicios del servidor o los cambios de tráfico. Para mantener el orden durante esos eventos, garantiza el reconocimiento oportuno de todos los mensajes. Si no es posible recibir una confirmación oportuna, considera usar un tema de mensajes no entregados para evitar la retención de mensajes indefinidos. Ten en cuenta que es posible que no se conserve el orden cuando los mensajes se escriben en un tema de mensajes no entregados.

  • Afinidad de mensajes (clientes de streamingPull): Por lo general, los mensajes para la misma clave se entregan al mismo cliente suscriptor de streamingPull. Se espera afinidad cuando los mensajes son pendientes de una clave de pedido para un cliente suscriptor específico. Si no hay mensajes pendientes, la afinidad podría cambiar para el balanceo de cargas o las desconexiones del cliente.

    Para garantizar un procesamiento sin inconvenientes, incluso con posibles cambios de afinidad, es fundamental diseñar tu aplicación streamingPull de manera que pueda controlar mensajes en cualquier cliente para una clave de ordenamiento determinada.

  • Integración en Dataflow: No habilites el ordenamiento de mensajes para suscripciones cuando configures Dataflow con Pub/Sub. Dataflow tiene su propio mecanismo para ordenar todos los mensajes, lo que garantiza el orden cronológico en todos los mensajes como parte de las operaciones de renderización en ventanas. Este método de ordenamiento difiere del enfoque basado en claves de ordenamiento de Pub/Sub. El uso de claves de pedido con Dataflow puede reducir el rendimiento de la canalización.

  • Ajuste de escala automático: La entrega ordenada de Pub/Sub escala a miles de millones de claves de ordenamiento. Un número mayor de claves de ordenamiento permite una entrega más paralela a los suscriptores, ya que el ordenamiento se aplica a todos los mensajes con la misma clave de ordenamiento.

Sin embargo, los envíos pedidos tienen algunas compensaciones. En comparación con la entrega desordenada, la entrega ordenada puede disminuir ligeramente la disponibilidad de publicación y aumentar la latencia de entrega de mensajes de extremo a extremo. En el caso de entrega ordenado, la conmutación por error requiere coordinación para garantizar que los mensajes se escriban y lean en el orden correcto.

Si quieres obtener más información para usar el ordenamiento de los mensajes, consulta los siguientes temas de prácticas recomendadas:

Comportamiento del cliente del suscriptor para el ordenamiento de los mensajes

Los clientes suscriptores reciben mensajes en el orden en que se publicaron en una región específica. Pub/Sub admite diferentes formas de recibir mensajes, como clientes suscriptores conectados a suscripciones de extracción y de envío. Las bibliotecas cliente usan streamingPull (excepto PHP).

Para obtener más información sobre estos tipos de suscripción, consulta Cómo elegir un tipo de suscripción.

En las siguientes secciones, se explica qué significa la recepción de mensajes en orden para cada tipo de cliente suscriptor.

Clientes suscriptores de StreamingPull

Cuando usas las bibliotecas cliente con streamingPull, debes especificar una devolución de llamada de usuario que se ejecute cada vez que un cliente suscriptor reciba un mensaje. Con las bibliotecas cliente, para cualquier clave de ordenamiento determinada, se ejecuta la devolución de llamada hasta la finalización de los mensajes en el orden correcto. Si los mensajes se confirman dentro de esa devolución de llamada, todos los cálculos de un mensaje se realizan en orden. Sin embargo, si la devolución de llamada del usuario programa otro trabajo asíncrono en los mensajes, el cliente suscriptor debe asegurarse de que este trabajo se realice en orden. Una opción es agregar mensajes a una cola de trabajo local que se procese en orden.

Clientes suscriptores de extracción

En el caso de los clientes suscriptores conectados con suscripciones de extracción, el ordenamiento de mensajes de Pub/Sub admite lo siguiente:

  • Todos los mensajes para una clave de ordenamiento en PullResponse están en el orden adecuado en la lista.

  • Solo puede haber un lote de mensajes pendiente para una clave de ordenamiento a la vez.

El requisito de que solo un lote de mensajes pueda estar pendiente a la vez es necesario para mantener la entrega ordenada, ya que el servicio de Pub/Sub no puede garantizar el éxito o la latencia de la respuesta que envía para la solicitud de extracción de un suscriptor.

Clientes suscriptores de envío

Las restricciones de empuje son incluso más estrictas que las de tirar. En el caso de una suscripción de envío, Pub/Sub solo admite un mensaje pendiente por cada clave de pedido a la vez. Cada mensaje se envía a un extremo de envío como una solicitud independiente. Por lo tanto, enviar las solicitudes en paralelo tendría el mismo problema que entregar varios lotes de mensajes para la misma clave de ordenamiento a fin de extraer suscriptores de forma simultánea. Es posible que las suscripciones de envío no sean una buena opción para temas en los que los mensajes se publican con frecuencia con la misma clave de ordenamiento o en los que la latencia es extremadamente importante.

Exporta los clientes suscriptores

La exportación de suscripciones admite mensajes ordenados. En el caso de las suscripciones de BigQuery, los mensajes con la misma clave de ordenamiento se escriben en su tabla de BigQuery en orden. Para las suscripciones a Cloud Storage, es posible que los mensajes con la misma clave de ordenamiento no se escriban todos en el mismo archivo. Cuando están dentro del mismo archivo, los mensajes para una clave de ordenamiento están en orden. Cuando se distribuyen en varios archivos, los mensajes posteriores de una clave de ordenamiento pueden aparecer en un archivo con un nombre que tenga una marca de tiempo anterior a la marca de tiempo del nombre del archivo con los mensajes anteriores.

Habilitar el ordenamiento de los mensajes

Para recibir los mensajes en orden, configura la propiedad de ordenamiento de mensajes de la suscripción de la que recibes mensajes. Recibir mensajes en orden puede aumentar la latencia. No puedes cambiar la propiedad de ordenamiento de mensajes después de crear una suscripción.

Puedes configurar la propiedad de ordenamiento de mensajes cuando creas una suscripción mediante la consola de Google Cloud, Google Cloud CLI o la API de Pub/Sub.

Console

Para crear una suscripción con la propiedad de ordenamiento de mensajes, sigue estos pasos:

  1. En la consola de Google Cloud, ve a la página Suscripciones.

Ir a Suscripciones

  1. Haz clic en Crear suscripción.

  2. Ingresa un ID de suscripción.

  3. Elige el tema del que deseas recibir mensajes.

  4. En la sección Ordenamiento de mensajes, selecciona Ordenar mensajes con una clave de ordenamiento.

  5. Haz clic en Crear.

gcloud

Para crear una suscripción con la propiedad de ordenamiento de mensajes, usa el comando gcloud pubsub subscriptions create y la marca --enable-message-ordering:

gcloud pubsub subscriptions create SUBSCRIPTION_ID \
  --enable-message-ordering

Reemplaza SUBSCRIPTION_ID por el ID de la suscripción.

Si la solicitud es exitosa, la línea de comandos muestra una confirmación:

Created subscription [SUBSCRIPTION_ID].

REST

Para crear una suscripción con la propiedad de ordenamiento de mensajes, envía una solicitud PUT como la siguiente:

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

Reemplaza lo siguiente:

  • PROJECT_ID: El ID del proyecto con el tema
  • SUBSCRIPTION_ID: El ID de la suscripción

Especifica lo siguiente en el cuerpo de la solicitud:

{
  "topic": TOPIC_ID,
  "enableMessageOrdering": true,
}

Reemplaza TOPIC_ID por el ID del tema que deseas adjuntar a la suscripción.

Si la solicitud se realiza de forma correcta, la respuesta es la suscripción en formato JSON:

{
  "name": projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID,
  "topic": projects/PROJECT_ID/topics/TOPIC_ID,
  "enableMessageOrdering": true,
}

C++

Antes de probar esta muestra, sigue las instrucciones de configuración de C++ en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para 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) {
  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.set_enable_message_ordering(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";
}

C#

Antes de probar esta muestra, sigue las instrucciones de configuración de C# en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para C#.


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

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

        var subscriptionRequest = new Subscription
        {
            SubscriptionName = subscriptionName,
            TopicAsTopicName = topicName,
            EnableMessageOrdering = 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 probar esta muestra, sigue las instrucciones de configuración de Go en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para Go.

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

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

func createWithOrdering(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: %w", err)
	}
	defer client.Close()

	// Message ordering can only be set when creating a subscription.
	sub, err := client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
		Topic:                 topic,
		AckDeadline:           20 * time.Second,
		EnableMessageOrdering: true,
	})
	if err != nil {
		return fmt.Errorf("CreateSubscription: %w", err)
	}
	fmt.Fprintf(w, "Created subscription: %v\n", sub)
	return nil
}

Java

Antes de probar esta muestra, sigue las instrucciones de configuración de Java en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para 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 CreateSubscriptionWithOrdering {
  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";

    createSubscriptionWithOrderingExample(projectId, topicId, subscriptionId);
  }

  public static void createSubscriptionWithOrderingExample(
      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())
                  // Set message ordering to true for ordered messages in the subscription.
                  .setEnableMessageOrdering(true)
                  .build());

      System.out.println("Created a subscription with ordering: " + subscription.getAllFields());
    }
  }
}

Node.js

Antes de probar esta muestra, sigue las instrucciones de configuración de Node.js en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para Node.js.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// 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 createSubscriptionWithOrdering(
  topicNameOrId,
  subscriptionNameOrId
) {
  // Creates a new subscription
  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, {
      enableMessageOrdering: true,
    });
  console.log(
    `Created subscription ${subscriptionNameOrId} with ordering enabled.`
  );
  console.log(
    'To process messages in order, remember to add an ordering key to your messages.'
  );
}

Node.js

Antes de probar esta muestra, sigue las instrucciones de configuración de Node.js en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para Node.js.

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

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

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

async function createSubscriptionWithOrdering(
  topicNameOrId: string,
  subscriptionNameOrId: string
) {
  // Creates a new subscription
  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, {
      enableMessageOrdering: true,
    });
  console.log(
    `Created subscription ${subscriptionNameOrId} with ordering enabled.`
  );
  console.log(
    'To process messages in order, remember to add an ordering key to your messages.'
  );
}

Python

Antes de probar esta muestra, sigue las instrucciones de configuración de Python en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para 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_message_ordering": True,
        }
    )
    print(f"Created subscription with ordering: {subscription}")

Rita

Antes de probar esta muestra, sigue las instrucciones de configuración de Ruby en la guía de inicio rápido sobre el uso de bibliotecas cliente. Si quieres obtener más información, consulta la documentación de referencia de la API de Pub/Sub para Ruby.

# topic_id        = "your-topic-id"
# subscription_id = "your-subscription-id"

pubsub = Google::Cloud::Pubsub.new

topic        = pubsub.topic topic_id
subscription = topic.subscribe subscription_id,
                               message_ordering: true

puts "Pull subscription #{subscription_id} created with message ordering."

¿Qué sigue?