Praktische Weitergabe der Verwendung von Swoole zum asynchronen Crawlen von Webseiten

*文
Freigeben: 2023-03-18 08:02:01
Original
5267 Leute haben es durchsucht

PHP-Programmierer wissen alle, dass in PHP geschriebene Programme alle synchron sind. Wie schreibt man ein asynchrones Programm in PHP? Hier nehmen wir das Crawlen von Webinhalten als Beispiel, um zu zeigen, wie man Swoole zum Schreiben asynchroner Programme verwendet.

PHP-Synchronisationsprogramm

Bevor Sie ein asynchrones Programm schreiben, machen Sie sich keine Sorgen, verwenden Sie zuerst PHP, um das Synchronisationsprogramm zu implementieren.

<?php
/**
 * Class Crawler
 * Path: /Sync/Crawler.php
 */
class Crawler
{
    private $url;
    private $toVisit = [];
    public function __construct($url)
    {
        $this->url = $url;
    }
    public function visitOneDegree()
    {
        $this->loadPageUrls();
        $this->visitAll();
    }
    private function loadPageUrls()
    {
        $content = $this->visit($this->url);
        $pattern = &#39;#((http|ftp)://(\S*?\.\S*?))([\s)\[\]{},;"\&#39;:<]|\.\s|$)#i&#39;;
        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)
    {
        return @file_get_contents($url);
    }
}
Nach dem Login kopieren
<?php
/**
 * crawler.php
 */
require_once &#39;Sync/Crawler.php&#39;;
$start = microtime(true);
$url = &#39;http://www.swoole.com/&#39;;
$ins = new Crawler($url);
$ins->visitOneDegree();
$timeUsed = microtime(true) - $start;
echo "time used: " . $timeUsed;
/* output:
time used: 6.2610177993774
*/
Nach dem Login kopieren

Eine vorläufige Studie zur Implementierung asynchroner Crawler durch Swoole

Beziehen Sie sich zunächst auf die offizielle Seite zum asynchronen Crawlen.
Anwendungsbeispiel

Swoole\Async::dnsLookup("www.baidu.com", function ($domainName, $ip) {
    $cli = new swoole_http_client($ip, 80);
    $cli->setHeaders([
        &#39;Host&#39; => $domainName,
        "User-Agent" => &#39;Chrome/49.0.2587.3&#39;,
        &#39;Accept&#39; => &#39;text/html,application/xhtml+xml,application/xml&#39;,
        &#39;Accept-Encoding&#39; => &#39;gzip&#39;,
    ]);
    $cli->get(&#39;/index.html&#39;, function ($cli) {
        echo "Length: " . strlen($cli->body) . "\n";
        echo $cli->body;
    });
});
Nach dem Login kopieren

Es scheint, dass durch eine leichte Änderung des synchronen file_get_contents-Codes eine asynchrone Implementierung erreicht werden kann. Es scheint, dass der Erfolg einfach ist.
Also haben wir den folgenden Code erhalten:

<?php
/**
 * Class Crawler
 * Path: /Async/CrawlerV1.php
 */
class Crawler
{
    private $url;
    private $toVisit = [];
    private $loaded = false;
    public function __construct($url)
    {
        $this->url = $url;
    }
    public function visitOneDegree()
    {
        $this->visit($this->url, true);
        $retryCount = 3;
        do {
            sleep(1);
            $retryCount--;
        } while ($retryCount > 0 && $this->loaded == false);
        $this->visitAll();
    }
    private function loadPage($content)
    {
        $pattern = &#39;#((http|ftp)://(\S*?\.\S*?))([\s)\[\]{},;"\&#39;:<]|\.\s|$)#i&#39;;
        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, $root = false)
    {
        $urlInfo = parse_url($url);
        Swoole\Async::dnsLookup($urlInfo[&#39;host&#39;], function ($domainName, $ip) use($urlInfo, $root) {
            $cli = new swoole_http_client($ip, 80);
            $cli->setHeaders([
                &#39;Host&#39; => $domainName,
                "User-Agent" => &#39;Chrome/49.0.2587.3&#39;,
                &#39;Accept&#39; => &#39;text/html,application/xhtml+xml,application/xml&#39;,
                &#39;Accept-Encoding&#39; => &#39;gzip&#39;,
            ]);
            $cli->get($urlInfo[&#39;path&#39;], function ($cli) use ($root) {
                if ($root) {
                    $this->loadPage($cli->body);
                    $this->loaded = true;
                }
            });
        });
    }
}
Nach dem Login kopieren
<?php
/**
 * crawler.php
 */
require_once &#39;Async/CrawlerV1.php&#39;;
$start = microtime(true);
$url = &#39;http://www.swoole.com/&#39;;
$ins = new Crawler($url);
$ins->visitOneDegree();
$timeUsed = microtime(true) - $start;
echo "time used: " . $timeUsed;
/* output:
time used: 3.011773109436
*/
Nach dem Login kopieren

Das Ergebnis lief 3 Sekunden lang. Achten Sie auf meine Implementierung. Nachdem ich eine Anfrage zum Crawlen der Homepage initiiert habe, werde ich jede Sekunde die Ergebnisse abfragen, und diese wird nach dreimaliger Abfrage beendet. Die 3 Sekunden hier scheinen der Exit zu sein, der durch dreimaliges Abfragen ohne Ergebnis verursacht wurde.
Es scheint, dass ich zu ungeduldig war und ihnen nicht genügend Vorbereitungszeit gegeben habe. Okay, ändern wir die Anzahl der Umfragen auf 10 und sehen uns die Ergebnisse an.

