redis队列应用

### redis队列应用--数据批量处理

---

###### 优化原因

> 客户端会将玩家游戏相关的数据发送给服务端,服务端再将数据保存到数据库或者上报到BI。之前的逻辑是每次收到请求服务端都是直接操作数据库保存或者上报到Bi,存到性能问题。

###### 优化思路

> 服务端收到客户端发过来的数据上报请求后,将数据放到redis队列中,然后使用定时脚本从队列里面拉取数据并进行处理。

###### 相关代码

上報数据写入队列

```php
$key = Keys::biEventQueue();
Dao::redis()->rPush($key, json_encode($data));
```

批量拉取队列数据

```php
     /**
     * 批量消费数据,分批次消费,再合并结果
     * @param int $total 总数量
     * @param int $limit 每次拉取数量上限
     */
    public function consumeReportQueue($total, $limit = 100)
    {
        $list = [];
        $limit = min($total, $limit);
        $key = Keys::biEventQueue();
        $redis = Dao::redis();

        while (1) {
            $count = min($total, $limit);
            if ($count <= 0) {
                break;
            }
            //使用事务,保证拉取操作的原子性
            $redis->watch($key);
            $redis->multi();
            $redis->lRange($key, 0, $count - 1);
            $redis->lTrim($key, $count, -1);
            $redis->lLen($key);
            //返回一个数组 对应redis每个操作的返回值
            $result = $redis->exec();
            
            if (!$result) {
                Log::info('consume failed', 'bi-report.log');
                continue;
            }
            if (!$result[0]) {
                break;
            }
            //合并数据
            $list = array_merge($list, $result[0]);
            
            if (!$result[2]) {
                break;
            }
            $total -= $count;
        }

        return $list;
    }
```

定时脚本消费数据

```php
$pidFile = PATH_LOG . '/bi-report';
$pid = getmypid();
$time = time();
$batch = 0;

//判断当前是否有定时脚本在执行,保证同时只能有一个脚本在执行任务
if (file_exists($pidFile)) {
    cli_exit('pid file exists');
}

file_put_contents($pidFile, $pid);

while (1) {
    cli_output('batch = ' . ++$batch);
    $dataGroups = array();
    //拉取数据
    $list = Bll::biData()->consumeReportQueue(1000);
    $batchCount = count($list);
    Log::info(['consume', $pid, $batch, $batchCount], 'bi-report.log');
    cli_output('batchCount = ' . $batchCount);
    if (!$list) break;
    foreach ($list as $row) {
        $row = json_decode($row, true);
        $dataGroups[$row['event']][] = $row;
    }
    //消费数据
    foreach ($dataGroups as $eventName => $list) {
        cli_output('eventName = ' . $eventName);
        cli_output('count = ' . count($list));
        $chunks = array_chunk($list, 50);
        foreach ($chunks as $k => $_list) {
            cli_output('chunk = ' . ($k + 1));
            try {
                Bll::biData()->eventReport(null, $eventName, $_list, true);
            } catch (Exception $e) {
                Log::info([$eventName, $_list], 'bi-report.log');
            }
        }
    }
    //退出判断
    if ($batchCount < 500) {
        Log::info(['end', $pid, $batch, time() - $time], 'bi-report.log');
        break;
    }
}

unlink($pidFile);

cli_exit('Done');
```

function exec_command(&$cmd, $async = false, $log_file = '')
{
    if (IS_WIN && $async) {
        $cmd .= ' > ' . ($log_file ?: 'nul');
        pclose(popen("start /B " . $cmd, "r"));
    } else {
        $cmd .= ' > ' . ($log_file ?: '/dev/null');
        if ($async) $cmd .= " &";
        exec($cmd);
    }
}

function exec_php_file($file, $args = null, $async = false, $log_file = '')
{
    $cmd = "php {$file}";

    if (!$args || !is_array($args)) {
        $args = array();
    }
    $args['env'] = ENV;
    foreach ($args as $k => $v) {
        $cmd .= " {$k}={$v}";
    }

    exec_command($cmd, $async, $log_file);

    return $cmd;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值