2

全部,

如何检测问题的背景

我的问题涉及网络应用程序的性能,主要是索引页面。当我在我公司的本地分支机构进行演示时,我注意到了这个问题,该分支机构的互联网速度很慢(我不知道确切的速度或 ping 速率),根据 Google 需要大约 10 秒的时间来加载这一事实来判断。我的索引页面需要大约 10-20 倍的时间来加载。我假设我的应用程序在服务器端完成了大部分工作(因为 php 正在进行所有数据库查询......)。但这让我查看了 Chrome 的网络工具,并查看了这 4 个 div 被 ajax 加载的延迟时间(我会详细说明)。有趣的是,被调用的脚本似乎是按顺序运行的,但不一定按照我调用 ajax 调用的顺序(有时它们会,有时它们不会)。

这些 divs / ajax 请求是什么?

以下是请求的代码片段:

    Yii::app()->clientScript->registerScript('leftDiv', '
    $( "#left_dash" ).load(
        "'.$this->createUrl("/site/page?view=leftDashLoad") .'", 
         function(){ 
            $("#left_dash p a").click(function() {
                $(this).parent().parent().find("div.scroll100").slideUp();
                $(this).parent().next().stop(false, false).slideDown();
            });

            $("p:first-child").next().slideDown();
        }
     );
 ' );

这是请求的页面:

$this->widget('widgets.ScrollList',array(
    'condition'=> 
        function($muddJob,$scrollList) 
        {
              $job = $muddJob->Job;; //returns a job or empty array
              if(!empty($job) )
              {
                 if( $muddJob->uploadArtwork == null && $muddJob->uploadData == null  ) {
                     array_push($scrollList->_models,$job);
                     $scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1);
                     return true;
                 }

              }

              return false;

        },
     'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Dealer Name"),
     'name'=> "Print New Ticket",
     'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),

        )
    ); 

想象一下那个页面(ajax 调用的页面)有 6 个类似的声明来创建小部件。目标是返回 html 以代替索引页面上的加载 gif。

这是滚动小部件:

<?php
Yii::import('widgets.ScrollListBase');
include_once Yii::app()->extensionPath . "/BusinessDay.php"; 

class ScrollList extends ScrollListBase
{
    private $_content;
    public $columns = array();
    public $columnValues;
    private  $_listInfo;
    public $name;
    public $_models = array();
    public $condition; 
    public $muddJobs; //object to pass
    public $jobsMailingTodayArray = array();


public function init()
    {
        //$this->init();
        $this->_listInfo = $this->generateListInfo($this->columns);
        //$muddJobs = $this->getCurrentMuddExchanges(); 
        $listInfo = $this->newScrollList($this->muddJobs);
        $contents = $this->createContent($listInfo,$this->name);
        $this->_content = $contents[0];
//        $this->_fullTableContent = $contents[1];
        //$this->_listInfo = $contents[2];
    }

   public function run()
   {
       //if($this->data['isVisible']) 
       echo $this->_content;
       Yii::app()->session["exploded_content_{$this->name}"] = $this->_models;
   }
    private function newScrollList($muddJobs)
        {
                $listInfo = $this->_listInfo;
                $tempCount = 0;


                foreach($muddJobs as $muddJob) 
                    {

                        $condition = $this->condition;

                        if($condition($muddJob,$this) && empty($this->jobsMailingTodayArray) ) //if no job exists for the muddExchange...
                        {
                            $tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
                            $listInfo = $tempArray[0];
                            $tempCount = $tempArray[1];

                        }

                        elseif ( !empty($this->jobsMailingTodayArray ) )
                        {
                            foreach ($this->jobsMailingTodayArray as $jobMailingToday)     //change to for loop over the length of the jobsMailingToday
                            {
                                $tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
                                $listInfo = $tempArray[0];
                                $tempCount = $tempArray[1];
                            }
                            $this->jobsMailingTodayArray = array();
                        }

                    }

                return array($listInfo,$tempCount);


        }

    }
