美文网首页
用Swoole异步抓取网页(二)

用Swoole异步抓取网页(二)

作者: 小小浪把_Dont_know拍 | 来源:发表于2017-03-19 19:22 被阅读629次

    上篇我们讲到,无论我怎么调高重试次数,数据总是没有准备好。

    我期望的异步动态模型是这样的:


    asyncModel2.png

    然而真实的场景不是这样的。通过调试,我大致了解到实际的模型应该是这样的:

    asyncModel1.png

    也就是说,无论我怎么提高重试次数,数据永远不会准备好,数据只有在当前函数准备好以后,才会开始执行,这里的异步,只是减少了准备连接的时间。
    那么问题来了,我该如何让程序在准备数据之后执行我期望的功能呢。
    先看一下Swoole官方执行异步任务的代码是如何写的

    $serv = new swoole_server("127.0.0.1", 9501);
    
    //设置异步任务的工作进程数量
    $serv->set(array('task_worker_num' => 4));
    
    $serv->on('receive', function($serv, $fd, $from_id, $data) {
        //投递异步任务
        $task_id = $serv->task($data);
        echo "Dispath AsyncTask: id=$task_id\n";
    });
    
    //处理异步任务
    $serv->on('task', function ($serv, $task_id, $from_id, $data) {
        echo "New AsyncTask[id=$task_id]".PHP_EOL;
        //返回任务执行的结果
        $serv->finish("$data -> OK");
    });
    
    //处理异步任务的结果
    $serv->on('finish', function ($serv, $task_id, $data) {
        echo "AsyncTask[$task_id] Finish: $data".PHP_EOL;
    });
    
    $serv->start();
    

    代码2.1

    可以看到,官方是通过一个function匿名函数,将后续的执行逻辑传了进去。这么看,事情就变得简单多了。

    <?php
    
    /**
     * Class Crawler
     * Path: /Async/Crawler.php
     */
    
    class Crawler
    {
        private $url;
        private $toVisit = [];
        public function __construct($url)
        {
            $this->url = $url;
        }
    
        public function visitOneDegree()
        {
            $this->visit($this->url, function ($content) {
                $this->loadPage($content);
                $this->visitAll();
            });
        }
    
        private function loadPage($content)
        {
            $pattern = '#((http|ftp)://(\S*?\.\S*?))([\s)\[\]{},;"\':<]|\.\s|$)#i';
            preg_match_all($pattern, $content, $matched);
            foreach ($matched[0] as $url) {
                if (in_array($url, $this->toVisit)) {
                    continue;
                }
                $this->toVisit[] = $url;
            }
        }
    
        private function visitAll()
        {
            foreach ($this->toVisit as $url) {
                $this->visit($url);
            }
        }
    
        private function visit($url, $callBack = null)
        {
            $urlInfo = parse_url($url);
            Swoole\Async::dnsLookup($urlInfo['host'], function ($domainName, $ip) use($urlInfo, $callBack) {
                if (!$ip) {
                    return;
                }
                $cli = new swoole_http_client($ip, 80);
                $cli->setHeaders([
                    'Host' => $domainName,
                    "User-Agent" => 'Chrome/49.0.2587.3',
                    'Accept' => 'text/html,application/xhtml+xml,application/xml',
                    'Accept-Encoding' => 'gzip',
                ]);
                $cli->get($urlInfo['path'], function ($cli) use ($callBack) {
                    if ($callBack) {
                        call_user_func($callBack, $cli->body);
                    }
                    $cli->close();
                });
            });
        }
    }
    

    代码2.2

    看了这段代码,竟然有种似曾相识的感觉,在nodejs开发中,随处可见的callback原来是有它的道理的。现在我才突然明白,原来callback的存在就是为了解决异步问题的。
    执行了一下程序,竟然只用0.0007s,还没开始就已经结束了!异步的效率真的能提升这么多吗?答案当然是否定的,是我们的代码出问题了。
    由于用了异步,没有等任务完全跑完,就已经执行了计算结束的时间的逻辑。看来又到了用callback的时候了。

    /**
    Async/Crawler.php
    **/
        public function visitOneDegree($callBack)
        {
            $this->visit($this->url, function ($content) use($callBack) {
                $this->loadPage($content);
                $this->visitAll();
                call_user_func($callBack);
            });
        }
    

    代码2.3

    <?php
    /**
     * crawler.php
     */
    require_once 'Async/Crawler.php';
    
    $start = microtime(true);
    
    $url = 'http://www.swoole.com/';
    $ins = new Crawler($url);
    $ins->visitOneDegree(function () use($start) {
        $timeUsed = microtime(true) - $start;
        echo "time used: " . $timeUsed;
    });
    /*output:
    time used: 0.068463802337646
    */
    

    代码2.4

    现在来看,结果可信多了。
    让我们比较一下同步的异步的差距,同步耗时6.26s,异步耗时0.068秒,差了整整6.192s。不,更准确地表述,应该是差了将近10倍!
    当然,从效率上讲,异步远远高于同步的代码,但是从逻辑上讲,异步的逻辑比同步更绕,代码中会带来大量的callback,不便于理解。
    Swoole官方里有一段关于异步与同步的选择的描述,非常中肯,分享给大家:

    我们不赞成用异步回调的方式去做功能开发,传统的PHP同步方式实现功能和逻辑是最简单的,也是最佳的方案。像node.js这样到处callback,只是牺牲可维护性和开发效率。

    相关文章

      网友评论

          本文标题:用Swoole异步抓取网页(二)

          本文链接:https://www.haomeiwen.com/subject/pilvnttx.html