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中的各个代码来实现的