Wie man HTTP-Anfragen in PHP stellt und nicht auf die Antwort wartet

Lesezeit: 12 Minuten

Brents Benutzeravatar
Brent

Gibt es in PHP eine Möglichkeit, HTTP-Aufrufe zu tätigen und nicht auf eine Antwort zu warten? Die Antwort ist mir egal, ich möchte nur so etwas tun file_get_contents(), aber warten Sie nicht, bis die Anfrage abgeschlossen ist, bevor Sie den Rest meines Codes ausführen. Dies wäre sehr nützlich, um in meiner Anwendung eine Art “Ereignisse” auszulösen oder lange Prozesse auszulösen.

Irgendwelche Ideen?

  • eine Funktion – ‘curl_multi’, suchen Sie in den PHP-Dokumenten danach. Sollte deine Probleme lösen

    – James Butler

    17. März 2011 um 0:09 Uhr

  • Der Titel dieses Beitrags ist irreführend. Ich bin auf der Suche nach wirklich asynchrone Aufrufe ähnlich Anfragen in Node.js oder einer AJAX-Anfrage. Die akzeptierte Antwort ist nicht asynchron (sie blockiert und stellt keinen Rückruf bereit), nur eine schnellere synchrone Anfrage. Erwägen Sie, die Frage oder die akzeptierte Antwort zu ändern.

    – Johntron

    29. Juli 2013 um 17:44 Uhr

  • Das Spielen mit der Verbindungsbehandlung über Header und Puffer ist nicht kugelsicher. Ich habe gerade eine neue Antwort unabhängig von Betriebssystem, Browser oder PHP-Version gepostet

    – RafaSashi

    21. Februar 2015 um 13:39 Uhr

  • Asynchron bedeutet nicht, dass Ihnen die Antwort egal ist. Es bedeutet nur, dass der Aufruf die Ausführung des Hauptthreads nicht blockiert. Asynchron erfordert immer noch eine Antwort, aber die Antwort kann in einem anderen Ausführungsthread oder später in einer Ereignisschleife verarbeitet werden. Diese Frage fragt nach einer Fire-and-Forget-Anforderung, die je nach Semantik der Nachrichtenübermittlung synchron oder asynchron sein kann, unabhängig davon, ob Sie sich für die Nachrichtenreihenfolge oder die Übermittlungsbestätigung interessieren.

    – CMCDragonkai

    5. März 2015 um 4:27 Uhr

  • Ich denke, Sie sollten diese HTTP-Anfrage im nicht blockierenden Modus stellen (w/c ist das, was Sie wirklich wollen). Denn wenn Sie eine Ressource aufrufen, möchten Sie im Grunde wissen, ob Sie den Server erreicht haben oder nicht (oder aus welchem ​​​​Grund auch immer, Sie brauchen nur die Antwort). Die beste Antwort ist wirklich fsockopen und das Einstellen des Stream-Lesens oder -Schreibens in den nicht blockierenden Modus. Es ist wie anrufen und vergessen.

    – KiX Ortillan

    27. November 2015 um 6:48 Uhr

Die Antwort, die ich zuvor akzeptiert hatte, funktionierte nicht. Es wartete noch auf Antworten. Dies funktioniert jedoch, entnommen aus Wie mache ich eine asynchrone GET-Anfrage in PHP?

