think-queue消息队列实践

背景:由于工作上有一个需求,需要我打开设备柜门,这个操作需要请求第三方接口,所以想到使用消息队列处理开门命令。

至于为啥要选think-queue,明明有那么多成熟的消息队列中间件,rabbitMQ之类的,但我看重的就是一点,方便啊,我本来就是用Tp5框架,框架自带的扩展,而且也满足我的要求,干嘛还要折腾别的MQ呢。PS:完整版的TP5是自带很多扩展功能的,不需要再用composer安装依赖。

消息队列是什么?

消息队列,包含两部分,分别是生产者消费者,生产者负责将信息插入队列,消费者负责取出队列的数据,然后根据数据的信息作出不同的处理(这也就是他叫“消息”队列的原因了,以前我还以为这货是拿来做推送的功能的= =!),处理成功之后,可以删除这条信息;失败的话也可以重新插入。而连通生产者和消费者的,叫做消息队列中间件(生产者和消费者之间并没有通信),通常的中间件有RedisrabbitMq等,本次我们采用的是Redis,rabbitMq这种比较专业的还是大项目用吧,我们就要一个小功能而已。

为什么消息队列这么快?

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式

(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端

 

(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

 

假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)

小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:

 

按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍

上代码!

首先是生产者的代码,如下。

    public function push(){
        // 1.当前任务将由哪个类来负责处理。
        //   当轮到该任务时,系统将生成一个该类的实例,并调用其 fire 方法
        $jobHandlerClassName = 'application\index\job\Hello';
        // 2.当前任务归属的队列名称,如果为新队列,会自动创建
        $jobQueueName = "helloJobQueue";
        // 3.当前任务所需的业务数据 . 不能为 resource 类型,其他类型最终将转化为json形式的字符串
        //   ( jobData 为对象时,需要在先在此处手动序列化,否则只存储其public属性的键值对)
        $jobData = ['ts' => time(), 'bizId' => uniqid(), 'a' => 1];
        // 4.将该任务推送到消息队列,等待对应的消费者去执行
        $isPushed = Queue::push(Hello::class, $jobData, $jobQueueName);
        // database 驱动时,返回值为 1|false  ;   redis 驱动时,返回值为 随机字符串|false
        if ($isPushed !== false) {
            echo date('Y-m-d H:i:s') . " a new Hello Job is Pushed to the MQ" . "
"; } else { echo 'Oops, something went wrong.'; } }

消费者:

checkDatabaseToSeeIfJobNeedToBeDone($data);

        if(!$isJobStillNeedToBeDone){
        $job->delete();
              return;
          }

          $isJobDone = $this->doHelloJob($data);

          if ($isJobDone) {
              //如果任务执行成功, 记得删除任务
              $job->delete();
              print("Hello Job has been done and deleted"."\n");
          }else{
              if ($job->attempts() > 3) {
                  //通过这个方法可以检查这个任务已经重试了几次了
                  print("Hello Job has been retried more than 3 times!"."\n");
                  $job->delete();
                  // 也可以重新发布这个任务
                  //print("Hello Job will be availabe again after 2s."."\n");
                  //$job->release(2); //$delay为延迟时间,表示该任务延迟2秒后再执行
              }
          }
      }

    /**
          * 有些消息在到达消费者时,可能已经不再需要执行了
     * @param array|mixed    $data     发布任务时自定义的数据
     * @return boolean                 任务执行的结果
          */
    private function checkDatabaseToSeeIfJobNeedToBeDone($data){
    return true;
}

    /**
     * 根据消息中的数据进行实际的业务处理
     * @param array|mixed    $data     发布任务时自定义的数据
     * @return boolean                 任务执行的结果
     */
    private function doHelloJob($data) {
        // 根据消息中的数据进行实际的业务处理...
        echo '恭喜你,成功了';

        return true;
    }
}

运行生产者的代码,输出如下信息,成功插入到redis了

也可以打开redis-cli查看

think-queue消息队列实践_第1张图片

接着我们打开命令行,cd到项目根目录,然后输入以下的命令。

//基本命令,执行一次就结束
php think queue:work --queue helloJobQueue

//持久命令,开启守护进程,不关窗口就会一直运行
php think queue:work --daemon --queue helloJobQueue

//终极命令,守护进程后台执行,关闭窗口也会一直运行
nohup php think queue:work --queue helloJobQueue

这就是成功了

你可能感兴趣的:(Redis,php)