Hyperf 实现redis消息队列-源码解读

   Hyperf 实现redis消息队列-源码解读

    之前写过一篇笔记《Redis实现消息队列》,其中对消息队列以及redis实现消息队列的三种方式进行了介绍。下面来对hyperf实现redis消息队列的源码进行解读,加深对redis如何来实现消息队列的理解。

    一、实现Redis消息队列的各个文件

   

   1、DriverInterface

    定义了驱动接口

 1 <?php
 2 
 3 declare(strict_types=1);
 4 namespace Hyperf\AsyncQueue\Driver;
 5 
 6 use Hyperf\AsyncQueue\JobInterface;
 7 
 8 interface DriverInterface
 9 {
10     /**
11      * Push a job to queue.
12      */
13     public function push(JobInterface $job, int $delay = 0): bool;
14 
15     /**
16      * Delete a delay job to queue.
17      */
18     public function delete(JobInterface $job): bool;
19 
20     /**
21      * Pop a job from queue.
22      */
23     public function pop(): array;
24 
25     /**
26      * Ack a job.
27      *
28      * @param mixed $data
29      */
30     public function ack($data): bool;
31 
32     /**
33      * Push a job to failed queue.
34      *
35      * @param mixed $data
36      */
37     public function fail($data): bool;
38 
39     /**
40      * Consume jobs from a queue.
41      */
42     public function consume(): void;
43 
44     /**
45      * Reload failed message into waiting queue.
46      */
47     public function reload(string $queue = null): int;
48 
49     /**
50      * Delete all failed message from failed queue.
51      */
52     public function flush(string $queue = null): bool;
53 
54     /**
55      * Return info for current queue.
56      */
57     public function info(): array;
58 }

    这里,push() 可以理解为生产消息,pop()可以理解为消费消息

    2、DriverFactory

    生成驱动:获取不同的驱动实例

<?php
    ...
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        $config = $container->get(ConfigInterface::class);

        $this->configs = $config->get('async_queue', []);

        foreach ($this->configs as $key => $item) {
            $driverClass = $item['driver'];

            if (! class_exists($driverClass)) {
                throw new InvalidDriverException(sprintf('[Error] class %s is invalid.', $driverClass));
            }

            $driver = make($driverClass, ['config' => $item]);
            if (! $driver instanceof DriverInterface) {
                throw new InvalidDriverException(sprintf('[Error] class %s is not instanceof %s.', $driverClass, DriverInterface::class));
            }

            $this->drivers[$key] = $driver;
        }
    }

        public function get(string $name): DriverInterface
    {
        $driver = $this->drivers[$name] ?? null;
        if (! $driver || ! $driver instanceof DriverInterface) {
            throw new InvalidDriverException(sprintf('[Error]  %s is a invalid driver.', $name));
        }

        return $driver;
    }
    ...

    这里是从config/autoload/async_queue.php中读取配置

    async_queue.php如下:

 1 <?php
 2    ...
 3    return [
 4     'default' => [
 5         'driver' => Hyperf\AsyncQueue\Driver\RedisDriver::class,
 6         'redis' => [
 7             'pool' => $poolName
 8         ],
 9         'channel' => env("REDIS_QUEUE", 'queue:job_service_queue'),
10         'timeout' => 10,
11         'retry_seconds' => 5,
12         'handle_timeout' => 10,
13         'processes' => 1,
14     ]
15 ];

   3、Driver 抽象类并且实现了DriverInterface接口

 1 <?php
 2     ...
 3 
 4     public function consume(): void
 5     {
 6         $messageCount = 0;
 7         $maxMessages = Arr::get($this->config, 'max_messages', 0);
 8 
 9         while (self::$running) {
10             [$data, $message] = $this->pop();
11 
12             if ($data === false) {
13                 continue;
14             }
15 
16             $callback = $this->getCallback($data, $message);
17 
18             if ($this->concurrent instanceof Concurrent) {
19                 $this->concurrent->create($callback);
20             } else {
21                 parallel([$callback]);
22             }
23 
24             if ($messageCount % $this->lengthCheckCount === 0) {
25                 $this->checkQueueLength();
26             }
27 
28             if ($maxMessages > 0 && $messageCount >= $maxMessages) {
29                 break;
30             }
31 
32             ++$messageCount;
33         }
34     }
35     ...

    通过轮询的方式,消费消息。

   注意:

  1)这里Driver类 使用了 implements  DriverInterface 但是却并未实现DriverInterface中的方法。

   注意目的是让继承Driver的子类去实现接口类的方法

   定义了两个抽象方法

  1) Retry

  2) remove

  4、RedisDriver 继承Driver

  注意:RedisDriver 并未使用 implements  DriverInterface ,但是却实现了DriverInterface中所有定义的方法

  Redis实现消息队列的过程几乎都是通过RedisDriver中的各个代码来实现的

posted @ 2022-06-28 09:22  欢乐豆123  阅读(944)  评论(0编辑  收藏  举报