redis 批量订阅频道 redis队列批量消费_redis 批量订阅频道

消息模式介绍

一般来说,消息队列有两种模式:

生产者消费者模式(Queue);

redis 批量订阅频道 redis队列批量消费_redis 批量订阅频道_02

消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。

消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

一种是发布者订阅者模式(Topic);

redis 批量订阅频道 redis队列批量消费_数据库_03

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。

和点对点方式不同,发布到topic的消息会被所有订阅者消费。

利用redis这两种场景的消息队列都能实现。

Queue 模式介绍

生产者生产消息放到队列中,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息,即对于每个消息最多只能被一个消费者拥有。

具体的方法就是创建一个任务队列,生产者主动lpush消息,而消费者去rpop数据。但是这样存在一个问题,就是消费者需要主动去请求数据,周期性的请求会造成资源的浪费。如果可以实现一旦有新消息加入队列就通知消费者就好了,这时借助brpop命令就可以实现这样的需求。brpop和rpop命令相似,唯一区别就是当列表中没有元素时,brpop命令会一直阻塞住连接,直到有新元素加入。

config/process.php

// 队列消费
    'redis_consumer' => [
        'handler'     => Webman\RedisQueue\Process\Consumer::class,
        'count'       => cpu_count() * 3, // 可以设置多进程
        'constructor' => [
            // 消费者类目录
            'consumer_dir' => app_path() . '/queue/redis',
        ],
    ],

config/redis_queue.php

<?php

return [
    'default' => [
        'host'    => 'redis://127.0.0.1:6379',
        'options' => [
            'auth'          => env('REDIS_PASSWORD',''),            // 密码,可选参数
            'db'            => 3,                       // 数据库
            'max_attempts'  => 5,                       // 消费失败后,重试次数
            'retry_seconds' => 5,                       // 重试间隔,单位秒
        ]
    ],
];

config/redis.php

<?php
/**
 * This file is part of webman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 */

return [
    'default' => [
        'host'     => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', ''),
        'port'     => env('REDIS_PORT', '6379'),
        'database' => 0,
    ],
];

app/queue/redis/ConverterSend.php

队列自动消费类

<?php

namespace app\queue\redis;

use Exception;
use support\Log;
use Webman\RedisQueue\Consumer;

class ConverterSend implements Consumer
{
    // 要消费的队列名
    public string $queue = 'converter_list';

    // 连接名,对应 config/redis_queue.php 里的连接`
    public string $connection = 'default';

    // 消费

    /**
     * 自动消费
     *
     * 队列生产 $contact = [
     * 'title' => $i,
     * ];
     * $a = RedisQueueService::instance()->_send($contact);
     *
     * @throws Exception
     */
    public function consume($data)
    {
        Log::info($data['title']);
    }
}

app/service/RedisQueueService.php

队列生产服务类

<?php

// *******************
// *** 消息队列生产者
// *******************

namespace app\service;

use support\Log;
use Webman\RedisQueue\Client;

class RedisQueueService
{
    public static ?RedisQueueService $_instance  = null;
    protected string                 $queue_name = 'converter_list';

    /**
     * @return RedisQueueService|mixed
     */
    public static function instance(): ?RedisQueueService
    {
        if (!static::$_instance) static::$_instance = new self();
        return static::$_instance;
    }

    /**
     * @param string $name
     * @return $this
     */
    public function setName(string $name = ''): RedisQueueService
    {
        $this->queue_name = $name;
        return $this;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->queue_name;
    }

    /**
     * 投递消息
     *
     * @param array $data
     * @param int   $delay
     * @return bool
     */
    public function _send(array $data = [], int $delay = 0): bool
    {
        $queue = $this->queue_name;
        Client::send($queue, $data, $delay);      // 投递延迟消息,消息会在 $delay 秒后处理
        return true;
    }
}

队列生产实例

$contact = [
            'title' => 1,
        ];
RedisQueueService::instance()->_send($contact);

效果消费能力速度

简单消费写入日志

Log::info($data'title');

服务器配置:

2核2G

队列数量 消费时间

  • 1W=2S
  • 10W=21S
  • 100W=1m33s