function post_without_wait($url, $params)
{
    foreach ($params as $key => &$val) {
      if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);
    }
    $post_string = implode('&', $post_params);

    $parts=parse_url($url);

    $fp = fsockopen($parts['host'],
        isset($parts['port'])?$parts['port']:80,
        $errno, $errstr, 30);

    $out = "POST ".$parts['path']." HTTP/1.1\r\n";
    $out.= "Host: ".$parts['host']."\r\n";
    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
    $out.= "Content-Length: ".strlen($post_string)."\r\n";
    $out.= "Connection: Close\r\n\r\n";
    if (isset($post_string)) $out.= $post_string;

    fwrite($fp, $out);
    fclose($fp);
}

  • Dies ist NICHT asynchron! Insbesondere wenn der Server auf der anderen Seite ausgefallen ist, bleibt dieser Codeabschnitt für 30 Sekunden hängen (der 5. Parameter in fsockopen). Auch die Ausführung von fwrite wird seine süße Zeit in Anspruch nehmen (die Sie mit stream_set_timeout($fp, $my_timeout) begrenzen können). Das Beste, was Sie tun können, ist, ein niedriges Timeout für fsockopen auf 0,1 (100 ms) und $my_timeout auf 100 ms zu setzen Sie riskieren jedoch, dass die Anforderung eine Zeitüberschreitung aufweist.

    – Chris Cinelli

    24. Oktober 2012 um 23:22 Uhr

  • Ich versichere Ihnen, dass es asynchron ist und keine 30 Sekunden dauert. Das ist ein Timeout max. Es ist möglich, dass Ihre Einstellungen unterschiedlich sind, was diesen Effekt verursacht, aber das hat bei mir hervorragend funktioniert.

    – Brent

    28. November 2012 um 21:37 Uhr

  • @UltimateBrent Der Code enthält nichts, was darauf hindeutet, dass er asynchron ist. Es wartet nicht auf eine Antwort, aber das ist nicht asynchron. Wenn der Remoteserver die Verbindung öffnet und dann hängt, würde dieser Code 30 Sekunden warten, bis Sie dieses Timeout erreichen.

    – chmac

    6. März 2013 um 10:04 Uhr

  • Der Grund dafür, dass es “asynchron” zu funktionieren scheint, weil Sie nicht aus dem Socket lesen, bevor Sie ihn schließen, sodass er nicht hängen blieb, selbst wenn der Server nicht rechtzeitig eine Antwort ausgab. Dies ist jedoch absolut nicht asynchron. Wenn der Schreibpuffer voll ist (sehr unwahrscheinlich), wird Ihr Skript definitiv dort hängen bleiben. Sie sollten in Betracht ziehen, Ihren Titel in etwas wie “Anfordern einer Webseite, ohne auf Antwort zu warten” zu ändern.

    – howanghk

    26. März 2013 um 4:42 Uhr

  • Dies ist weder asynchron noch verwendet es curl, wie Sie es wagen, es zu nennen curl_post_async und sogar Upvotes bekommen…

    – Daniel W.

    31. Oktober 2013 um 11:19 Uhr


Wenn Sie das Ziel, das Sie aufrufen möchten, asynchron steuern (z. B. Ihre eigene “longtask.php”), können Sie die Verbindung von dort aus beenden, und beide Skripte werden parallel ausgeführt. Es funktioniert so:

  1. quick.php öffnet longtask.php über cURL (keine Magie hier)
  2. longtask.php schließt die Verbindung und fährt fort (magisch!)
  3. cURL kehrt zu quick.php zurück, wenn die Verbindung geschlossen wird
  4. Beide Aufgaben laufen parallel weiter

Ich habe das ausprobiert, und es funktioniert gut. Aber quick.php wird nichts darüber wissen, wie es longtask.php geht, es sei denn, Sie schaffen ein Kommunikationsmittel zwischen den Prozessen.

Probieren Sie diesen Code in longtask.php aus, bevor Sie irgendetwas anderes tun. Es wird die Verbindung schließen, aber weiterhin laufen (und jegliche Ausgabe unterdrücken):

while(ob_get_level()) ob_end_clean();
header('Connection: close');
ignore_user_abort();
ob_start();
echo('Connection Closed');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();