?>

这是它的父母:

<?php


class ScrollListBase extends CWidget
{       

        private $content = "<p>";
        private $divDeclaration = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n";
        private $headTag = "<th>";
        private $headTagClose = "</th>\n";
        private $theadTagClose = "</thead>\n";
        private $bodyTag = "<tbody>\n";
        private $listInfo = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";


        /**
     * Initializes the widget.
     */

        public function createContent($listInfo,$name)
        {

                $largeHref = Yii::app()->request->baseUrl . '/index.php/site/fullTableView';

                $this->content .= "<span class='badge' >{$listInfo[1]}  </span> <a href='#'>{$name} </a> <a href='$largeHref/Name/{$name}'> <small>(view larger)</small> </a> </p>";


                if( $listInfo[1] > 0 ) 
                {
//                    $this->fullTable .= substr($listInfo[0],22);            
//                    $this->fullTableContent= $this->fullContent .= $this->fullTable . "</tbody>\n</table>\n</div>";

                    $this->content .= $listInfo[0] . "</tbody>\n</table>\n</div>";
                }

            return array($this->content);
        }

//Helper Methods

        /**
         * 
         * @param type $attributeArray. send an accociative array
         * @return type = either a job or an empty array
         */
        protected function getJobByAttributes($attributeArray)
        {
            return Jobs::model()->with('MuddExchange')->findByAttributes($attributeArray);

        }


        protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
       {
            $tempCount++;
            $viewIndex = $muddJob->exchange_id;
            $model = $muddJob;
            $job = $muddJob->Job;
            if ( isset($job ))
            {
                $model = $job;
                $url = "/Jobs/";
                $viewIndex = $model->job_id;
            }
            $link = CHtml::link("$model->jobNumber",array("{$url}{$viewIndex}"));
            $listInfo .= "<tr>\n<td>$link</td>\n";

            foreach ($columnValues as $columnValue)
            {
                $listInfo .= "<td>{$columnValue}</td>\n";
            }

            $listInfo .= "</tr>";

            return array($listInfo,$tempCount);
       }



       protected function getListInfo()
       {
           return $this->listInfo;
       }

       /**
        * Takes an array of strings to generate the column names for a particular list.
        * @param array $heads
        * @return string 
        * 
        */
       protected function generateListInfo($heads) 
       {
           //<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
           $htmlScrollStart = $this->divDeclaration;

           foreach ($heads as $tableColumn => $name)
           {
               $htmlScrollStart .= $this->headTag . $name . $this->headTagClose;
           }

           $htmlScrollStart .= $this->theadTagClose . $this->bodyTag;

           return $htmlScrollStart;
       }





    public function calculateDueDate($jobsMailDate,$job)
    {
        $package = PackageSchedule::model()->findByAttributes(array('package_id'=>$job->packageID));
        $projectedDays = $package->projected_days_before_mail_date;

        $dropDate1 = $jobsMailDate->projected_mail_date;
        $dropDate = wrapBusinessDay($dropDate1); //use this for actual command...  
        $toSec = 24*60*60;
        $dayInt =0;
        $secDropDate = strtotime($dropDate1);

        do{
           $dayInt +=1; 
           $daysInSec = ($dayInt)  * $toSec ;
           $secGuessDueDate = $secDropDate - $daysInSec; 
           $dueDate = date('Y-m-d',$secGuessDueDate);

           $difference = $dropDate->difference($dueDate);


        }while( $difference != $projectedDays);
        return $dueDate;
    }
}
?>

为什么我认为这种行为很奇怪

整个缓慢的互联网本身就是一头野兽,但我认为这不在 StackOverflow 的范围内。我更关心这些 div 的加载。最后加载的 div,即平均需要 1.5 到 2 秒,是对创建单个小部件的页面的 ajax 请求。其背后的逻辑在这里:

