Laravel 应用实践:如何优雅的完成全量数据同步 
                                                    
                        
                    
                    
  
                    
                    需求场景:
我们公司有个产品,用户可以通过第三方应用中的积分进行消费抵扣,好比用肯德基的积分去买麦当劳。每次积分交易都要去第三方系统同步积分余额进行校验,本地没有最新的积分余额信息。
负责运营的同学要定期统计第三方系统中用户剩余积分余额,技术同学为此做了一个批量同步功能,基本思路就是从数据库表里捞出全量数据,做一个大循环,每条用户记录调用同步接口把第三方的积分余额同步过来,再导出成Excel给运营。
早期,这样的实现没有问题,但是随着用户记录越来越多,运营同学统计的等候时间越来越慢,跑完一次同步需要很长时间,更严重的是引起锁表。
解决问题:
为了解决这个问题,提出几个优化目标:
- 数据统计不能影响正常业务,减少对数据库的压力
- 数据处理进度可见
- 按时提取,自动生成结果数据
优化方案
公司技术框架刚转Laravel,凡事先考虑Laravel现有框架是否能有方案支持,解题思路如下:
- Redis:把待同步的用户基础数据放在Redis队列中,通过LPOP方法,每次冒泡处理一个记录,减少处理过程中对数据库的依赖
- 通过对对队列长度的判断和数据总数,计算当前进度
- 以上逻辑用Artisan 命令行方式写成组件,可手动执行,也可以放在任务中定时运行
- 结果数据用插件 maatwebsite/excel 生成Excel文件供运营下载
实践
用到的Laravel框架知识栈:
Redis操作
Artisan 命令行
任务调度
整个方案核心就一个命令行组件,关于如何写Artisan命令行,可以看这篇 Laravel 5.1 Artisan 命令行实战
public function handle()
    {
        //命令行参数 --pre为预处理
        $pre = $this->option('pre');
        if ($pre) {
            $this->info('sync pre points!');    
            $users=User::all();
            //进度条
            $bar = $this->output->createProgressBar(count($users));
            //待处理记录放进队列
            foreach ($users as $user){
                Redis::RPUSH('sync_points',$user->youzan_wx_id);
                $bar->advance();
            }
            $bar->finish();
        } else {
            $total=Redis::LLEN('sync_points');
            if($total>0){
                $bar = $this->output->createProgressBar($total);
                for ($i = 0; $i < $total; $i++) {
                    $youzan_id=Redis::LPOP('sync_points');
                    //同步数据
                    $this->service->syncUser($youzan_id);
                    $bar->advance();
                }
                $bar->finish();
            }
            //调用另外一个命令行组件导出excel
            $this->callSilent('export:points');
        }
    }命令编写完成后,需要注册 Artisan 后才能使用。注册文件为 app/Console/Kernel.php。
敲这个命令的时候,运维瞬间感觉到了优雅
php artisan sync:points --pre尤其是看到这个进度表:
Artisan 命令行的进度条实现简直太方便:
$users = App\User::all();
// 多少个任务
$bar = $this->output->createProgressBar(count($users));
foreach ($users as $user) {
    $this->performTask($user);
    // 一个任务处理完了,可以前进一点点了
    $bar->advance();
}
$bar->finish();通过这个命令,同步数据可以手动执行,也可以放在定时任务中在业务不繁忙的夜间自动同步
如何把命令行放在定时任务中执行,看这里:任务调度
有了定时任务自动处理,每天早上,运营同学都可以在后台,自动拿到一份昨晚同步好的积分余额清单,瞬间也感觉到了优雅
总结
以上需求,从提出目标到实现,也就一个小时时间,结果皆大欢喜,大家都觉得很优雅。当然,关于处理这种场景,肯定还有更优化的方案,我在这里总结出来,仅仅是从具体场景分享一下Laravel框架的便捷和高度集成,能够快速响应业务需求,并且保持简洁,是我喜欢Laravel的一万个理由,没有之一。
本作品采用《CC 协议》,转载必须注明作者和本文链接
 
           老财 的个人博客
 老财 的个人博客
         
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
             
                     
                     
             
             
             
             
             
             
             
           
           关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: