Messaggi di ordine

L'ordinamento dei messaggi è una funzionalità di Pub/Sub che consente di ricevere i messaggi nei client dell'abbonato nell'ordine in cui sono stati pubblicati dai client dell'editore.

Ad esempio, supponiamo che un client editore in una regione pubblichi i messaggi 1, 2 e 3 in ordine. Con l'ordinamento dei messaggi, il client del sottoscrittore riceve i messaggi pubblicati nello stesso ordine. Per essere recapitati in ordine, il cliente dell'editore deve pubblicare i messaggi nella stessa regione.

L'ordinamento dei messaggi è una funzionalità utile per scenari come l'acquisizione di modifiche al database, il monitoraggio delle sessioni utente e le applicazioni di inserimento di flussi in cui è importante preservare la cronologia degli eventi.

Questa pagina spiega il concetto di ordinamento dei messaggi e come configurare i client sottoscrittori per ricevere i messaggi in ordine. Per configurare i client publisher per l'ordinamento dei messaggi, consulta Utilizzare le chiavi di ordinamento per pubblicare un messaggio.

Panoramica dell'ordinamento dei messaggi

L'ordine in Pub/Sub è determinato da quanto segue:

  • Chiave di ordinamento: si tratta di una stringa utilizzata nei metadati dei messaggi Pub/Sub e rappresenta l'entità per la quale i messaggi devono essere ordinati. La chiave di ordinamento può avere una lunghezza massima di 1 kB. Per ricevere un insieme di messaggi ordinati in una regione, devi pubblicare tutti i messaggi con la stessa chiave di ordinamento nella stessa regione. Alcuni esempi di chiavi di ordinamento sono gli ID cliente e la chiave primaria di una riga in un database.

    La velocità effettiva di pubblicazione di ogni chiave di ordinamento è limitata a 1 MBps. La velocità effettiva in tutte le chiavi di ordinamento di un argomento è limitata alla quota disponibile in una regione di pubblicazione. Questo limite può essere aumentato a molte unità di GBps.

    Una chiave di ordinamento non è equivalente a una partizione in un sistema di messaggistica basato su partizioni, poiché le chiavi di ordinamento hanno una cardinalità molto più elevata rispetto alle partizioni.

  • Attiva l'ordinamento dei messaggi: questa è un'impostazione di abbonamento. Quando l'ordinamento dei messaggi è abilitato per una sottoscrizione, i client dell'abbonato ricevono i messaggi pubblicati nella stessa regione con la stessa chiave di ordinamento nell'ordine in cui sono stati ricevuti dal servizio. Devi abilitare questa impostazione nell'abbonamento.

    Supponi di avere due sottoscrizioni A e B collegate allo stesso argomento T. La sottoscrizione A è configurata con l'ordinamento dei messaggi abilitato e l'abbonamento B senza l'ordinamento dei messaggi abilitato. In questa architettura, le sottoscrizioni A e B ricevono lo stesso insieme di messaggi dall'argomento T. Se pubblichi messaggi con chiavi di ordinamento nella stessa regione, la sottoscrizione A riceve i messaggi nell'ordine in cui sono stati pubblicati. Invece, l'abbonamento B riceve i messaggi senza alcun ordine previsto.

In generale, se la tua soluzione richiede che i clienti dei publisher inviino messaggi ordinati e non ordinati, crea argomenti separati, uno per i messaggi ordinati e l'altro per i messaggi non ordinati.

Considerazioni sull'utilizzo della messaggistica ordinata

