registrieren registriertes Mitglied


Anzeige

Anzeige

Sind hier PHP'ler unterwegs?

Stell hier Deine Frage zu: HTML, CSS, PHP, MySQL, htaccess, robots.txt, Javascript usw
supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 24.11.2020, 17:05 Sind hier PHP'ler unterwegs?

Bevor ich jetzt gleich zu tief und zu umfangreich einsteige, worum es geht, frag ich erstmal nach. Sind hier PHP'ler unterwegs?
Zuletzt geändert von supervisior am 24.11.2020, 19:35, insgesamt 1-mal geändert.

Anzeige von:


Content Erstellung von ABAKUS Internet Marketing
Ihre Vorteile:
  • einzigartige Texte
  • suchmaschinenoptimierte Inhalte
  • eine sinnvolle Content-Strategie
  • Beratung und Umsetzung
Jetzt anfragen: 0511 / 300325-0

Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 24.11.2020, 18:49 Sind hier PHP'ler unterwegs?

Mindestens zwei! ;)

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 24.11.2020, 19:24 Sind hier PHP'ler unterwegs?

Sehr schön. Fit mit PHP-curl?

Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 24.11.2020, 19:35 Sind hier PHP'ler unterwegs?

Stell einfach deine Frage ;)

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 24.11.2020, 19:43 Sind hier PHP'ler unterwegs?

Muss ich doch vorher wissen....

Muss vorher vorausschicken, dass ich mit meinem Anliegen zunächst bei stackoverflow war und fast schien es als hätten wir eine Lösung gefunden, aber es wurde zu viel kommentiert, was den Aufpassern bei stackoverflow nicht gepasst hat. Also Sackgasse!

Schau Dir den nachfolgenden Code erstmal an. Der funktioniert und macht was er soll. Wenn so weit klar, dann sag ich Dir wo es hängt.

