Todos os produtos
Search
Central de documentação

ApsaraMQ for RocketMQ:Mensagens transacionais

Última atualização: Jun 27, 2026

As mensagens transacionais são um tipo de mensagem especial do ApsaraMQ for RocketMQ que garante o sucesso ou a falha simultânea da transação local e da entrega da mensagem. Esse mecanismo de commit em duas fases mantém a sincronia entre o serviço principal e seus consumidores downstream, sem a sobrecarga de bloqueio de recursos das transações distribuídas da eXtended Architecture (XA).

Distributed transaction requirements

Use mensagens transacionais quando:

  • Um sistema de pedidos precisar atualizar o banco de dados e notificar os serviços de logística, pontos e carrinho de forma atômica.

  • Um serviço de pagamento precisar registrar um débito e publicar um evento para consumidores downstream do livro-razão.

  • A entrega de uma mensagem sem a conclusão da transação local (ou vice-versa) deixar o sistema em um estado inconsistente.

Como funcionam as mensagens transacionais

Limitações das mensagens normais

Combinar uma transação local no banco de dados com o envio de uma mensagem normal cria uma lacuna em que uma operação pode ter sucesso enquanto a outra falha:

  • A mensagem é enviada, mas a transação local falha. Os consumidores downstream agem sobre uma alteração não confirmada.

  • A transação local é confirmada, mas o envio da mensagem falha. Os consumidores downstream nunca recebem a notificação sobre a alteração.

  • Ocorre um timeout e nem o produtor nem o broker conseguem determinar se devem confirmar ou reverter a operação.

Normal message solution

Alto custo das transações XA

O protocolo XA coordena transações distribuídas entre sistemas, mas bloqueia recursos durante toda a duração da transação. À medida que o número de sistemas participantes aumenta, a contenção de bloqueios cresce e o throughput diminui.

Commit em duas fases com half messages

As mensagens transacionais do ApsaraMQ for RocketMQ usam um protocolo de commit em duas fases que evita ambos os problemas:

Transactional message solution

  1. Envie uma half message. O produtor envia uma mensagem ao broker. O broker a persiste e retorna um reconhecimento (ACK) ao produtor. A mensagem é marcada como não pronta para entrega — uma mensagem nesse estado é chamada de half message. Os consumidores downstream ainda não podem vê-la.

  2. Execute a transação local. O produtor executa sua operação local no banco de dados (por exemplo, atualizar o status de um pedido de não pago para pago).

  3. Confirme ou reverta. O produtor informa o resultado da transação local ao broker:

    • Commit: O broker marca a half message como pronta para entrega e a encaminha aos consumidores.

    • Rollback: O broker descarta a half message. Os consumidores nunca a recebem.

  4. Verificação de status da transação (recuperação). Caso o broker não receba um resultado de commit ou rollback — devido a uma falha de rede ou reinicialização do produtor —, ele envia uma consulta de status a uma instância do produtor no cluster. O produtor verifica o resultado da transação local e o reenvia ao broker.

Transaction status check workflow

Nota

Para obter informações sobre o intervalo de consulta e a contagem máxima de tentativas, consulte Limites de parâmetros.

Ciclo de vida da mensagem

Uma mensagem transacional passa pelos seguintes estados:

Transactional message lifecycle

Estado

Descrição

Inicialização

O produtor constrói a half message e prepara-se para enviá-la ao broker.

Transação pendente de commit

O broker armazena a half message no sistema de armazenamento de transações. Diferentemente de uma mensagem normal, o broker não persiste a half message da maneira padrão. A mensagem permanece invisível para os consumidores.

Confirmada para consumo

A transação local é bem-sucedida. O broker armazena a half message no sistema de armazenamento, tornando-a visível para os consumidores.

Rollback da mensagem

A transação local falha. O broker descarta a half message. O fluxo de trabalho é encerrado.

Em consumo

Um consumidor recebe a mensagem e inicia seu processamento. Se o consumidor não retornar um resultado dentro do timeout configurado, o ApsaraMQ for RocketMQ tenta entregar a mensagem novamente. Para mais detalhes, consulte Tentativa de consumo.

Commit do resultado de consumo