<?php
 include_once Yii::app()->extensionPath . "/CurrentExchanges.php"; 
 $currentExchanges = Yii::app()->session['currentExchanges'];
 $this->layout = 'barebones';
 $this->widget('widgets.ScrollList',array(
    'condition'=> 
        function($muddJob,$scrollList) 
        {
          if ($muddJob->dropDate1 != null && $muddJob->dropDate1 != '0000-00-00')
            {
                $job = $muddJob->Job;;
                if(!empty($job) && $job->packageID != null) //if job exists for the muddExchange and has a package
                {
                    if($job->uploadArtwork == null )
                    {
                        $jobsMailDate = JobsMailDate::model()->findByAttributes(array("job_id"=>$job->job_id,'sequence_num'=>1));
                        if(!empty($jobsMailDate))
                            {
                               $calculatedDueDate = $scrollList->calculateDueDate($jobsMailDate,$job);                                
                                if (strtotime($calculatedDueDate) <= strtotime(date("Y-m-d")) )
                                {
                                    array_push($scrollList->_models , $job);
                                    $scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1,$jobsMailDate->projected_mail_date);
                                    return true;
                                }
                            }
                    }
                }
            }
            return false;
        },
     'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Drop Date", 'col4' =>'Projected Drop Date'),
     'name'=> "Artwork Due Today",
     'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),

        )
    );
?>

computeduedate 方法对服务器进行了 2 次额外调用。

我不明白的是,为什么左 div (处理最多的)通常是第一个返回的,而 artLoad 通常是最后一个加载的(相差很大)。以下是 chromes 网络工具返回的一些时间:

leftDashLoad:  475ms
rightDashLoad: 593ms
dataLoad:      825ms
artLoad:       1.41s

dataLoad:      453ms
rightDashLoad: 660ms
leftDashLoad:  919ms
artLoad:       1.51s

rightDashLoad: 559ms
leftDashLoad:  1.17s
dataLoad:      1.65s  
artLoad:       2.01s

我只是无法理解为什么左/右仪表板的返回速度比 artLoad 快得多。除了实际比较之外,artLoad 和 dataLoad 的代码几乎相同(一个 if 语句)。如果这真的是异步的,我希望顺序是 art/dataLoad、rightDashLoad 和 leftDashLoad 纯粹基于每个页面中完成的计算量。也许服务器不是多线程的,或者有一些奇怪的配置,但如果是这样的话,我不明白为什么加载的影响会受到缓慢的互联网的严重打击。如果我忽略了一些明显的事情或未能正确使用谷歌,我深表歉意。谢谢你尽你所能的帮助!

语言/其他技术信息

该应用程序是使用 Yii 框架开发的。PHP 5.3。阿帕奇服务器。INNODB 表。服务器托管在dreamhost 中。

更新

我已更改视图页面,以便 ajax 调用现在调用控制器操作。在我的本地开发环境中,它似乎使加载时间更加相似(异步?),但在 QA 环境(托管在 dreamhost...)上确实减慢了加载时间。这是本地网络工具信息的屏幕截图:

开发环境

和 qa 站点(请注意,数据库的数据量大致相同......)

质量保证环境

想法?在我看来,我最初的问题可能会得到解决,因为返回时间(在我的本地开发人员上)看起来更像我期望的那样。

此外,由于 xdebug 正在使用会话,我自己对 NetBeans 调试如何在此同步加载中发挥作用一无所知。我相信这迫使 ajax 调用等待轮到他们。

4

1 回答 1

1

感谢@Rowan 帮助我诊断出这种奇怪的行为。PHP 试图在会话关闭之前请求会话,以防止出现竞争危险。我的代码中有会话请求,并且我的 IDE (NetBeans) 启动了一个会话。在 ajax 调用页面中删除对会话的所有引用并使用 renderPartial() 进行 ajax 调用操作被证明可以返回预期的行为(以及更好的代码 IMO)。让我知道如何在 StackOverflow 方面正确地感谢用户(我可以发表评论,或者有什么可用的吗?)。谢谢你们!

于 2013-09-25T22:15:50.520 回答