Maison >développement back-end >tutoriel php >Explication détaillée du traitement en arrière-plan asynchrone PHP
Traitement en arrière-plan asynchrone PHP
PHP en tant que serveur d'interface en arrière-plan est déjà très courant et le traitement en arrière-plan asynchrone est souvent requis dans les scénarios d'application réels .
Recommandé : "Tutoriel vidéo PHP"
PHP a certes l'avantage d'être un serveur backend, mais il n'est pas facile de gérer certains clients quand. vous vous souciez des résultats, ses inconvénients sont révélés. Il n'y a pas de mécanisme d'exécution asynchrone.
Par exemple, lorsque nous voulons enregistrer les performances de l'accès d'un certain client à PHP (y compris l'heure de début, l'heure de fin, l'état du résultat, etc.), bien sûr, le client souhaite enregistrer les performances actuelles de PHP. le traitement peut revenir plus tôt et obtenir les résultats, mais si une solution conventionnelle est installée, le client doit attendre que PHP termine l'enregistrement des performances avant d'obtenir les résultats.
Cela équivaut à aller à la banque pour vérifier votre solde actuel, et le caissier se précipite et fait des histoires avec d'autres personnes pendant un moment, puis vient vous dire le même résultat.
Ainsi, souvent, un PHP capable d'effectuer des opérations asynchrones est nécessaire.
Comment PHP implémente-t-il le traitement asynchrone ?
L'une des solutions consiste à utiliser les appels système php pour démarrer un nouveau processus.
php fournit la fonction fsockopen. La fonction de cette fonction est d'initialiser une connexion socket à l'hôte spécifié. Par défaut, la connexion socket sera ouverte en mode blocage.
Bien sûr, vous pouvez le convertir en mode non bloquant via stream_set_blocking(). C'est la clé.
Donc, l'idée est la suivante : ouvrez un socket non bloquant pour vous connecter à la machine locale, et la machine locale effectuera un traitement fastidieux après l'avoir reçu.
Un code de traitement similaire à celui-ci (fichier posttest.php) :
$fp = fsockopen($php_Path,80); if (!$fp) { LMLog::error("fsockopen:err" ); } else { $out = "GET /album/action/album_write_friends_thread_record.php?key=&u= HTTP/1.1\r\n"; $out .= "Host: ".$php_Path."\r\n"; $out .= "Connection: Close\r\n\r\n"; stream_set_blocking($fp,true); stream_set_timeout($fp,1); fwrite($fp, $out); usleep(1000); fclose($fp); }
Ici, usleep(1000) est très critique, il peut garantir que cette requête puisse être envoyée.
Nous regardons la logique du code de traitement (fichier album_write_friends_thread_record.php) :
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2016-09-23 * Time: 09:26 */ /** * 客户端调用服务器接口页面 * user: guwen */ sleep(20);// 睡眠20s ?>
En fait, lorsque notre serveur exécute le programme fsockopen, il n'attendra pas 20 secondes avant de revenir à To. le client,
mais après avoir émis cette requête, il revient au client, détruit le processus et laisse le travail restant à d'autres processus pour le faire lentement, ce qui réalise l'asynchronie de PHP.
Quatre méthodes courantes d'exécution asynchrone PHP
Le client et le serveur communiquent via le protocole HTTP, le client initie une requête, le serveur effectue le traitement après avoir reçu la requête et renvoie le résultat du traitement.
Parfois, le serveur doit effectuer des opérations fastidieuses, telles que le traitement des téléchargements, l'envoi de messages, l'envoi d'e-mails, etc. Les résultats de cette opération n'ont pas besoin d'être renvoyés au client.
Mais comme php est exécuté de manière synchrone, le client doit attendre que le service soit traité avant de passer à l'étape suivante.
Par conséquent, les opérations fastidieuses conviennent à une exécution asynchrone. Une fois que le serveur a reçu la demande, il reviendra d'abord après avoir traité les données requises par le client, et les opérations fastidieuses restantes seront exécutées de manière asynchrone. l'arrière-plan du serveur.
Les méthodes courantes d'exécution asynchrone PHP sont les suivantes. Vous pouvez choisir en fonction de leurs avantages et inconvénients respectifs :
1.
La page client utilise la technologie AJAX pour requêter le serveur
$.get("doRequest.php", { name: "fdipzone"} ); <img src="doRequest.php?name=fdipzone">
Avantages : Le plus simple et le plus rapide est d'embarquer l'appel AJAX dans le code HTML renvoyé au client, ou , intégrez une balise img et src pointe vers le script fastidieux à exécuter.
Inconvénients : De manière générale, Ajax doit être déclenché après onLoad. C'est-à-dire que si l'utilisateur clique sur la page puis la ferme, notre script en arrière-plan ne sera pas déclenché.
Si vous utilisez la balise img, cette méthode ne peut pas être qualifiée d'exécution asynchrone au sens strict. Le navigateur de l'utilisateur attendra longtemps que l'exécution du script php soit terminée, c'est-à-dire que la barre d'état du navigateur de l'utilisateur indique toujours qu'il est toujours en cours de chargement.
Bien entendu, d'autres méthodes avec des principes similaires peuvent également être utilisées, comme les balises de script, etc.
2. Fonction popen()
Cette fonction ouvre un tube pointant vers le processus dérivé de Produced. par l'exécution d'une certaine commande.
Ouvre un canal vers le processus généré en forçant l'exécution de la commande donnée.
Vous pouvez donc l'appeler mais ignorer sa sortie. Le code d'utilisation est le suivant :
// popen — 打开进程文件指针 resource popen ( string $command , string $mode ) pclose(popen('php /home/fdipzone/doRequest.php &', 'r'));
Avantages : Les inconvénients de la première méthode sont évités et la vitesse d'exécution est rapide.
Inconvénients : Cette méthode ne peut pas demander un autre WebService via le protocole HTTP et ne peut exécuter que des fichiers de script locaux. Et il ne peut être ouvert que dans un seul sens et ne peut pas transmettre un grand nombre de paramètres au script appelé. Et si le nombre de visites est élevé, un grand nombre de processus seront générés. Si vous faites appel à des ressources externes, vous devez réfléchir vous-même à la concurrence.
1) Ne peut être exécuté que localement
2) Ne peut pas transmettre un grand nombre de paramètres
3) Lorsque le nombre de visites est élevé, de nombreux processus seront créés
🎜>
3. extension curl
CURL est un puissant outil de ligne de commande HTTP qui peut simuler des requêtes HTTP telles que POST/ GET, puis obtenez et extrayez les données, affichées sur la "sortie standard" (stdout).设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒
代码如下:
<?php $ch = curl_init(); $curl_opt = array( CURLOPT_URL, 'http://www.example.com/doRequest.php' CURLOPT_RETURNTRANSFER,1, CURLOPT_TIMEOUT,1 ); curl_setopt_array($ch, $curl_opt); curl_exec($ch); curl_close($ch); ?>
缺点:如你问题中描述的一样,由于使用CURL需要设置CUROPT_TIMEOUT为1(最小为1,郁闷)。也就是说,客户端至少必须等待1秒钟。
4. fscokopen()函数
fsockopen是最好的,缺点是需要自己拼接header部分。
<?php $url = 'http://www.example.com/doRequest.php'; $param = array( 'name'=>'fdipzone', 'gender'=>'male', 'age'=>30 ); doRequest($url, $param); function doRequest($url, $param=array()){ $urlinfo = parse_url($url); $host = $urlinfo['host']; $path = $urlinfo['path']; $query = isset($param)? http_build_query($param) : ''; $port = 80; $errno = 0; $errstr = ''; $timeout = 10; $fp = fsockopen($host, $port, $errno, $errstr, $timeout); $out = "POST ".$path." HTTP/1.1\r\n"; $out .= "host:".$host."\r\n"; $out .= "content-length:".strlen($query)."\r\n"; $out .= "content-type:application/x-www-form-urlencoded\r\n"; $out .= "connection:close\r\n\r\n"; $out .= $query; fputs($fp, $out); fclose($fp); } ?>
注意:当执行过程中,客户端连接断开或连接超时,都会有可能造成执行不完整,因此需要加上
ignore_user_abort(true); // 忽略客户端断开 set_time_limit(0); // 设置执行不超时
fsockopen支持socket编程,可以使用fsockopen实现邮件发送等socket程序等等,使用fcockopen需要自己手动拼接出header部分
可以参考: http://cn.php.net/fsockopen/
使用示例如下:
$fp = fsockopen("www.34ways.com", 80, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno)<br />\n"; } else { $out = "GET /index.php / HTTP/1.1\r\n"; $out .= "Host: www.34ways.com\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); /*忽略执行结果 while (!feof($fp)) { echo fgets($fp, 128); }*/ fclose($fp); }
所以总结来说,fscokopen()函数应该可以满足您的要求。可以尝试一下。
fscokopen的问题和popen 一样,并发非常多时会产生很多子进程,当达到apache的连接限制数时,就会挂掉,我问题已经说了这种情况。
PHP 本身没有多线程的东西,但可以曲线的办法来造就出同样的效果,比如多进程的方式来达到异步调用,只限于命令模式。还有一种更简单的方式,可用于 Web 程序中,那就是用fsockopen()、fputs() 来请求一个 URL 而无需等待返回,如果你在那个被请求的页面中做些事情就相当于异步了。
关键代码如下:
$fp=fsockopen('localhost',80,&$errno,&$errstr,5); if(!$fp){ echo "$errstr ($errno)<br />\n"; } fputs($fp,"GET another_page.php?flag=1\r\n"); fclose($fp);
上面的代码向页面 another_page.php 发送完请求就不管了,用不着等待请求页面的响应数据,利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。
比如,一个很切实的应用,某个 Blog 在每 Post 了一篇新日志后需要给所有它的订阅者发个邮件通知。如果按照通常的方式就是:
日志写完 -> 点提交按钮 -> 日志插入到数据库 -> 发送邮件通知 ->
告知撰写者发布成功
那么作者在点提交按钮到看到成功提示之间可能会等待很常时间,基本是在等邮件发送的过程,比如连接邮件服务异常、或器缓慢或是订阅者太多。而实际上是不管邮件发送成功与否,保证日志保存成功基本可接受的,所以等待邮件发送的过程是很不经济的,这个过程可异步来执行,并且邮件发送的结果不太关心或以日志形式记录备查。
改进后的流程就是:
日志写完 -> 点提交按钮 -> 日志插入到数据库 --->
告知撰写者发布成功
└ 发送邮件通知 -> [记下日志]
用个实际的程序来测试一下,有两个 php,分别是 write.php 和 sendmail.php,在 sendmail.php 用 sleep(seconds) 来模拟程序执行使用时间。
write.php,执行耗时 1 秒
<?php function asyn_sendmail() { $fp=fsockopen('localhost',80,&$errno,&$errstr,5); if(!$fp){ echo "$errstr ($errno)<br />\n"; } sleep(1); fputs($fp,"GET /sendmail.php?param=1\r\n"); #请求的资源 URL 一定要写对 fclose($fp); } echo time().'<br>'; echo 'call asyn_sendmail<br>'; asyn_sendmail(); echo time().'<br>'; ?>
sendmail.php,执行耗时 10 秒
<?php //sendmail(); //sleep 10 seconds sleep(10); fopen('C:\'.time(),'w'); ?>
通过页面访问 write.php,页面输出:
1272472697 call asyn_sendmail 1272472698
并且在 C:\ 生成文件:
1272472708
从上面的结果可知 sendmail.php 花费至少 10 秒,但不会阻塞到 write.php 的继续往下执行,表明这一过程是异步的。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!