O consumidor confirma o resultado do consumo. A mensagem é marcada como consumida, mas não é excluída imediatamente.

Exclusão da mensagem

O período de retenção da mensagem expira ou o espaço de armazenamento fica escasso. O ApsaraMQ for RocketMQ exclui as mensagens mais antigas de forma rotativa. Consulte Armazenamento e limpeza de mensagens.

Por padrão, o ApsaraMQ for RocketMQ retém todas as mensagens. Uma mensagem consumida não é excluída imediatamente — os consumidores podem consumi-la novamente até que o período de retenção expire ou o espaço de armazenamento seja recuperado.

Enviar mensagens transacionais (Java)

Pré-requisitos

Antes de começar, certifique-se de ter:

  • Um tópico com MessageType definido como Transaction no console do ApsaraMQ for RocketMQ.

  • O endpoint da instância (na aba Endpoints da página Instance Details)

  • (Se aplicável) O nome de usuário e a senha da instância (na aba Intelligent Authentication da página Access Control)

Diferenças em relação às mensagens normais

O envio de uma mensagem transacional difere do envio de uma mensagem normal em dois aspectos:

  • Verificador de transação obrigatório. Registre um verificador de transação ao construir o produtor. O verificador é executado automaticamente se o broker consultar o status da transação após uma falha.

  • Vinculação de tópico obrigatória. Vincule o tópico de destino ao produtor durante a construção para que o verificador integrado possa recuperar o status da transação.

Código de exemplo

Construa um produtor com um verificador de transação, inicie uma transação, envie uma half message, execute a transação local e, em seguida, confirme ou reverta a operação.

Código de exemplo

import java.time.Duration;
import org.apache.rocketmq.client.apis.*;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;
import org.apache.rocketmq.client.apis.producer.Transaction;
import org.apache.rocketmq.client.apis.producer.TransactionResolution;
import org.apache.rocketmq.client.java.message.MessageBuilderImpl;
import org.apache.rocketmq.client.apis.message.MessageBuilder;
import org.apache.rocketmq.shaded.com.google.common.base.Strings;

public class ProducerTransactionMessageExample {

    // Simulates checking whether the order exists in the database.
    private static boolean checkOrderById(String orderId) {
        return true;
    }

    // Simulates the local transaction (e.g., inserting an order record).
    private static boolean doLocalTransaction() {
        return true;
    }

    public static void main(String[] args) throws ClientException {
        // Replace with your instance endpoint.
        // Find this on the Endpoints tab of the Instance Details page
        // in the ApsaraMQ for RocketMQ console.
        String endpoints = "<your-instance-endpoint>";

        // The topic must have MessageType set to Transaction.
        String topic = "<your-transaction-topic>";

        ClientServiceProvider provider = ClientServiceProvider.loadService();
        ClientConfigurationBuilder builder = ClientConfiguration.newBuilder()
            .setEndpoints(endpoints);

        // Authentication:
        // - Public endpoint: specify username and password (find these on the
        //   Intelligent Authentication tab of the Access Control page).
        // - VPC endpoint on ECS: no credentials needed; the broker resolves
        //   them from the VPC.
        // - Serverless instance: always specify credentials, regardless of
        //   access method.
        builder.setCredentialProvider(
            new StaticSessionCredentialsProvider("<your-username>", "<your-password>"));
        builder.setRequestTimeout(Duration.ofMillis(5000));
        ClientConfiguration configuration = builder.build();

        MessageBuilder messageBuilder = new MessageBuilderImpl();

        // Build the producer with a transaction checker.
        // The checker runs when the broker queries a half message whose
        // commit/rollback result was not received.
        Producer producer = provider.newProducerBuilder()
            .setTransactionChecker(messageView -> {
                // Look up the order ID attached to the half message.
                // If the order exists in the database, the local transaction
                // committed successfully. Otherwise, roll back.
                final String orderId = messageView.getProperties().get("OrderId");
                if (Strings.isNullOrEmpty(orderId)) {
                    return TransactionResolution.ROLLBACK;
                }
                return checkOrderById(orderId)
                    ? TransactionResolution.COMMIT
                    : TransactionResolution.ROLLBACK;
            })
            .setTopics(topic)
            .setClientConfiguration(configuration)
            .build();

        // Step 1: Begin a transaction.
        final Transaction transaction;
        try {
            transaction = producer.beginTransaction();
        } catch (ClientException e) {
            e.printStackTrace();
            return;
        }

        // Step 2: Build and send the half message.
        Message message = messageBuilder.setTopic(topic)
            .setKeys("messageKey1")
            .setTag("messageTag")
            // Attach a business ID for the transaction checker to query later.
            .addProperty("OrderId", "xxx")
            .setBody("messageBody".getBytes())
            .build();

        final SendReceipt sendReceipt;
        try {
            sendReceipt = producer.send(message, transaction);
        } catch (ClientException e) {
            // Half message send failed. The transaction ends here.
            return;
        }

        // Step 3: Run the local transaction.
        boolean localTransactionOk = doLocalTransaction();

        // Step 4: Commit or roll back based on the local transaction result.
        if (localTransactionOk) {
            try {
                transaction.commit();
            } catch (ClientException e) {
                // If the commit call fails, the broker will invoke the
                // transaction checker to resolve the status.
                e.printStackTrace();
            }
        } else {
            try {
                transaction.rollback();
            } catch (ClientException e) {
                // Log the error. The broker will invoke the transaction
                // checker to resolve the status.
                e.printStackTrace();
            }
        }
    }
}