Code: Alles auswählen

        function multi_thread_curl($urlArray, $optionArray, $nThreads) {
            global $lc_delay_value, $lc_delay_cleaned;
            $curlArray = array_chunk($urlArray, $nThreads, $preserve_keys = true);


            foreach ($curlArray as $threads) {

                foreach ($threads as $key => $value) {
                    ${'ch' . $key} = curl_init();
                    curl_setopt_array(${'ch' . $key}, $optionArray);
                    curl_setopt(${'ch' . $key}, CURLOPT_URL, $value); 
                }

                $mh = curl_multi_init();

                foreach ($threads as $key => $value) {
                    curl_multi_add_handle($mh, ${'ch' . $key});
                }

                $active = null;
                do {
                    $mrc = curl_multi_exec($mh, $active);
                    usleep($lc_delay_value);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);

                while ($active && $mrc == CURLM_OK) {
                    if (curl_multi_select($mh) != -1) {
                        do {
                            $mrc = curl_multi_exec($mh, $active);
                        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
                    }
                }


                foreach ($threads as $key => $value) {
                    $results[$key] = curl_multi_getcontent(${'ch' . $key});
                    curl_multi_remove_handle($mh, ${'ch' . $key});
                }


                curl_multi_close($mh);
            }
            return $results;
        }

        $optionArray = array(
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_HEADER => true,
            CURLOPT_CUSTOMREQUEST => 'GET',
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_ENCODING => 'gzip',
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_NOBODY => false,
            CURLOPT_USERAGENT => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.47 Safari/537.36",
            CURLOPT_HTTPHEADER => array('Cache-Control: max-age=0,no-store,no-cache'),
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
        );

        $nThreads = $lc_threads_value;

        $crawler_query = "SELECT url FROM lc_url WHERE blacklisted != 1";
        $crawler_query_result = $mysqli->query($crawler_query);

        if ($crawler_query_result->num_rows > 0) {
            while ($crawlUrls = $crawler_query_result->fetch_array()) {
                $crawlerurl = $crawlUrls['url'];


                $urlArray = array($crawlerurl);
                $results = multi_thread_curl($urlArray, $optionArray, $nThreads);

            }
        }

Anzeige von:

SEO Consulting bei ABAKUS Internet Marketing
Erfahrung seit 2002
  • persönliche Betreuung
  • individuelle Beratung
  • kompetente Umsetzung

Jetzt anfragen: 0511 / 300325-0.


Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 24.11.2020, 20:45 Sind hier PHP'ler unterwegs?

So weit klar. Mich wundert allerdings die Art und Weise, wie du die "multi_thread_curl"-Funktion aufrufst. Wenn ich das richtig sehe, übergibst du ihr immer nur jeweils ein Array mit einer einzigen URL, und das wäre ja wohl nicht Zweck der Sache, wenn es hier um Multi-Threading geht.

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 24.11.2020, 21:02 Sind hier PHP'ler unterwegs?

Ne, da liegst warum auch immer falsch. Das Array spuckt mehrere 10.000 URLs aus. Ich weiß aber, was Du meinst, hab das aber erstmal hinten angestellt, weil es so schon länger funktioniert. Ich bin kein Full Stack Programmierer. Von daher war ich froh eine Multi-Threading Funktion hinbekommen zu haben, die sich dann auch noch einstellen lässt.

Aber nun zum eigentlichen bei dem ich alle 5 Minuten einen Synapsen Kollaps bekomme, weil ich nicht weiterkomme. Das optionArray hat eine Handvoll statischer Options, was so weit ok ist, allerdings haben meine URLs jetzt nicht nur diese fixen Optionen, sondern 5, bzw. 6 je URL. Jede URL muss also 5/6x aufgerufen werden, aber jedes Mal mit anderen Optionen, wobei sich immer nur der UA ändert und der/die Cookies.

Ich müsste im Grunde genommen die multi_thread_curl Funktion mehrmals aufrufen können, jedoch jedes Mal mit anderen Parametern. So zumindest meine inzwischen verzweifelte Denke....

Code: Alles auswählen

$results = multi_thread_curl($urlArray, $optionArray, $nThreads);
$results = multi_thread_curl($urlArray, $optionArray2, $nThreads);
$results = multi_thread_curl($urlArray, $optionArray3, $nThreads);
.....
$results = multi_thread_curl($urlArray, $optionArray55, $nThreads);
Bin für jede Gedankenstütze dankbar!

Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 24.11.2020, 21:16 Sind hier PHP'ler unterwegs?

Zunächst nochmal zu deinem Code: fetch_array gibt dir immer nur eine einzige Zeile deiner Tabelle zurück, und zwar in Form eines assoziativen Arrays (darum der Name - man könnte denken, dass man mehrere Zeilen kriegt, dem ist aber nicht so). Dann holst du dir den "url"-Wert dieser Zeile und packst den in ein Array. Das Array hat damit nur ein einziges Element. Und das Array übergibst du an deine "multi_thread_curl"-Funktion, die dann ihre Arbeit tut. Da sie aber nur eine einzige URL übergeben bekommt, kann sie mit Multi-Threading keinen Vorteil rausholen. Natürlich funktioniert es trotzdem, aber es ist halt nicht so schnell wie es sein könnte.

Bitte füge mal folgende Zeile am Anfang deiner "multi_thread_curl"-Funktion ein:

Code: Alles auswählen

echo("Processing " . count($urlArray) . " URLs in parallel\n");
Ich würde Geld darauf wetten, dass da immer nur jeweils 1 URL ankommt, und das dann zehntausende Male.

Korrekterweise würdest du die Funktion wie folgt aufrufen:

Code: Alles auswählen

$crawl_urls = array();
while ($row = $crawler_query_result->fetch_array())
{
	$crawl_urls[] = $row["url"];
}

$results = multi_thread_curl($crawl_urls, $optionArray, $nThreads);
Also erst alle URLs holen und in ein Array packen, und dieses Array dann an die Funktion übergeben. Nur so kann sie die URLs parallel verarbeiten, und nur so hast du auch etwas vom Multi-Threading.

Bezüglich deines Problems: Du könntest die Funktion so erweitern, dass sie für jede URL ein eigenes Options-Array erwartet. Also ein Array von Arrays. Das müsstest du genau so in Chunks aufsplitten wie es schon mit den URLs gemacht wird. Dann könntest du am Ende alles in einem Rutsch abhandeln. Du würdest dieselbe URL dann mehrfach ins Array packen, aber halt mit verschiedenen Optionen. Reicht dir das als Gedankenstütze? Ich könnte dir auch Code dafür schreiben, aber vielleicht möchtest du es ja erstmal selbst probieren.

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 24.11.2020, 22:45 Sind hier PHP'ler unterwegs?

Du hättest Dich weniger damit beschäftigen sollen, dass ich die Funktion falsch nutzen würde, was ich aber nicht tue. Andernfalls wäre ich die letzten 3 Jahre auf beiden Augen blind gewesen. Will heißen, ich bekomme genau die URLs aus dem Array, die ich haben will. Ich kann das nicht nur optisch validieren, sondern auch mit einem Logfile, der zu zu jedem Request einen Timestamp generiert. Es funktioniert also. Können wir das mal bitte ausklammern? Das blockiert grad.

Wenn Du Lust und Zeit hast und 3,50 verdienen willst, kannst mir die Arbeit, aber vor allem die Zeit abnehmen, weil ich vermutlich 3x länger dafür brauche.

Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 24.11.2020, 23:02 Sind hier PHP'ler unterwegs?

Du, mein lieber supervisior, ich programmiere leidenschaftlich seit ich 12 bin (mittlerweile 25 Jahre ...) in C++, C#, Python, Java, JavaScript und auch PHP. Sogar mit Assembler und Grafikkarten-Shadern habe ich rumgemacht. Ich bin ein "Vollblutprogrammierer" und kann von mir behaupten, dass ich weiß, wovon ich rede.

Ich kann dir zu 100% garantieren, dass dein Code, wie du ihn oben gepostet hast, kein Multi-Threading nutzt, und habe detailliert erklärt, warum das so ist. Wohlgemerkt: Das heißt nicht, dass der Code "falsch" ist, sondern nur, dass es keinen Geschwindigkeitsvorteil gibt, also der ganze Multi-Threading-Zirkus für die Katz ist. Übrigens, je nach dem, wie du loggst, kannst du das anhand der Logs gar nicht erkennen.

Wenn ich du wäre und auch nur der Hauch einer Chance bestünde, dass ich meinen Code um einen Faktor 100+ schneller machen könnte, dann würde ich das auf jeden Fall wissen wollen und mich nicht aus Prinzip verschließen. Besser 3 Jahre blind sein und dann die Augen öffnen als für immer blind zu bleiben ... ;)

