Hyperf通过RabbitMQ的ttl以及死信实现延迟消息

废话不多说:

新增app/Amqp/DelayProducer.php

<?php

declare(strict_types=1);

namespace App\Amqp;

use Hyperf\Amqp\Builder;
use Hyperf\Di\Annotation\AnnotationCollector;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

class DelayProducer extends Builder
{
    public function produce(DelayProducerMessage $producerMessage, bool $confirm = false, int $timeout = 5): bool
    {
        return retry(1, function () use ($producerMessage, $confirm, $timeout) {
            return $this->produceMessage($producerMessage, $confirm, $timeout);
        });
    }

    /**
     * @param DelayProducerMessage $producerMessage
     * @param bool $confirm
     * @param int $timeout
     * @param int $delayTime
     * @return bool
     * @throws \Throwable
     */
    private function produceMessage(DelayProducerMessage $producerMessage, bool $confirm = false, int $timeout = 5)
    {
        $result = false;

        $this->injectMessageProperty($producerMessage);
        $message = new AMQPMessage($producerMessage->payload(), $producerMessage->getProperties());

        $pool = $this->getConnectionPool($producerMessage->getPoolName());
        /** @var \Hyperf\Amqp\Connection $connection */
        $connection = $pool->get();
        if ($confirm) {
            $channel = $connection->getConfirmChannel();
        } else {
            $channel = $connection->getChannel();
        }
        $channel->set_ack_handler(function () use (&$result) {
            $result = true;
        });

        try {
            $delayExchange   = 'dead_letter_ex_' . $producerMessage->getExchange();
            $delayQueue      = 'dead_letter_queue_' . $producerMessage->getExchange() . $producerMessage->getTtl();
            $delayRoutingKey = 'dead_letter_rk_'. $producerMessage->getRoutingKey();
            //定义延迟交换器
            $channel->exchange_declare($delayExchange, 'topic', false, true, false);

            //定义延迟队列
            $channel->queue_declare($delayQueue, false, true, false, false, false, new AMQPTable(array(
                "x-dead-letter-exchange"    => $producerMessage->getExchange(),
                "x-dead-letter-routing-key" => $producerMessage->getRoutingKey(),
                "x-message-ttl"             => $producerMessage->getTtl() * 1000,
            )));
            //绑定延迟队列到交换器上
            $channel->queue_bind($delayQueue, $delayExchange, $delayRoutingKey);

            $channel->basic_publish($message, $delayExchange, $delayRoutingKey);
            $channel->wait_for_pending_acks_returns($timeout);
        }
        catch (\Throwable $exception) {
            // Reconnect the connection before release.
            $connection->reconnect();
            throw $exception;
        }
        finally {
            $connection->release();
        }

        return $confirm ? $result : true;
    }

    private function injectMessageProperty(DelayProducerMessage $producerMessage)
    {
        if (class_exists(AnnotationCollector::class)) {
            /** @var \App\Annotations\DelayProducer $annotation */
            $annotation = AnnotationCollector::getClassAnnotation(get_class($producerMessage), \App\Annotations\DelayProducer::class);
            if ($annotation) {
                $annotation->routingKey && $producerMessage->setRoutingKey($annotation->routingKey);
                $annotation->exchange && $producerMessage->setExchange($annotation->exchange);
                $annotation->ttl && $producerMessage->setTtl($annotation->ttl);
            }
        }
    }
}

新增app/Amqp/DelayProducerMessage.php

<?php

declare(strict_types=1);

namespace App\Amqp;


use Hyperf\Amqp\Message\ProducerMessage;

class DelayProducerMessage extends ProducerMessage
{
    /**
     * @var integer 延迟时间(秒)
     */
    protected $ttl;

    public function setTtl($ttl)
    {
        $this->ttl = $ttl;
        return $this;
    }

    public function getTtl()
    {
        return $this->ttl;
    }
}

生产者:app/Amqp/Producer/OrderTimeoutResponse.php

<?php

declare(strict_types=1);

namespace App\Amqp\Producer;

use App\Amqp\DelayProducerMessage;
use App\Annotations\DelayProducer;

/**
 * @DelayProducer(exchange="order.timeout.response", routingKey="order.timeout.response", ttl=86400)
 */
class OrderTimeoutResponse extends DelayProducerMessage
{
    public function __construct($data)
    {
      
        $this->payload = $data;
    }
}

消费者:app/Amqp/Consumer/OrderTimeoutResponse.php

<?php

declare(strict_types=1);

namespace App\Amqp\Consumer;


use App\Exception\BusinessException;
use Hyperf\Amqp\Annotation\Consumer;
use Hyperf\Amqp\Message\ConsumerMessage;
use Hyperf\Amqp\Result;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Logger\LoggerFactory;
use Psr\Container\ContainerInterface;
use Hyperf\Config\Annotation\Value;
use Psr\Log\LoggerInterface;

/**
 * 确认超时自动处理 - 延时队列
 * @Consumer(exchange="order.timeout.response", routingKey="order.timeout.response", queue="order_timeout_response", nums=1)
 * Class OrderTimeoutResponse
 * @package App\Amqp\Consumer
 */
class TransferOrderTimeoutResponse extends ConsumerMessage
{
    /**
     * @var ConfigInterface
     */
    protected ConfigInterface $config;



    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        $this->config = $container->get(ConfigInterface::class);
        $this->logger = $this->container->get(LoggerFactory::class)->get('transferOrderTimeoutResponse');
    }


    public function consume($data): string
    {
       //业务处理
        return Result::ACK;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值