Der Code wird aus der kopiert Von Benutzern beigesteuerte Anmerkungen des PHP-Handbuchs und etwas verbessert.

  • Das würde funktionieren. Wenn Sie jedoch ein MVC-Framework verwenden, kann die Implementierung schwierig sein, da dieses Framework Aufrufe abfängt und umschreibt. Zum Beispiel funktioniert es nicht in einem Controller in CakePHP

    – Chris Cinelli

    24. Oktober 2012 um 23:32 Uhr


  • Ein Zweifel an diesem Code, der Prozess, den Sie in Longtask ausführen müssen, muss nach diesen Zeilen gehen? Danke.

    – morger

    26. September 2016 um 17:11 Uhr

  • Es funktioniert nicht perfekt. Versuchen Sie hinzuzufügen while(true); nach deinem Code. Die Seite bleibt hängen, das heißt, sie läuft noch im Vordergrund.

    – Sos.

    10. Mai 2020 um 3:25 Uhr


  • Wie öffne ich es über cURL? Wie erstelle ich “einige Kommunikationsmittel zwischen den Prozessen”?

    – RedGuy11

    22. Januar 2021 um 1:04 Uhr

Sie können Tricks anwenden, indem Sie exec() verwenden, um etwas aufzurufen, das HTTP-Anforderungen ausführen kann, wie z wgetaber Sie müssen alle Ausgaben des Programms irgendwohin leiten, wie eine Datei oder /dev/null, sonst wartet der PHP-Prozess auf diese Ausgabe.

Wenn Sie den Prozess vollständig vom Apache-Thread trennen möchten, versuchen Sie Folgendes (ich bin mir nicht sicher, aber ich hoffe, Sie verstehen die Idee):

exec('bash -c "wget -O (url goes here) > /dev/null 2>&1 &"');

Es ist keine schöne Angelegenheit, und Sie werden wahrscheinlich so etwas wie einen Cron-Job brauchen, der ein Heartbeat-Skript aufruft, das eine tatsächliche Datenbank-Ereigniswarteschlange abfragt, um echte asynchrone Ereignisse auszuführen.

  • In ähnlicher Weise habe ich auch Folgendes getan: exec(“curl $url > /dev/null &”);

    – Matt Huggins

    21. September 2009 um 18:50 Uhr

  • Frage: Gibt es einen Vorteil, ‘bash -c “wget”‘ statt nur ‘wget’ aufzurufen?

    – Matt Huggins

    9. November 2009 um 21:18 Uhr

  • In meinen Tests mit exec("curl $url > /dev/null 2>&1 &"); ist hier eine der schnellsten Lösungen. Es ist immens schneller (1,9 s für 100 Iterationen) als die post_without_wait() Funktion (14,8 s) in der obigen “akzeptierten” Antwort. UND es ist ein Einzeiler …

    – Rinogo

    28. Januar 2016 um 20:29 Uhr

  • Verwenden Sie den vollständigen Pfad (z. B. /usr/bin/curl), um es noch schneller zu machen

    – Putnik

    25. Oktober 2017 um 21:42 Uhr

  • wartet dies, bis das Skript fertig ist?

    – Cikatomo

    12. April 2020 um 15:35 Uhr

Sie können diese Bibliothek verwenden: https://github.com/stil/curl-easy

Dann ist es ziemlich einfach:

<?php
$request = new cURL\Request('http://yahoo.com/');
$request->getOptions()->set(CURLOPT_RETURNTRANSFER, true);

// Specify function to be called when your request is complete
$request->addListener('complete', function (cURL\Event $event) {
    $response = $event->response;
    $httpCode = $response->getInfo(CURLINFO_HTTP_CODE);
    $html = $response->getContent();
    echo "\nDone.\n";
});

// Loop below will run as long as request is processed
$timeStart = microtime(true);
while ($request->socketPerform()) {
    printf("Running time: %dms    \r", (microtime(true) - $timeStart)*1000);
    // Here you can do anything else, while your request is in progress
}

Unten sehen Sie die Konsolenausgabe des obigen Beispiels. Es wird eine einfache Live-Uhr angezeigt, die angibt, wie viel Zeit die Anfrage läuft:


Animation

Ab 2018, Fressen ist zur De-facto-Standardbibliothek für HTTP-Anforderungen geworden, die in mehreren modernen Frameworks verwendet wird. Es ist in reinem PHP geschrieben und erfordert keine Installation von benutzerdefinierten Erweiterungen.