Il seguente elenco contiene informazioni importanti sul comportamento dei messaggi ordinati in Pub/Sub:

  • Ordine all'interno della chiave: i messaggi pubblicati con la stessa chiave di ordinamento dovrebbero essere ricevuti in questo ordine. Supponiamo che per ordinare la chiave A pubblichi i messaggi 1, 2 e 3. Con gli ordini abilitati, è previsto che il primo venga consegnato prima del 2 e il 2 prima del 3.

  • Ordinamento tra più tasti: non è previsto che i messaggi pubblicati con chiavi di ordinamento diverse vengano ricevuti in ordine. Supponi di aver ordinato le chiavi A e B. Per la chiave di ordinamento A, i messaggi 1 e 2 vengono pubblicati in ordine. Per la chiave di ordinamento B, i messaggi 3 e 4 vengono pubblicati in ordine. Tuttavia, il messaggio 1 potrebbe arrivare prima o dopo il messaggio 4.

  • Riconsegna dei messaggi: Pub/Sub recapita ogni messaggio almeno una volta, di conseguenza il servizio Pub/Sub potrebbe consegnare nuovamente i messaggi. I nuovi caricamenti di un messaggio attivano la ricarica di tutti i messaggi successivi per quella chiave, anche quelli confermati. Supponiamo che il client di un sottoscrittore riceva i messaggi 1, 2 e 3 per una chiave di ordinamento specifica. Se il messaggio 2 viene recapitato di nuovo (perché la scadenza della conferma è scaduta o la conferma di "best effort" non è rimasta in Pub/Sub), anche il messaggio 3 viene recapitato nuovamente. Se in una sottoscrizione sono attivati sia l'ordinamento dei messaggi sia un argomento messaggi non recapitabili, questo comportamento potrebbe non essere vero, in quanto Pub/Sub inoltra i messaggi ad argomenti messaggi non recapitabili secondo il criterio del "best effort".

  • Ritardi di conferma e argomenti non accettati: i messaggi non confermati per una determinata chiave di ordinamento possono ritardare potenzialmente la consegna di messaggi per altre chiavi di ordinamento, in particolare durante i riavvii del server o le modifiche al traffico. Per mantenere l'ordine tra questi eventi, assicurati di ricevere un riscontro tempestivo di tutti i messaggi. Se non è possibile ricevere un riscontro tempestivo, valuta l'utilizzo di un argomento messaggi non recapitabili per evitare la conservazione a tempo indeterminato dei messaggi. Tieni presente che l'ordine potrebbe non essere conservato quando i messaggi vengono scritti in un argomento messaggi non recapitabili.

  • Affinità messaggi (client flussoingPull): i messaggi per la stessa chiave vengono generalmente recapitati allo stesso client dell'abbonato streamingPull. L'affinità è prevista quando i messaggi sono in sospeso per una chiave di ordinamento a un client dell'abbonato specifico. Se non sono presenti messaggi in sospeso, l'affinità potrebbe cambiare per il bilanciamento del carico o le disconnessioni del client.

    Per garantire un'elaborazione agevole anche con potenziali modifiche di affinità, è fondamentale progettare l'applicazione streamingPull in modo da poter gestire i messaggi in qualsiasi client per una determinata chiave di ordinamento.

  • Integrazione con Dataflow: non abilitare l'ordinamento dei messaggi per gli abbonamenti durante la configurazione di Dataflow con Pub/Sub. Dataflow dispone di un proprio meccanismo per l'ordinamento totale dei messaggi, garantendo l'ordine cronologico di tutti i messaggi nell'ambito delle operazioni di windowing. Questo metodo di ordinamento è diverso da quello basato su chiavi di ordinamento di Pub/Sub. L'uso delle chiavi di ordinamento con Dataflow può potenzialmente ridurre le prestazioni della pipeline.

  • Scalabilità automatica: la distribuzione ordinata di Pub/Sub scala fino a miliardi di chiavi di ordinamento. Un numero maggiore di chiavi di ordinamento consente la consegna più parallela ai sottoscrittori, in quanto l'ordinamento si applica a tutti i messaggi con la stessa chiave di ordinamento.

La consegna degli ordini effettuati prevede alcuni compromessi. Rispetto alla consegna non ordinata, la consegna ordinata potrebbe ridurre leggermente la disponibilità di pubblicazione e aumentare la latenza end-to-end della consegna dei messaggi. Nel caso di consegna ordinata, il failover richiede un coordinamento per garantire che i messaggi vengano scritti e letti nell'ordine corretto.

Per maggiori informazioni su come utilizzare l'ordinamento dei messaggi, consulta i seguenti argomenti delle best practice:

Comportamento del client del sottoscrittore per l'ordinamento dei messaggi

I client abbonati ricevono i messaggi nell'ordine in cui sono stati pubblicati in una regione specifica. Pub/Sub supporta diverse modalità di ricezione dei messaggi, ad esempio i client abbonati connessi per le sottoscrizioni pull ed push. Le librerie client usano streamingPull (ad eccezione di PHP).

Per scoprire di più su questi tipi di abbonamento, vedi Scegliere un tipo di abbonamento.

Le seguenti sezioni descrivono cosa significa ricevere i messaggi in ordine per ogni tipo di client del sottoscrittore.

Client abbonati StreamingPull

Quando utilizzi le librerie client con streamingPull, devi specificare un callback dell'utente che viene eseguito ogni volta che un messaggio viene ricevuto dal client di un sottoscrittore. Con le librerie client, per ogni chiave di ordinamento, il callback viene eseguito fino al completamento nei messaggi nell'ordine corretto. Se i messaggi vengono confermati all'interno del callback, tutti i calcoli su un messaggio avvengono in ordine. Tuttavia, se il callback dell'utente pianifica altre operazioni asincrone sui messaggi, il client dell'abbonato deve assicurarsi che il lavoro asincrono venga svolto in ordine. Un'opzione è aggiungere i messaggi a una coda di lavoro locale che viene elaborata in ordine.

Esegui il pull dei client degli abbonati

Per i client degli abbonati connessi per le sottoscrizioni pull, l'ordinamento dei messaggi Pub/Sub supporta quanto segue:

  • Tutti i messaggi relativi a una chiave di ordinamento in PullResponse sono nell'ordine corretto nell'elenco.

  • Può esserci un solo gruppo di messaggi in sospeso per una chiave di ordinamento alla volta.

