多请求多数据量综合页面优化方案

关于页面优化一直是一个让人头痛的问题,除了修正那些不负责任的代码外,要考虑的因素还有很多。系统优化,我觉得就好比医生治病,你必须准确的找到病因,手起刀落,干净利索。今天就遇到了一个重症患者。


问题描述


之前做了一个综合展示页面,这个页面布局就想一个桌子,上面摆满了,水果,青菜,大鱼大肉,还有碗筷刀叉,总之东西很多,所以页面显示很慢。打开页面会有10s左右的卡死状态。老大让我解决这个问题。


问题分析


首先。浏览器F12查看请求量,除去权限控制,最大请求个数大概 十几个, 而且,当时的开发者为了解决初始化慢的问题,把绝大多数请求都放到 js 里,所以 页面初始化完毕之后, js 还在不停的向后台请求数据,造成了页面卡住的现象。

然后逐一去看这些请求,除了自己写查库等基本操作,还有从之前写接口获取的数据,还有从三方支持的接口获取的数据。

分析日志,并没有耗时特别长的sql。

最后归纳一下分析出来的问题

  1. 请求数量太多
  2. 原接口无用数据太多
  3. 三方接口效率低


解决方案


  • 把多个请求归纳到一个。减少通信消耗时间

  • 重写获取数据方法,去除掉所有无用操作和无用数据。

  • 分发,异步执行多任务,然后统一返回,去除同步执行代码消耗时间。

  • 对于一些耗时长的逻辑加缓存


技术实现


gearman实现异步分发处理任务


关于gearman 的安装和基本概念就不赘述了,我就简述一些我使用gearman 如何并发执行多任务的过程。

不多BB,直接上代码

public function testTask($uid)
    {
        $GLOBALS['_gm'] = [];  //因为要把回调函数里边的数据拿出来,目前想到的就是用全局变量来处理
        $client = new \GearmanClient();
        $client->addServer($server, $port);
        $client->setCompleteCallback(function($task) {
            $GLOBALS['_gm'][$task->unique()] = json_decode($task->data(), true);
        });
        $client->setTimeout(3000);
        $client->addTask("function1", json_encode($fileData), '', 'data1');
        $client->addTask("function2", json_encode($fileData), '', 'data2');
        $client->addTask("function3", json_encode($fileData), '', 'data3');
        $client->addTask("function4", json_encode($fileData), '', 'data4');
        $client->addTask("function5", json_encode($fileData), '', 'data5');
        $client->addTask("function6", json_encode($fileData), '', 'data6');
        $client->addTask("function7", json_encode($fileData), '', 'data7');
        $client->addTask("function8", json_encode($fileData), '', 'data8');

        if ($client->runTasks()) {
            return $GLOBALS['_gm'];
        } else {
            return false;
        }
    }

一些解释:

  • setCompleteCallback() : 文档里边有具体描述。  当 task complete 时 回调, 这边我需要拿到任务处理完毕后的数据,而且需要标记哪个结果对应哪个任务(后来想了一下,也可以在返回值里加标记),addTask 的 第四个参数用来标记任务,当全部处理完毕后,获取所有数据。

  • 因为在回调方法里不能操作一般变量,所以用全局变量来获取返回值。

  • setTimeout 是设置执行时间,如果不设置的话,当worker 程序出错不能及时返回的话,系统会一直卡主直到502。

后端代码

/**
     * [getWorker 获取worker]
     * @DateTime 2018-01-25T19:03:19+0800
     * @return   [type]                   [description]
     */
    public function getWorker()
    {
        $worker    = new \GearmanWorker();
        $worker->addServer($server, $port);
        //
        $worker->addFunction("funciton1", function($job){
            $data = $job->workload();
            $params = json_decode($data, true);
            ...
            return json_encode($data1);
        });
        $worker->addFunction("funciton2", function($job){
            $data = $job->workload();
            $params = json_decode($data, true);
            ...
            return json_encode($data2);
        });


        ....
        
        return $worker;
    }


    /**
     * [testWorker 执行脚本]
     * @Author   xuao
     * @DateTime 2018-01-25T19:03:35+0800
     * @return   [type]                   [description]
     */
    public function testWorker()
    {
        $worker = $this->getHrWorker();
        while ($worker->work());
    }

说明:

  • getWorker() 负责提供一个 注册 function1-8的worker。

  • 执行testWorker() 启动worker,通过启动多个进程可以启动多个worker。本人用superviser 来管理进程,并且 suiperviser 支持同一脚本启动多个进程。

  • 如果感兴趣的话可以加一些输出,是真正的异步执行。关于并发执行任务就介绍完毕。  


加 redis 缓存解决频繁统计数据问题


获取的这些数据中,有一部分数据变更不是很频繁,并且有一个特点是,只有添加数据,没有编辑数据。然后我采用了这样的缓存方案。


先根据查询条件查到需要的数据,然后把 查询条件 和 查询的数据的最大id 拼起来拼成key ,然后对查询到的数据进行处理, 把处理完的数据作为 value, key => value 存到redis 并设置过期时间。 


当再次执行该函数的时候,先查询缓存中是否有对应的数据,如果有,拿出来,并更新过期时间,如果没有, 重新获取,并重新生成缓存。


使用的扩展是 phpredis。 


结果


这一套整完之后,再打开这个页面, 1s 以内全部请求结束,舒服。

关于业务层面一些具体实现,因为没有跟产品沟通,就对具体逻辑没有做调整,所以,依然有优化空间。

你可能感兴趣的:(解决问题)