Todos os produtos
Search
Central de documentação

ApsaraMQ for RocketMQ:Send and consume scheduled and delayed messages by using the PHP HTTP SDK

Última atualização: Jun 27, 2026

Os exemplos de código PHP a seguir demonstram como enviar e consumir mensagens agendadas e atrasadas com o SDK cliente HTTP do ApsaraMQ for RocketMQ.

Funcionamento das mensagens agendadas e atrasadas

Mensagens agendadas e atrasadas adiam a entrega em vez de chegarem imediatamente após a publicação.

  • Mensagem atrasada: entregue após um período de atraso especificado. Exemplo: entrega 30 segundos após o envio.

  • Mensagem agendada: entregue em um momento específico. Exemplo: entrega em 2024-06-15 09:00:00.

Ambos os tipos compartilham a mesma API. Chame setStartDeliverTime com um timestamp Unix em milissegundos que represente quando o broker deve entregar a mensagem.

  • Para uma mensagem atrasada, calcule o timestamp como hora atual + duração do atraso.

  • Para uma mensagem agendada, converta o horário de entrega alvo diretamente para um timestamp Unix em milissegundos.

Casos de uso comuns:

  • Timeout de pedido: cancele um pedido não pago após 30 minutos publicando uma mensagem atrasada no momento da criação do pedido.

  • Nova tentativa com backoff: reprocesse uma tarefa com falha após um atraso, em vez de tentar novamente imediatamente.

  • Notificações temporizadas: acione um lembrete em um horário específico, como 15 minutos antes de um evento agendado.

Para mais informações, consulte Mensagens agendadas e mensagens atrasadas.

Pré-requisitos

Antes de começar, verifique se você:

Envie mensagens agendadas ou atrasadas

O código a seguir envia quatro mensagens atrasadas, cada uma entregue 10 segundos após a publicação. Para enviar uma mensagem agendada, passe o horário de entrega alvo como um timestamp Unix em milissegundos para setStartDeliverTime.

Substitua os placeholders antes de executar o código:

Placeholder

Descrição

Exemplo

<HTTP_ENDPOINT>

Endpoint HTTP obtido na página Instance Details no console do ApsaraMQ for RocketMQ

http://xxxx.mq-http.cn-hangzhou.aliyuncs.com

<TOPIC>

Nome do tópico criado no console

scheduled-msg-topic

<INSTANCE_ID>

ID da instância. Se a instância não tiver namespace, defina como null ou ""

MQ_INST_xxxx

<?php

require "vendor/autoload.php";

use MQ\Model\TopicMessage;
use MQ\MQClient;

class ProducerTest
{
    private $client;
    private $producer;

    public function __construct()
    {
        $this->client = new MQClient(
            // HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console
            "<HTTP_ENDPOINT>",
            // AccessKey ID for authentication
            getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
            // AccessKey secret for authentication
            getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
        );

        $topic = "<TOPIC>";
        // Instance ID. If the instance has no namespace, set to null or "".
        $instanceId = "<INSTANCE_ID>";

        $this->producer = $this->client->getProducer($instanceId, $topic);
    }

    public function run()
    {
        try
        {
            for ($i = 1; $i <= 4; $i++)
            {
                $publishMessage = new TopicMessage(
                    "hello mq! " // Message body
                );
                // Custom message property
                $publishMessage->putProperty("a", $i);
                // Message key for tracing
                $publishMessage->setMessageKey("MessageKey");

                // Deliver the message 10 seconds from now.
                // For a scheduled message, set this to the target delivery time
                // as a millisecond-level Unix timestamp.
                $publishMessage->setStartDeliverTime(time() * 1000 + 10 * 1000);

                $result = $this->producer->publishMessage($publishMessage);

                print "Send mq message success. msgId is:" . $result->getMessageId()
                    . ", bodyMD5 is:" . $result->getMessageBodyMD5() . "\n";
            }
        } catch (\Exception $e) {
            print_r($e->getMessage() . "\n");
        }
    }
}

$instance = new ProducerTest();
$instance->run();

?>

Pontos importantes:

  • setStartDeliverTime aceita um timestamp Unix em milissegundos. Em PHP, time() retorna segundos; portanto, multiplique por 1000 para converter.

  • Para um atraso de 10 segundos: time() * 1000 + 10 * 1000.

  • Para entrega em um horário específico, converta esse horário para um timestamp Unix em milissegundos. Por exemplo, para entregar às 2024-06-15 09:00:00 UTC: strtotime('2024-06-15 09:00:00') * 1000.

Consuma mensagens agendadas ou atrasadas

O consumo de mensagens agendadas e atrasadas ocorre da mesma forma que o de mensagens normais. Após o horário de entrega, as mensagens ficam disponíveis por meio de long polling.

Substitua os placeholders antes de executar o código:

Placeholder

Descrição

Exemplo

<HTTP_ENDPOINT>

Endpoint HTTP obtido na página Instance Details

http://xxxx.mq-http.cn-hangzhou.aliyuncs.com

<TOPIC>

Nome do tópico criado no console

scheduled-msg-topic

<GROUP_ID>

ID do grupo de consumidores criado no console

GID_scheduled_consumer

<INSTANCE_ID>

ID da instância. Se a instância não tiver namespace, defina como null ou ""

MQ_INST_xxxx

<?php

use MQ\MQClient;

require "vendor/autoload.php";

class ConsumerTest
{
    private $client;
    private $consumer;

    public function __construct()
    {
        $this->client = new MQClient(
            // HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console
            "<HTTP_ENDPOINT>",
            // AccessKey ID for authentication
            getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"),
            // AccessKey secret for authentication
            getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
        );

        $topic = "<TOPIC>";
        // Consumer group ID created in the ApsaraMQ for RocketMQ console
        $groupId = "<GROUP_ID>";
        // Instance ID. If the instance has no namespace, set to null or "".
        $instanceId = "<INSTANCE_ID>";

        $this->consumer = $this->client->getConsumer($instanceId, $topic, $groupId);
    }

    public function ackMessages($receiptHandles)
    {
        try {
            $this->consumer->ackMessage($receiptHandles);
        } catch (\Exception $e) {
            if ($e instanceof MQ\Exception\AckMessageException) {
                // ACK may fail if the receipt handle has expired
                printf("Ack Error, RequestId:%s\n", $e->getRequestId());
                foreach ($e->getAckMessageErrorItems() as $errorItem) {
                    printf("\tReceiptHandle:%s, ErrorCode:%s, ErrorMsg:%s\n",
                        $errorItem->getReceiptHandle(),
                        $errorItem->getErrorCode(),
                        $errorItem->getErrorMessage());
                }
            }
        }
    }

    public function run()
    {
        // Consume messages in a loop. For production use, run multiple threads
        // to consume messages concurrently.
        while (True) {
            try {
                // Long polling: if no message is available, the request waits
                // on the broker until a message arrives or the polling period ends.
                $messages = $this->consumer->consumeMessage(
                    3, // Max messages per request (up to 16)
                    3  // Long polling timeout in seconds (up to 30)
                );
            } catch (\MQ\Exception\MessageResolveException $e) {
                // Thrown when some messages contain invalid characters and cannot be parsed
                $messages = $e->getPartialResult()->getMessages();
                $failMessages = $e->getPartialResult()->getFailResolveMessages();

                $receiptHandles = array();
                foreach ($messages as $message) {
                    $receiptHandles[] = $message->getReceiptHandle();
                    printf("MsgID %s\n", $message->getMessageId());
                }
                foreach ($failMessages as $failMessage) {
                    $receiptHandles[] = $failMessage->getReceiptHandle();
                    printf("Fail To Resolve Message. MsgID %s\n", $failMessage->getMessageId());
                }
                $this->ackMessages($receiptHandles);
                continue;
            } catch (\Exception $e) {
                if ($e instanceof MQ\Exception\MessageNotExistException) {
                    // No messages available. Long polling continues on the next iteration.
                    printf("No message, continue long polling! RequestId:%s\n", $e->getRequestId());
                    continue;
                }

                print_r($e->getMessage() . "\n");

                sleep(3);
                continue;
            }

            print "consume finish, messages:\n";

            $receiptHandles = array();
            foreach ($messages as $message) {
                $receiptHandles[] = $message->getReceiptHandle();

                printf("MessageID:%s TAG:%s BODY:%s \nPublishTime:%d, FirstConsumeTime:%d, \nConsumedTimes:%d, NextConsumeTime:%d, MessageKey:%s\n",
                    $message->getMessageId(), $message->getMessageTag(), $message->getMessageBody(),
                    $message->getPublishTime(), $message->getFirstConsumeTime(),
                    $message->getConsumedTimes(), $message->getNextConsumeTime(),
                    $message->getMessageKey());
                print_r($message->getProperties());
            }

            // If the broker does not receive an ACK before the time specified
            // in getNextConsumeTime(), the message is redelivered.
            // Each delivery generates a new receipt handle.
            print_r($receiptHandles);
            $this->ackMessages($receiptHandles);
            print "ack finish\n";
        }

    }
}

$instance = new ConsumerTest();
$instance->run();

?>

Aspectos importantes sobre o consumo de mensagens

  • Long polling: quando nenhuma mensagem está disponível, o broker retém a solicitação do consumidor até que uma mensagem chegue ou o tempo limite de polling expire. Isso reduz solicitações de rede desnecessárias.

  • ACK (confirmação de consumo): após processar uma mensagem, o consumidor envia um ACK com o receipt handle. Se o broker não receber nenhum ACK antes de getNextConsumeTime(), ele reentrega a mensagem.

  • Expiração do receipt handle: cada entrega atribui um receipt handle único com seu próprio tempo de expiração. Um handle expirado causa falha no ACK e aciona a reentrega.

  • Falhas de análise: se o corpo de uma mensagem contiver caracteres inválidos, o SDK lança MessageResolveException. Chame getPartialResult() para recuperar as mensagens analisadas com sucesso e tratar as falhas separadamente.

Referências