Substitua os seguintes placeholders pelos valores reais:

Placeholder

Descrição

Exemplo

<your-instance-endpoint>

Endpoint da instância (na aba Endpoints da página Instance Details)

xxx-hangzhou.rmq.aliyuncs.com:8080

<your-transaction-topic>

Nome do tópico com MessageType definido como Transaction

order-tx-topic

<your-username>

Nome de usuário da instância (na aba Intelligent Authentication da página Access Control)

MjoxODgwNzcwODY5MD****

<your-password>

Senha da instância

NEh6cm9FVUl****

Para exemplos completos de SDK em todas as linguagens suportadas, consulte SDKs do Apache RocketMQ 5.x.

Melhores práticas

Minimize resultados de transação desconhecidos

A verificação de status da transação funciona como uma rede de segurança para falhas durante o commit ou rollback. Um alto volume de verificações de status degrada o desempenho do sistema e atrasa a entrega de mensagens. Projete transações locais para retornar um resultado definitivo de Commit ou Rollback o mais rápido possível.

Trate corretamente as transações em andamento

Quando o broker consulta o status de uma half message e a transação local ainda está em execução, retorne Unknown — e não Commit ou Rollback. Retornar um resultado prematuro pode causar inconsistência de dados.

Se as verificações de status chegarem cedo demais porque a transação local é lenta, considere estas abordagens:

  • Aumente o atraso da primeira verificação. Configure um intervalo maior antes de o broker enviar sua primeira consulta de status. Contrapartida: isso também atrasa a recuperação para transações que realmente falharam.

  • Detecte explicitamente o estado em andamento. Projete a lógica da transação local para distinguir entre "ainda em execução" e "falhou", para que o verificador retorne o status correto.

Limites

Restrição

Detalhes

Tipo de tópico

Mensagens transacionais exigem um tópico com MessageType definido como Transaction.

Um SendReceipt por transação

Cada transação suporta apenas um SendReceipt.

Apenas consistência eventual

As mensagens transacionais garantem a consistência entre a transação local e a entrega da mensagem. Elas não garantem consistência em tempo real entre os consumidores downstream. Até que a mensagem seja entregue, o estado downstream pode ficar defasado em relação à transação upstream. Use mensagens transacionais apenas quando o processamento assíncrono downstream for aceitável.

Responsabilidade do lado do consumidor

O ApsaraMQ for RocketMQ garante a entrega das mensagens confirmadas, mas cada consumidor downstream deve lidar corretamente com o processamento. Implemente uma lógica de tentativa de consumo para lidar com falhas transitórias. Consulte Tentativa de consumo.

Timeout da transação

Se o broker não conseguir determinar o resultado da transação após o timeout configurado e a contagem máxima de tentativas, ele reverte a half message por padrão. Consulte Limites de parâmetros.