Il requisito che prevede che un solo batch di messaggi alla volta possa essere in sospeso è necessario per mantenere la consegna ordinata, poiché il servizio Pub/Sub non può garantire il successo o la latenza della risposta che invia per la richiesta di pull di un sottoscrittore.

Esegui il push dei client degli abbonati

Le restrizioni alla spinta sono ancora più restrittive di quelle alla trazione. Per una sottoscrizione push, Pub/Sub supporta un solo messaggio in sospeso per ogni chiave di ordinamento alla volta. Ogni messaggio viene inviato a un endpoint push come richiesta separata. Pertanto, l'invio in parallelo delle richieste avrebbe lo stesso problema della consegna di più batch di messaggi per la stessa chiave di ordinamento per il pull contemporaneamente dei sottoscrittori. Le sottoscrizioni push potrebbero non essere una buona scelta per gli argomenti in cui i messaggi vengono pubblicati spesso con la stessa chiave di ordinamento o in cui la latenza è estremamente importante.

Esporta client abbonati

I messaggi ordinati di supporto per le iscrizioni sono stati esportati. Per le sottoscrizioni BigQuery, i messaggi con la stessa chiave di ordinamento vengono scritti in ordine nella relativa tabella BigQuery. Per le sottoscrizioni Cloud Storage, i messaggi con la stessa chiave di ordinamento potrebbero non essere scritti tutti nello stesso file. All'interno dello stesso file, i messaggi relativi a una chiave di ordinamento sono in ordine. Se distribuiti su più file, i messaggi successivi relativi a una chiave di ordinamento possono essere visualizzati in un file con un nome che ha un timestamp precedente a quello del nome del file con i messaggi precedenti.

Attiva l'ordinamento dei messaggi

Per ricevere i messaggi in ordine, imposta la proprietà di ordinamento dei messaggi nell'abbonamento da cui li ricevi. La ricezione dei messaggi in ordine potrebbe aumentare la latenza. Non puoi modificare la proprietà di ordinazione dei messaggi dopo aver creato una sottoscrizione.

Puoi impostare la proprietà di ordinamento dei messaggi quando crei una sottoscrizione utilizzando la console Google Cloud, Google Cloud CLI o l'API Pub/Sub.

Console

Per creare una sottoscrizione con la proprietà di ordinamento dei messaggi, segui questi passaggi:

  1. Nella console Google Cloud, vai alla pagina Abbonamenti.

Vai ad Abbonamenti

  1. Fai clic su Crea sottoscrizione.

  2. Inserisci un ID abbonamento.

  3. Scegli un argomento da cui ricevere i messaggi.

  4. Nella sezione Ordine dei messaggi, seleziona Ordina i messaggi con una chiave di ordinamento.

  5. Fai clic su Crea.

gcloud

Per creare una sottoscrizione con la proprietà di ordinamento dei messaggi, utilizza il comando gcloud pubsub subscriptions create e il flag --enable-message-ordering:

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

Sostituisci SUBSCRIPTION_ID con l'ID dell'abbonamento.

Se la richiesta ha esito positivo, la riga di comando visualizza un messaggio di conferma:

Created subscription [SUBSCRIPTION_ID].

REST

Per creare un abbonamento con la proprietà di ordinazione dei messaggi, invia una richiesta PUT come la seguente:

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

Sostituisci quanto segue:

  • PROJECT_ID: l'ID del progetto con l'argomento
  • SUBSCRIPTION_ID: l'ID dell'abbonamento

Nel corpo della richiesta, specifica quanto segue:

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

Sostituisci TOPIC_ID con l'ID dell'argomento da collegare alla sottoscrizione.

Se la richiesta ha esito positivo, la risposta è l'abbonamento in formato JSON:

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

C++

Prima di provare questo esempio, segui le istruzioni di configurazione di C++ riportate nella Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API C++ 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) {
  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#

Prima di provare questo esempio, segui le istruzioni di configurazione di C# nella Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API C# di Pub/Sub.


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

Prima di provare questo esempio, segui le istruzioni di configurazione di Go nella Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Go Pub/Sub.

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

Prima di provare questo esempio, segui le istruzioni di configurazione Java in Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Java Pub/Sub.

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

Prima di provare questo esempio, segui le istruzioni di configurazione di Node.js in Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Node.js Pub/Sub.

/**
 * 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

Prima di provare questo esempio, segui le istruzioni di configurazione di Node.js in Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Node.js Pub/Sub.

/**
 * 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

Prima di provare questo esempio, segui le istruzioni di configurazione di Python in Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Python Pub/Sub.

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

Ruby

Prima di provare questo esempio, segui le istruzioni di configurazione di Ruby riportate in Guida rapida sull'utilizzo delle librerie client. Per ulteriori informazioni, consulta la documentazione di riferimento dell'API Ruby Pub/Sub.

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

Passaggi successivi