相信很多小伙伴都在开发中使用过消息队列,尤其是高并发的情况,一般可以在缓存中操作数据,然后通过消息异步处理业务逻辑,操作数据库等。

本人所在的公司使用了阿里云的消息队列和RabbitMQ,据说使用阿里云消息队列的一部分原因是RabbitMQ实现延迟消息比较复杂,要依赖死信...接下来进入主题,说说我是怎么使用消息和遇到的坑吧~

一般来讲,我们可以使用一个脚本来接收阿里云消息处理业务逻辑,但是如果业务量特别大的话,我们可能会遇到一个问题,就是脚本处理不过来,消息积攒的数量可能远远超出每秒能够处理的数量。针对这种现象,我们可以启动多个处理脚本来同时处理消息,这样会明显加快处理消息的速度。

但是,多个脚本同时从一条消息队列里面取消息的时候,会不会同时取到同一条消息,然后造成消息重复处理的现象呢?我觉得肯定是会的,消息是第三方服务,我们无法保证他的100%稳定,所以我们需要在处理的时候下点功夫了。

我们发送消息的数据体是json,一般我们会在每条消息里面加一个taskid,以时间戳(精确到毫秒级) + 随机数组成,这个taskid足够长,我们得以保证他不会重复(重复的可能性极小,类似于mongodb的主键也是这个原理)。

接下来看一段代码:

<?php

try {
    //伪代码
    $getData    = $mq->receive();
    $getData    = \Zend\Json\Json::decode($getData, true);  //如果不是json数据 我们可以捕获到异常

    //先检测数据
    $errorMsg   = [];
    if (!isset($getData['taskid']) || $getData['taskid'] == '') {
        $errorMsg[]     = 'taskid不能为空';
    }

    if (!isset($getData['order_id']) || $getData['order_id'] == '') {
        $errorMsg[]     = '订单号不能为空';
    }

    if (!cache()->setex($this->cachePrefix . $getData['taskid'], 1)) {
        $errorMsg[]     = '该任务已被处理';
    }

    if (count($errorMsg) >= 1) {
        //记日志  方便排错
        $this->log('xxxx处理脚本错误 哪个文件 错误级别 错误原因' . implode('|', $errorMsg));
        return false;
    }

    //必须有过期时间  不然会把redis撑爆
    cache()->expire($this->cachePrefix . $getData['taskid'], 7 * 86400);

    /**
     * 处理业务逻辑
     */
    
    //业务逻辑处理正常,删除redis锁,删除消息
    cache()->del($this->cachePrefix . $getData['taskid']);
    $mq->ack();
    return true;
}

//捕获到了异常
catch (\Exception $e) {
     //一定要把这次消息删掉 不然会重复进来 日志错误级别记高一点 手动处理问题
    $mq->ack();
    //记日志
    $this->log();
}

小伙伴们知道这段代码哪里有问题吗?


zhanglei
19 声望0 粉丝

不要轻易否定别人,你也许会一步步看着你曾经那个不认可的人比你过的更好~