time used: 10.034232854843
Nach dem Login kopieren

Du weißt, wie ich mich in dieser Zeit fühle.

Ist es ein Leistungsproblem bei Swoole? Warum gibt es nach 10 Sekunden kein Ergebnis? Liegt es daran, dass meine Haltung falsch ist? Der alte Mann Marx sagte: „Übung ist das einzige Kriterium, um die Wahrheit zu prüfen.“ Es scheint, dass wir es debuggen müssen, um den Grund herauszufinden.

Also habe ich Haltepunkte bei

$this->visitAll();
Nach dem Login kopieren

und

$this->loadPage($cli->body);
Nach dem Login kopieren

hinzugefügt. Schließlich habe ich festgestellt, dass visitAll() immer zuerst und dann loadPage() ausgeführt wird.

Nachdem ich eine Weile darüber nachgedacht habe, verstehe ich wahrscheinlich den Grund. Was ist der Grund?

Das von mir erwartete asynchrone dynamische Modell sieht folgendermaßen aus:

Praktische Weitergabe der Verwendung von Swoole zum asynchronen Crawlen von Webseiten

Das tatsächliche Szenario sieht jedoch nicht so aus. Durch das Debuggen habe ich ungefähr verstanden, dass das tatsächliche Modell so aussehen sollte:


Praktische Weitergabe der Verwendung von Swoole zum asynchronen Crawlen von Webseiten

Mit anderen Worten, egal wie ich die Zahl erhöhe Bei vielen Wiederholungen werden die Daten erst dann ausgeführt, wenn die aktuelle Funktion bereit ist. Durch die Asynchronität wird nur die Zeit zum Vorbereiten der Verbindung verkürzt.
Dann stellt sich die Frage, wie ich das Programm dazu bringen kann, die Funktionen auszuführen, die ich nach der Aufbereitung der Daten erwarte.
Schauen wir uns zunächst an, wie Swooles offizieller Code zum Ausführen asynchroner Aufgaben geschrieben ist

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

Sie können sehen, dass der Beamte die nachfolgende Ausführungslogik durch eine anonyme Funktion leitet. So gesehen wird es viel einfacher.

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();
            });
        });
    }
}
Nach dem Login kopieren

Nachdem ich diesen Code gelesen habe, verspüre ich ein Déjà-vu-Gefühl. In der NodeJS-Entwicklung haben die allgegenwärtigen Rückrufe ihre eigenen Gründe. Jetzt verstehe ich plötzlich, dass es einen Rückruf gibt, um asynchrone Probleme zu lösen.
Ich habe das Programm ausgeführt, es dauerte nur 0,0007 Sekunden und es war vorbei, bevor es überhaupt gestartet war! Kann die asynchrone Effizienz wirklich so stark verbessert werden? Die Antwort lautet natürlich: Nein, mit unserem Code stimmt etwas nicht.
Aufgrund der Verwendung von Asynchronität wurde die Logik zur Berechnung der Endzeit ausgeführt, ohne auf den vollständigen Abschluss der Aufgabe zu warten. Es scheint, dass es an der Zeit ist, den Rückruf erneut zu verwenden.

/**
Async/Crawler.php
**/
    public function visitOneDegree($callBack)
    {
        $this->visit($this->url, function ($content) use($callBack) {
            $this->loadPage($content);
            $this->visitAll();
            call_user_func($callBack);
        });
    }
Nach dem Login kopieren
<?php
/**
 * crawler.php
 */
require_once &#39;Async/Crawler.php&#39;;
$start = microtime(true);
$url = &#39;http://www.swoole.com/&#39;;
$ins = new Crawler($url);
$ins->visitOneDegree(function () use($start) {
    $timeUsed = microtime(true) - $start;
    echo "time used: " . $timeUsed;
});
/*output:
time used: 0.068463802337646
*/
Nach dem Login kopieren

Aus heutiger Sicht sind die Ergebnisse viel glaubwürdiger.
Vergleichen wir den Unterschied zwischen synchron und asynchron. Die Synchronisation dauert 6,26 Sekunden und die asynchrone dauert 0,068 Sekunden, was einem Unterschied von ganzen 6,192 Sekunden entspricht. Nein, um es genauer auszudrücken: Es müsste fast zehnmal schlimmer sein!
Natürlich ist asynchroner Code hinsichtlich der Effizienz viel höher als synchroner Code, aber logisch gesehen ist asynchrone Logik komplizierter als synchrone, und der Code bringt viele Rückrufe mit sich, was nicht leicht zu verstehen ist.
Swoole hat eine Beschreibung zur Auswahl von asynchron und synchron, die ich gerne mit Ihnen teilen möchte:

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

Verwandte Lektüre:

So installieren Sie die Swoole-Erweiterung mit PHP7

Einführung in Swoole-Entwicklungspunkte

Beispiel zur asynchronen Verwendung von PHP mit mehreren Threads

Das Obige ist der gesamte Inhalt dieses Artikels. Wenn Schüler Fragen haben, können sie diese im diskutieren Kommentarbereich unten~

Das obige ist der detaillierte Inhalt vonPraktische Weitergabe der Verwendung von Swoole zum asynchronen Crawlen von Webseiten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!