Loading

通过redis的有序集合[zset] 实现延迟队列

总结一下php使用redis的有序集合zset实现延迟队列

将消息数据序列化,作为zset发基本元素,把 消息生产时间戳+消息处理延迟时间戳  作为score,每次通过

zRangeByScore获取一条消息进行处理,后通过zRem删除集合元素,相当于移除需要消费的Job,浅谈一下优点:

  • 实现简单,适合做中小型对延迟时间要求不高的业务场景
  • 浅谈一下缺点:
    1.不适合延迟时间比较高的业务场景,延迟时间可能会有几秒钟的误差
    2.不适合大型项目,大型项目建议使用rabbitmq或者kafka延迟消息队列
    <?php
    class DelayQueue
    {
      protected $prefix =   'delay_queue:' ;  //队列名称
      protected $redis = null;
      protected $key =   '' ;
      public function __construct(  $queue ,   $config = [])
      {
        $this ->key =   $this ->prefix .   $queue ;
        $this ->redis =   new Redis();
        $this ->redis->connect(  $config [  'host' ],   $config [  'port' ],   $config [  'timeout' ]);
        $this ->redis->auth(  $config [  'auth' ]);
      }
      public function delTask(  $value )  //删除任务
      {
        return $this ->redis->zRem(  $this ->key,   $value );
      }
      public function getTask()  //获取任务
      {
        //获取任务,以0和当前时间为区间,返回一条记录
        return $this ->redis->zRangeByScore(  $this ->key, 0, time(), [  'limit' => [0, 1]]);
      }
      public function addTask(  $name ,   $time ,   $data )
      {
        //添加任务,以时间作为score,对任务队列按时间从小到大排序
        return $this ->redis->zAdd(
          $this ->key,
          $time ,
          json_encode([
            'task_name' =>   $name ,
            'task_time' =>   $time ,
            'task_params' =>   $data ,
          ], JSON_UNESCAPED_UNICODE)
        );
      }
      public function run()
      {
        //每次只取一条任务
        $task =   $this ->getTask();
        if (  empty (  $task )) {
          return false;
        }
        $task =   $task [0];
        //有并发的可能,这里通过zrem返回值判断谁抢到该任务
        if (  $this ->delTask(  $task )) {
          $task = json_decode(  $task , true);
          //处理任务
          echo '任务:' .   $task [  'task_name' ] .   ' 运行时间:' .   date (  'Y-m-d H:i:s' ) . PHP_EOL;
          return true;
        }
        return false;
      }
    }
    $dq =   new DelayQueue(  'close_order' , [
      'host' =>   '127.0.0.1' ,
      'port' => 6379,
      'auth' =>   '' ,
      'timeout' => 60,
    ]);
    $dq ->addTask(  'close_order_10' , time() + 30, [  'order_id' =>   '10' ]);
    $dq ->addTask(  'close_order_20' , time() + 60, [  'order_id' =>   '20' ]);
    $dq ->addTask(  'close_order_30' , time() + 90, [  'order_id' =>   '30' ]);

    然后,我们写一个php脚本,用来处理队列中的任务。

    <?php
    set_time_limit(0);
    $dq =  new DelayQueue( 'close_order' , [
      'host' =>  '127.0.0.1' ,
      'port' => 6379,
      'auth' =>  '' ,
      'timeout' => 60,
    ]);
    while (true) {
      $dq ->run();
      usleep(100000);
    }

     

posted @ 2022-08-28 18:23  LiJialong  阅读(243)  评论(0编辑  收藏  举报