Todos los productos
Search
Centro de documentos

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

Última actualización: Sep 04, 2026

Los siguientes ejemplos de código PHP muestran cómo enviar y consumir mensajes programados y diferidos con el SDK cliente HTTP de ApsaraMQ for RocketMQ.

Funcionamiento de los mensajes programados y diferidos

Los mensajes programados y diferidos posponen su entrega en lugar de llegar inmediatamente después de la publicación.

  • Mensaje diferido: Se entrega después de un periodo de retraso especificado. Por ejemplo, entregar 30 segundos después del envío.

  • Mensaje programado: Se entrega en un momento específico. Por ejemplo, entregar el 2024-06-15 a las 09:00:00.

Ambos tipos comparten la misma API. Llame a setStartDeliverTime con una marca de tiempo Unix en milisegundos que represente cuándo debe entregar el mensaje el broker.

  • Para un mensaje diferido, calcule la marca de tiempo como hora actual + duración del retraso.

  • Para un mensaje programado, convierta la hora de entrega objetivo directamente a una marca de tiempo Unix en milisegundos.

Casos de uso comunes:

  • Tiempo de espera de pedido: Cancele un pedido no pagado después de 30 minutos al publicar un mensaje diferido cuando se crea el pedido.

  • Reintento con retroceso: Vuelva a procesar una tarea con errores después de un retraso en lugar de reintentar inmediatamente.

  • Notificaciones temporizadas: Active un recordatorio en un momento específico, como 15 minutos antes de un evento programado.

Para obtener más información, consulte Scheduled messages and delayed messages.

Requisitos previos

Antes de empezar, asegúrese de contar con lo siguiente:

Envío de mensajes programados o diferidos

El siguiente código envía cuatro mensajes diferidos, cada uno entregado 10 segundos después de su publicación. Para enviar un mensaje programado en su lugar, pase la hora de entrega objetivo como una marca de tiempo Unix en milisegundos a setStartDeliverTime.

Reemplace los marcadores de posición antes de ejecutar el código:

Marcador de posición

Descripción

Ejemplo

<HTTP_ENDPOINT>

Endpoint HTTP de la página Instance Details en la consola de ApsaraMQ for RocketMQ

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

<TOPIC>

Nombre del topic creado en la consola

scheduled-msg-topic

<INSTANCE_ID>

ID de la instancia. Si la instancia no tiene namespace, establézcalo en null o ""

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();

?>

Puntos clave:

  • setStartDeliverTime acepta una marca de tiempo Unix en milisegundos. En PHP, time() devuelve segundos, así que multiplique por 1000 para convertir.

  • Para un retraso de 10 segundos: time() * 1000 + 10 * 1000.

  • Para la entrega en un momento específico, convierta esa hora a una marca de tiempo Unix en milisegundos. Por ejemplo, para entregar a las 2024-06-15 09:00:00 UTC: strtotime('2024-06-15 09:00:00') * 1000.

Consumo de mensajes programados o diferidos

Los mensajes programados y diferidos se consumen de la misma manera que los mensajes normales. Una vez que llega la hora de entrega, los mensajes están disponibles mediante sondeo largo.

Reemplace los marcadores de posición antes de ejecutar el código:

Marcador de posición

Descripción

Ejemplo

<HTTP_ENDPOINT>

Endpoint HTTP de la página Instance Details

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

<TOPIC>

Nombre del topic creado en la consola

scheduled-msg-topic

<GROUP_ID>

ID del grupo de consumidores creado en la consola

GID_scheduled_consumer

<INSTANCE_ID>

ID de la instancia. Si la instancia no tiene namespace, establézcalo en null o ""

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 el consumo de mensajes

  • Sondeo largo: Cuando no hay ningún mensaje disponible, el broker retiene la solicitud del consumidor hasta que llega un mensaje o expira el tiempo de espera del sondeo. Esto reduce las solicitudes de red innecesarias.

  • ACK (confirmación de consumo): Después de procesar un mensaje, el consumidor envía un ACK con el identificador de recepción. Si el broker no recibe ningún ACK antes de getNextConsumeTime(), vuelve a entregar el mensaje.

  • Caducidad del identificador de recepción: Cada entrega asigna un identificador de recepción único con su propio tiempo de caducidad. Un identificador caducado provoca que el ACK falle y desencadena una nueva entrega.

  • Errores de análisis sintáctico: Si el cuerpo de un mensaje contiene caracteres no válidos, el SDK lanza MessageResolveException. Llame a getPartialResult() para recuperar los mensajes analizados correctamente y gestionar los errores por separado.

Referencias