Der Inhalt dieses Artikels befasst sich mit der Verwendung der Befehlszeile zur Implementierung der Aufgabenverarbeitung (Code) im asynchronen Multiprozessmodus. Ich hoffe, dass er hilfreich ist Dir zu helfen.
Die Verwendung von PHP zur Implementierung asynchroner Aufgaben war schon immer ein Problem. Zu den bekannten asynchronen Frameworks von PHP gehören Swoole und Workerman, die jedoch nicht direkt in der Webumgebung verwendet werden können erzwungen Um eine Webumgebung aufzubauen, werden auch asynchrone Aufrufe im Multiprozessmodus implementiert. Aber manchmal ist es wirklich nicht nötig, den Dienst zu starten und den Server auf Client-Nachrichten warten zu lassen, ganz zu schweigen davon, dass der Servercode zwischendurch nicht geändert werden kann. In diesem Artikel wird erläutert, wie Sie Multiprozess- und asynchrone Aufrufe in der Webumgebung in der CLI-Umgebung implementieren, ohne ein Framework oder eine Bibliothek eines Drittanbieters zu verwenden.
Asynchrone Aufrufe in der Webumgebung
Es gibt zwei häufig verwendete Methoden
Verwenden Sie eine Socket-Verbindung
Diese Methode ist eine typische C/S-Architektur und erfordert Serverunterstützung.
// 1. 创建socket套接字 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // 2. 进行socket连接 socket_connect($socket, '127.0.0.1', '3939'); //socket_set_nonblock($socket); // 以非阻塞模式运行,由于在客户端不实用,所以这里不考虑 // 3. 向服务端发送请求 socket_write($socket, $request, strlen($request)); // 4. 接受服务端的回应消息(忽略非阻塞的情况,如果服务端不是提供异步服务,那这一步可以省略) $recv = socket_read($socket, 2048); // 5. 关闭socket连接 socket_close($socket);
2. Verwenden Sie popen, um die Prozesspipeline zu öffnen
Diese Methode verwendet Betriebssystembefehle und wird direkt vom Betriebssystem ausgeführt.
Die in diesem Artikel besprochenen asynchronen Aufrufe verwenden diese Methode.
$sf = '/path/to/cli_async_task.php'; //要执行的脚本文件 $op = 'call'; //脚本文件接收的参数1 $data = base64_encode(serialize(['TestTask', 'arg1', 'arg2'])); //脚本文件接收的参数2 pclose(popen("php '$sf' --op $op --data $data &", 'r')); //打开之后接着就关闭进程管道,让该进程以守护模式运行 echo PHP_EOL.'异步任务已执行。'.PHP_EOL;
Der Vorteil dieser Methode besteht darin, dass sie in einem Schritt gelöst werden kann und der aktuelle Prozess keinen Overhead erfordert.
Der Nachteil liegt auch auf der Hand: Der Laufstatus des Aufgabenskripts kann nicht nachverfolgt werden.
Das Highlight wird also die Skriptdatei sein, die die Aufgabe ausführt. Im Folgenden wird die Implementierung der Aufgabenverarbeitung und der Mehrfachverarbeitung vorgestellt.
CLI
UmgebungHinweis: Der Multiprozessmodus unterstützt nur Linux, nicht Windows! !
Jeder Schritt wird hier beginnend bei 0 vorgestellt (ohne Verwendung eines Frameworks oder einer Klassenbibliothek), Ein vollständiger Code wird am Ende angehängt.
Ein Aspekt, der in keinem Skript ignoriert werden kann, ist die Fehlerbehandlung. Beim Schreiben eines Aufgabenverarbeitungsskripts besteht der erste Schritt darin, eine Fehlerbehandlungsmethode zu schreiben.
In PHP rufen Sie einfach die drei Funktionen set_Exception_handler set_error_handler register_shutdown_function auf und schreiben dann eine benutzerdefinierte Verarbeitungsmethode.
Der nächste Schritt besteht darin, die Autoloading-Funktion spl_autoload_register zu definieren, um die Mühe zu vermeiden, jedes Mal eine neue Klasse zu erfordern/inzuschließen.
Definieren Sie die Protokolloperationsmethode.
Definieren Sie die Aufgabenverarbeitungsmethode.
Lesen Sie die Parameter aus der Befehlszeile und beginnen Sie mit der Ausführung der Aufgabe.
PHP erstellt mehrere Prozesse mithilfe der Funktion pcntl_fork, die eine Kopie des aktuellen Prozesses aufspaltet (Schattenklontechnik). Es gibt zwei Prozesse: Der aktuelle Prozess ist der Hauptprozess (die Ontologie) und der gegabelte Prozess ist der untergeordnete Prozess (der Schattenklon). Es ist zu beachten, dass die Codeumgebung der beiden Prozesse dieselbe ist und beide Prozesse am Speicherort der Funktion pcntl_fork ausgeführt wurden. Der Unterschied besteht darin, dass die von getmypid erhaltene Prozessnummer unterschiedlich ist. Der wichtigste Unterschied besteht darin, dass beim Aufruf der Funktion pcntl_fork der vom untergeordneten Prozess erhaltene Rückgabewert 0 ist, während der Hauptprozess die Prozessnummer pid des untergeordneten Prozesses erhält .
Okay, nachdem wir wissen, wer der untergeordnete Prozess ist, können wir den untergeordneten Prozess Aufgaben ausführen lassen.
Woher kennt der Hauptprozess den Status des untergeordneten Prozesses?
Verwenden Sie pcntl_wait. Diese Funktion verfügt über zwei Parameter: $status und $options. $status ist ein Referenztyp, der zum Speichern des Status des untergeordneten Prozesses verwendet wird. $options verfügt über zwei optionale Konstanten, WNOHANG|WUNTRACED, was bedeutet, dass sofort zurückgegeben wird, ohne auf das Ende des untergeordneten Prozesses zu warten bzw. Warten auf den untergeordneten Prozess. Offensichtlich blockiert die Verwendung von WUNTRACED den Hauptprozess. (Sie können auch die Funktion pcntl_waitpid verwenden, um den spezifischen PID-Status des untergeordneten Prozesses abzurufen.)
In mehreren Prozessen muss der Hauptprozess den Status jedes untergeordneten Prozesses verwalten, andernfalls ist der untergeordnete Prozess wahrscheinlich nicht in der Lage zu sein, den Prozess zu beenden und zu einem Zombie zu werden.
Über die Nachrichtenkommunikation zwischen mehreren Prozessen
Dieser Bereich muss eine bestimmte Geschäftslogik beinhalten, daher kann ich ihn nur kurz erwähnen. Ohne die Nutzung von Drittanbieterdiensten wie redis
in Betracht zu ziehen, kann PHP Methoden wie Pipeline-Kommunikation und Shared Memory nativ implementieren. Die Implementierung ist relativ einfach, der Nachteil besteht jedoch darin, dass die nutzbare Datenkapazität begrenzt ist und der Datenaustausch nur über einfache Textprotokolle möglich ist.
So beenden Sie alle Prozessaufgaben manuell
如果多进程处理不当,很可能导致进程任务卡死,甚至占用过多系统资源,此时只能手动结束进程。
除了一个个的根据进程号来结束,还有一个快速的方法是首先在任务脚本里自定义进程名称,就是调用cli_set_process_title函数,然后在命令行输入:ps aux|grep cli_async_worker |grep -v grep|awk '{print $2}'|xargs kill -9 (里面的 cli_async_worker 就是自定义的进程名称),这样就可以快速结束多进程任务了。
以下是完整的任务执行脚本代码:
可能无法直接使用,需要修改的地方有:
脚本目录和日志目录常量
自动加载任务类的方法(默认是加载脚本目录中以Task
结尾的文件)
其他的如:错误和日志处理方式和文本格式就随意吧...
如果命名管道文件设置有错误,可能导致进程假死,你可能需要手动删除进程管道通信的代码。
多进程的例子:execAsyncTask('multi', [ 'test' => ['a', 'b', 'c'], 'grab' => [['url' => 'https://www.baidu.com', 'callback' => 'http://localhost']] ]);。执行情况可以在日志文件中查看。execAsyncTask函数参考【__使用popen打开进程管道__】。
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_USER_WARNING); @ini_set('display_errors', 0); @ini_set('date.timezone', 'PRC'); chdir(__DIR__); /* 任务脚本目录 */ defined('TASK_PATH') or define('TASK_PATH', realpath(__DIR__ .'/tasks')); /* 任务日志目录 */ defined('TASK_LOGS_PATH') or define('TASK_LOGS_PATH', __DIR__ .'/tasks/logs'); if (!is_dir(TASK_LOGS_PATH)) @mkdir(TASK_LOGS_PATH, 0777, true); set_exception_handler(function($e) { $time = date('H:i:s', time()); $msg = sprintf(''. '<h3>[%s] %s (%s)</h3>'. "\n". '<pre class="brush:php;toolbar:false">%s', $time, $e->getMessage(), $e->getCode(), $e->getTraceAsString() ); file_put_contents(TASK_LOGS_PATH .'/exception-'.date('Ymd').'.log', $msg.PHP_EOL, FILE_APPEND|LOCK_EX); }); set_error_handler(function($errno, $errmsg, $filename, $line) { if (!(error_reporting() & $errno)) return; ob_start(); debug_print_backtrace(); $backtrace = ob_get_contents(); ob_end_clean(); $datetime = date('Y-m-d H:i:s', time()); $msg = <<
Das obige ist der detaillierte Inhalt vonWie verwendet PHP die Befehlszeile, um die Aufgabenverarbeitung im asynchronen Multiprozessmodus zu implementieren (Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!