Es kann sehr gut und sogar asynchrone HTTP-Aufrufe ausführen bündeln sie B. wenn Sie 100 HTTP-Aufrufe tätigen müssen, aber nicht mehr als 5 gleichzeitig ausführen möchten.

Beispiel für gleichzeitige Anfragen

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client(['base_uri' => 'http://httpbin.org/']);

// Initiate each request but do not block
$promises = [
    'image' => $client->getAsync('/image'),
    'png'   => $client->getAsync('/image/png'),
    'jpeg'  => $client->getAsync('/image/jpeg'),
    'webp'  => $client->getAsync('/image/webp')
];

// Wait on all of the requests to complete. Throws a ConnectException
// if any of the requests fail
$results = Promise\unwrap($promises);

// Wait for the requests to complete, even if some of them fail
$results = Promise\settle($promises)->wait();

// You can access each result using the key provided to the unwrap
// function.
echo $results['image']['value']->getHeader('Content-Length')[0]
echo $results['png']['value']->getHeader('Content-Length')[0]

Sehen http://docs.guzzlephp.org/en/stable/quickstart.html#concurrent-requests

  • Diese Antwort ist jedoch nicht asynchron. anscheinend macht guzzle das nicht

    – köstlich

    21. November 2018 um 1:16 Uhr


  • Guzzle erfordert, dass Sie curl installieren. Andernfalls ist es nicht parallel und gibt Ihnen keine Warnung, dass es nicht parallel ist.

    – Welizar Hristov

    23. November 2018 um 16:54 Uhr

  • Danke für den Link @daslicious – ja, es scheint, dass er nicht vollständig asynchron ist (wie wenn Sie eine Anfrage senden möchten, sich aber nicht um das Ergebnis kümmern), aber ein paar Posts weiter unten in diesem Thread hat ein Benutzer eine Problemumgehung angeboten Einstellen eines sehr niedrigen Request-Timeout-Werts, der die Verbindungszeit noch zulässt, aber nicht auf das Ergebnis wartet.

    – Simon Osten

    24. November 2018 um 6:27 Uhr

  • composer require guzzle/guzzle gibt mir fügt meinem Projekt 537 Dateien und 2,5 Millionen Bytes neuen Code hinzu! Für einen HTTP-Client! Nein danke.

    – EricP

    13. Juni 2021 um 20:56 Uhr

  • Wir brauchen mehr Leute wie @EricP in unseren Projekten.

    – matt

    17. März 2022 um 13:35 Uhr

Benutzeravatar von philfreo
philfreo

/**
 * Asynchronously execute/include a PHP file. Does not record the output of the file anywhere. 
 *
 * @param string $filename              file to execute, relative to calling script
 * @param string $options               (optional) arguments to pass to file via the command line
 */ 
function asyncInclude($filename, $options="") {
    exec("/path/to/php -f {$filename} {$options} >> /dev/null &");
}

  • Diese Antwort ist jedoch nicht asynchron. anscheinend macht guzzle das nicht

    – köstlich

    21. November 2018 um 1:16 Uhr


  • Guzzle erfordert, dass Sie curl installieren. Andernfalls ist es nicht parallel und gibt Ihnen keine Warnung, dass es nicht parallel ist.

    – Welizar Hristov

    23. November 2018 um 16:54 Uhr

  • Danke für den Link @daslicious – ja, es scheint, dass er nicht vollständig asynchron ist (wie wenn Sie eine Anfrage senden möchten, sich aber nicht um das Ergebnis kümmern), aber ein paar Posts weiter unten in diesem Thread hat ein Benutzer eine Problemumgehung angeboten Einstellen eines sehr niedrigen Request-Timeout-Werts, der die Verbindungszeit noch zulässt, aber nicht auf das Ergebnis wartet.

    – Simon Osten

    24. November 2018 um 6:27 Uhr

  • composer require guzzle/guzzle gibt mir fügt meinem Projekt 537 Dateien und 2,5 Millionen Bytes neuen Code hinzu! Für einen HTTP-Client! Nein danke.

    – EricP

    13. Juni 2021 um 20:56 Uhr

  • Wir brauchen mehr Leute wie @EricP in unseren Projekten.

    – matt

    17. März 2022 um 13:35 Uhr