Ich schlage dir folgenden "Deal" vor: Ich erweitere die Funktion für dich und zeige dir, wie du sie clever nutzt, wenn du die von mir gepostete echo-Zeile an den Anfang der Funktion einbaust und mir mitteilst, was sie ausgibt. Das ist eine Sache von höchstens einer Minute und wird eindeutig zeigen, was Sache ist. Du kannst dabei nur gewinnen: Auf jeden Fall kriegst du 1A-Qualitäts-Code mit Gütesiegel von mir für lau. Und entweder liege ich falsch, dann kannst du frohlocken und dich darüber amüsieren, dass ich mich blamiert habe. Oder aber ich liege richtig, dann kannst du einen ordentlichen Geschwindigkeits-Boost rausholen, indem du den Code verbesserst. Du bist doch so ein Geschwindigkeits-Freak. ;) Nimm einfach mal einen Rat an und gib mir eine Chance dir zu zeigen, dass dein Code so viel schneller sein könnte.

nerd
PostRank 10
PostRank 10
Beiträge: 4289
Registriert: 15.02.2005, 04:02

Beitrag nerd » 25.11.2020, 01:46 Sind hier PHP'ler unterwegs?

Hanzo hat recht; jede PHP instanz laeuft immer nur auf einem einzigen core. Von "Multi-Threading Funktion" kann hier keine rede sein, da du ja auch nirgends in deinem script einen neuen PHP call ausloest. Dein script wird ganz normal von oben nach unten "in sich selbst" abgearbeitet.

Wenn du deine multi_thread_curl() function rekursiv aufrufst dann wird die funktion darueber trotzdem warten bis dein multi_thread_curl() komplett durch ist bevor es weitermacht. Du hast in PHP auch gar keine moeglichkeiten irgendwelche functioncalls asyncron (d.h. weitermachen, obwohl der vorherige call noch nicht beendet ist!) abarbeiten zu lassen.

Was du machen kannst ist queues oder php-workers einzusetzten, welche von manchen frameworks unterstuetzt wird. Ich hab es mir nicht weiter angesehen, aber die idee ist wohl dass man dort auch mehrere worker parallel anstossen kann die dann abgearbeitet werden; allerdings muss jeder worker ein in sich abgeschlossenes programm sein was sich nicht mit anderen workern verstaendigen kann.

https://devcenter.heroku.com/articles/php-workers
https://laravel.com/docs/5.4/queues

"Queues allow you to defer the processing of a time consuming task, such as sending an email, until a later time. Deferring these time consuming tasks drastically speeds up web requests to your application."
Zuletzt geändert von nerd am 25.11.2020, 02:02, insgesamt 1-mal geändert.

Hanzo2012
Community-Manager
Community-Manager
Beiträge: 2128
Registriert: 26.09.2011, 23:31

Beitrag Hanzo2012 » 25.11.2020, 01:52 Sind hier PHP'ler unterwegs?

@nerd:

Das geht am Punkt vorbei. Was du wahrscheinlich nicht weißt, ist, dass es in PHP die Möglichkeit gibt mehrere cURL-Requests parallel abzuarbeiten (siehe hier). Genau das tut die Funktion "multi_thread_curl", die hier gezeigt wurde (die wird nicht rekursiv aufgerufen!). Man braucht dafür nicht mehrere PHP-Prozesse. Die Funktion an sich scheint in Ordnung zu sein und sollte funktionieren.

Das Problem ist ein anderes, zumindest in dem oben gezeigten Code: Er füttert die Funktion häppchenweise mit einzelnen URLs statt auf einen Rutsch mit allen URLs. So kann die Funktion immer nur eine URL gleichzeitig laden und nicht vom Multi-Threading profitieren.

Im Prinzip macht der Code das:

LadeURLsParallel(array("URL1")); // lade erst URL1
LadeURLsParallel(array("URL2")); // dann URL2
LadeURLsParallel(array("URL3")); // dann URL3
...


Er sollte aber das machen:

LadeURLsParallel(array("URL1", "URL2", "URL3", ...)); // lade URL1, URL2, URL3, ... parallel

nerd
PostRank 10
PostRank 10
Beiträge: 4289
Registriert: 15.02.2005, 04:02

Beitrag nerd » 25.11.2020, 02:08 Sind hier PHP'ler unterwegs?

Hanzo2012 hat geschrieben:
25.11.2020, 01:52
@nerd: Das geht am Punkt vorbei.
Ich habs nochmal bearbeitet.
Ja so tief steck ich in curl nicht drinnen; ich mache meine api calls ueber guzzle, was mir die ganze beinarbeit abnimmt...
Und dein beispiel hier bezieht ja ausschlieslich auf curl; andere php funktionen haben nicht zwingend eine multi_thread_...() funktion implementiert.

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 25.11.2020, 07:56 Sind hier PHP'ler unterwegs?

@Hanzo

Also, ich hab mich jetzt weichklopfen lassen, obwohl wir grad das Ziel aus dem Auge verlieren.....

Ich hab gemacht, was Du gesagt hast. Es ist in der Tat so, wie Du es angekündigt hattest. (Hättest mal tatsächlich wetten sollen. ;)) Ohne die Änderung und nur die echo Ausgabe in der Funktion spuckt zwar alle URLs aus, aber eben schön 1 nach der Anderen und nicht parallel. Mit der Änderung:

Code: Alles auswählen

Processing 57396 URLs in parallel 
Zu meiner Verteidigung, ich wusste vorher schon, dass ich diesbezüglich nachbessern muss oder sagen wir mal ich hatte ein Bauchgefühl, dass das nicht 100%ig Okay sein kann. Hab das aber erstmal hinten angestellt, weil mich das eigentliche Problem mehr beschäftigt hat. Aber auch, wenn mit Deiner Änderung es jetzt tatsächlich ein Multi-Threading gibt, ergibt sich "gefühlt" kein sonderlicher Zeitunterschied. Es dauert gefühlt fast genauso lang.

An dieser Stelle schon mal ein Danke! Können wir uns jetzt dem eigentlichen Problem widmen?

supervisior
PostRank 9
PostRank 9
Beiträge: 2718
Registriert: 26.06.2006, 09:11

Beitrag supervisior » 25.11.2020, 08:16 Sind hier PHP'ler unterwegs?

Nur mal so rein informativ, mit der besagten Methodik, die auf meinem eigenen Server zum Einsatz, also ein Selbstaufruf stattfindet, lassen sich 100.000 URLs in weniger als 45 Min. crawlen. Serverlast: Nicht nennenswert, unter 0.3. Allerdings nur weil mein Webserver eine unbegrenzte Anzahl an gleichzeitigen Requests ermöglicht.

Es ginge aber noch mehr in noch kürzerer Zeit, wenn ich die Zahl der gleichzeitigen Threads erhöhen würde und aktuell grad mal 3 parallel Threads fahre.

Antworten
  • Vergleichbare Themen
    Antworten
    Zugriffe
    Letzter Beitrag