Benutzeravatar der Community
Gemeinschaft

  1. Täuschen Sie eine Anfrage zur Abtreibung vor CURL ein Tief setzen CURLOPT_TIMEOUT_MS

  2. Satz ignore_user_abort(true) um die Verarbeitung fortzusetzen, nachdem die Verbindung geschlossen wurde.

Mit dieser Methode ist es nicht erforderlich, die Verbindungsbehandlung über Header und Puffer zu implementieren, die zu sehr von Betriebssystem, Browser und PHP-Version abhängig sind

Master-Prozess

function async_curl($background_process=""){

    //-------------get curl contents----------------

    $ch = curl_init($background_process);
    curl_setopt_array($ch, array(
        CURLOPT_HEADER => 0,
        CURLOPT_RETURNTRANSFER =>true,
        CURLOPT_NOSIGNAL => 1, //to timeout immediately if the value is < 1000 ms
        CURLOPT_TIMEOUT_MS => 50, //The maximum number of mseconds to allow cURL functions to execute
        CURLOPT_VERBOSE => 1,
        CURLOPT_HEADER => 1
    ));
    $out = curl_exec($ch);

    //-------------parse curl contents----------------

    //$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    //$header = substr($out, 0, $header_size);
    //$body = substr($out, $header_size);

    curl_close($ch);

    return true;
}

async_curl('http://example.com/background_process_1.php');

Hintergrundprozess

ignore_user_abort(true);

//do something...

NB

Wenn Sie möchten, dass cURL in weniger als einer Sekunde abläuft, können Sie CURLOPT_TIMEOUT_MS verwenden, obwohl es einen Fehler/eine „Funktion“ auf „Unix-ähnlichen Systemen“ gibt, der bewirkt, dass libcurl sofort abbricht, wenn der Wert < 1000 ms mit dem Fehler „ cURL-Fehler (28): Timeout wurde erreicht". Die Erklärung für dieses Verhalten ist:

[…]

Die Lösung besteht darin, Signale mit CURLOPT_NOSIGNAL zu deaktivieren

Ressourcen

  • Wie gehen Sie mit Verbindungszeitüberschreitungen um (Auflösung, DNS)? Wenn ich timeout_ms auf 1 setze, bekomme ich immer “Resolving timed out after 4 ms” oder so ähnlich

    – Martin Wickmann

    3. April 2017 um 13:53 Uhr

  • Ich weiß nicht, aber 4 ms klingen für mich schon ziemlich schnell … Ich glaube nicht, dass Sie das Problem schneller lösen können, indem Sie die Curl-Einstellungen ändern. Versuchen Sie vielleicht, die gezielte Anfrage zu optimieren …

    – RafaSashi

    3. April 2017 um 14:36 ​​Uhr

  • Ok, aber timeout_ms=1 setzt den Timeout für die gesamte Anfrage. Wenn Ihre Auflösung also länger als 1 ms dauert, wird curl eine Zeitüberschreitung haben und die Anfrage stoppen. Ich sehe nicht, wie das überhaupt funktionieren kann (vorausgesetzt, die Auflösung dauert> 1 ms).

    – Martin Wickmann

    4. April 2017 um 8:03 Uhr

  • Obwohl es nicht viel Sinn macht, funktioniert dies einwandfrei und ist eine ziemlich gute Lösung, um PHP asynchron zu machen

    – Jared Scarito

    16. Juni 2021 um 21:12 Uhr


1445180cookie-checkWie man HTTP-Anfragen in PHP stellt und nicht auf die Antwort wartet

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy