Swoole ist eine ereignisgesteuerte, asynchrone, Coroutine-basierte Parallelitätsbibliothek mit hoher Leistung für PHP.
Führen Sie das Swoole-Programm von Docker aus
docker run --rm phpswoole/swoole „php --ri swoole“
Einzelheiten zur Verwendung finden Sie unter: So verwenden Sie dieses Bild.
https://wiki.swoole.com/
$http = new SwooleHttpServer('127.0.0.1', 9501);$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);$http->on('request', function ($request, $response) { $result = []; Co::join([go(function () use (&$result) {$result['google'] = file_get_contents("https://www.google.com/"); }),go(function () use (&$result) {$result['taobao'] = file_get_contents("https://www.taobao.com/"); }) ]);$response->end(json_encode($result)); });$http->start();
Corun(function() {Cogo(function() {while(1) {sleep(1);$fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30);echo fread ($fp, 8192), PHP_EOL; } });Cogo(function() {$fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);while(1) {$conn = stream_socket_accept($fp) ;fwrite($conn, 'Die Ortszeit ist ' . date('n/j/Y g:i A')); } });Cogo(function() {$redis = new Redis();$redis->connect('127.0.0.1', 6379);while(true) {$redis->subscribe(['test'], function ($instance, $channelName, $message) {echo 'Neue Redis-Nachricht: '.$channelName, "==>", $message, PHP_EOL; }); } });Cogo(function() {$redis = new Redis();$redis->connect('127.0.0.1', 6379);$count = 0;while(true) {sleep(2);$redis- >publish('test','hello, world, count='.$count++); } }); });
Swoole hakt die blockierende io-Funktion von PHP auf der untersten Ebene ein und konvertiert sie automatisch in eine nicht blockierende Funktion, sodass diese Funktionen gleichzeitig in Coroutinen aufgerufen werden können.
ext-curl (Unterstützt Symfony und Guzzle)
ext-redis
ext-mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_oracle
ext-pdo_odbc
Stream-Funktionen (z. B. stream_socket_client/stream_socket_server), Unterstützt TCP/UDP/UDG/Unix/SSL/TLS/FileSystem API/Pipe
Ext-Buchsen
ext-seife
schlafen/usleep/time_sleep_until
proc_open
gethostbyname/shell_exec/exec
fread/fopen/fsockopen/fwrite/flock
IDE-Helfer und API: https://github.com/swoole/ide-helper
Twitter: https://twitter.com/phpswoole
Discord: https://discord.swoole.dev
Weitere Informationen: https://wiki.swoole.com/#/other/discussion
Project Awesome Swoole führt eine kuratierte Liste toller Dinge rund um Swoole, darunter
Swoole-basierte Frameworks und Bibliotheken.
Pakete zur Integration von Swoole mit gängigen PHP-Frameworks, einschließlich Laravel, Symfony, Slim und Yii.
Bücher, Videos und andere Lernmaterialien über Swoole.
Debugging-, Profiling- und Testtools für die Entwicklung von Swoole-basierten Anwendungen.
Coroutine-freundliche Pakete und Bibliotheken.
Andere Swoole-bezogene Projekte und Ressourcen.
Die Netzwerkschicht in Swoole ist ereignisbasiert und nutzt die zugrunde liegende Epoll/Kqueue-Implementierung voll aus, wodurch es wirklich einfach wird, Millionen von Anfragen zu bedienen.
Swoole 4.x verwendet einen brandneuen Engine-Kernel und verfügt nun über ein Vollzeit-Entwicklerteam. Damit treten wir in eine beispiellose Phase in der PHP-Geschichte ein, die eine einzigartige Möglichkeit für eine schnelle Leistungsentwicklung bietet.
Swoole 4.x oder höher unterstützt die integrierte Coroutine mit hoher Verfügbarkeit, und Sie können vollständig synchronisierten Code verwenden, um asynchrone Leistung zu implementieren. PHP-Code ohne zusätzliche Schlüsselwörter, die zugrunde liegende automatische Coroutine-Planung.
Entwickler können Coroutinen als ultraleichte Threads verstehen und Sie können problemlos Tausende von Coroutinen in einem einzigen Prozess erstellen.
Gleichzeitige 10K-Anfragen zum Lesen von Daten aus MySQL dauern nur 0,2 Sekunden!
$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () {$mysql = new SwooleCoroutineMySQL;$mysql->connect(['host' => '127.0.0.1','user' => 'root','password' => 'root','database' => 'test']);$statement = $mysql->prepare('SELECT * FROM `user`');for ($n = 100; $n--;) {$result = $statement->execute();assert(count($result) > 0 ); } }); } });echo 'use' . (microtime(true) - $s) . ' S';
Sie können mehrere Dienste in einer einzigen Ereignisschleife erstellen: TCP, HTTP, Websocket und HTTP2 und problemlos Tausende von Anfragen verarbeiten.
Funktion tcp_pack(string $data): string{return pack('N', strlen($data)) . $data; }function tcp_unpack(string $data): string{return substr($data, 4, unpack('N', substr($data, 0, 4))[1]); }$tcp_options = ['open_length_check' => true,'package_length_type' => 'N','package_length_offset' => 0,'package_body_offset' => 4];
$server = new SwooleWebSocketServer('127.0.0.1', 9501, SWOOLE_BASE);$server->set(['open_http2_protocol' => true]);// http && http2$server->on('request', function ( SwooleHttpRequest $request, SwooleHttpResponse $response) {$response->end('Hallo ' . $request->rawcontent()); });// websocket$server->on('message', function (SwooleWebSocketServer $server, SwooleWebSocketFrame $frame) {$server->push($frame->fd, 'Hello ' . $frame->data); });// tcp$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP);$tcp_server->set($tcp_options);$tcp_server->on('receive', function (SwooleServer $ server, int $fd, int $reactor_id, string $data) {$server->send($fd, tcp_pack('Hallo ' . tcp_unpack($data))); });$server->start();
Unabhängig davon, ob Sie DNS-Abfragen durchführen, Anfragen senden oder Antworten erhalten, wird all dies von Coroutine automatisch geplant.
go(function () {// http$http_client = new SwooleCoroutineHttpClient('127.0.0.1', 9501);assert($http_client->post('/', 'Swoole Http'));var_dump($http_client->body );// websocket$http_client->upgrade('/');$http_client->push('Swoole Websocket');var_dump($http_client->recv()->data); });go(function () {// http2$http2_client = new SwooleCoroutineHttp2Client('localhost', 9501);$http2_client->connect();$http2_request = new SwooleHttp2Request;$http2_request->method = 'POST';$ http2_request->data = 'Swoole Http2';$http2_client->send($http2_request);$http2_response = $http2_client->recv();var_dump($http2_response->data); });go(function () use ($tcp_options) {// tcp$tcp_client = new SwooleCoroutineClient(SWOOLE_TCP);$tcp_client->set($tcp_options);$tcp_client->connect('127.0.0.1', 9502);$tcp_client->send(tcp_pack('Swoole Tcp'));var_dump(tcp_unpack($tcp_client->recv())); });
Channel ist die einzige Möglichkeit, Daten zwischen Coroutinen auszutauschen. Die Entwicklungskombination aus Coroutine + Channel ist das berühmte CSP-Programmiermodell.
In der Swoole-Entwicklung wird Channel normalerweise zum Implementieren eines Verbindungspools oder zum gleichzeitigen Planen von Coroutinen verwendet.
Im folgenden Beispiel haben wir tausend gleichzeitige Anfragen an Redis. Normalerweise hat dies die maximale Anzahl von Redis-Verbindungen überschritten und eine Verbindungsausnahme ausgelöst, aber der auf dem Kanal basierende Verbindungspool kann Anforderungen perfekt planen. Wir müssen uns keine Sorgen über eine Überlastung der Verbindung machen.
Klasse RedisPool {/**@var SwooleCoroutineChannel */protected $pool;/** * RedisPool-Konstruktor. * @param int $size max. Verbindungen */public function __construct(int $size = 100) {$this->pool = new SwooleCoroutineChannel($size);for ($i = 0; $i < $size; $i++) {$redis = new SwooleCoroutineRedis();$res = $redis->connect('127.0 .0.1', 6379);if ($res == false) {throw new RuntimeException("Verbindung zum Redis-Server konnte nicht hergestellt werden."); } else {$this->put($redis); } } }public function get(): SwooleCoroutineRedis{return $this->pool->pop(); }öffentliche Funktion put(SwooleCoroutineRedis $redis) {$this->pool->push($redis); }public function close(): void{$this->pool->close();$this->pool = null; } }go(function () {$pool = new RedisPool();// max. Parallelitätszahl ist mehr als max. Verbindungen// aber es ist kein Problem, Channel hilft Ihnen bei der Planung für ($c = 0; $c < 1000; $ c++) {go(function () use ($pool, $c) {for ($n = 0; $n < 100; $n++) {$redis = $pool->get();assert($redis->set("awesome-{$c}-{$n}", 'swoole'));assert($redis->get("awesome-{$c }-{$n}") === 'swoole');assert($redis->delete("awesome-{$c}-{$n}"));$pool->put($redis); } }); } });
Einige Swoole-Clients implementieren den Verzögerungsmodus für Parallelität, Sie können ihn jedoch dennoch flexibel mit einer Kombination aus Coroutinen und Kanälen implementieren.
go(function () {// Benutzer: Du musst mir ein paar Informationen zurückbringen.// Kanal: OK! Ich werde für die Terminplanung verantwortlich sein.$channel = new SwooleCoroutineChannel;go(function () use ($channel) { // Coroutine A: Ok! Ich zeige dir die Github-Adresse info$addr_info = Co::getaddrinfo('github.com');$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); });go(function () use ($channel) {// Coroutine B: Ok! Ich zeige dir, wie dein Code aussieht$mirror = Co::readFile(__FILE__);$channel->push(['B ', $mirror]); });go(function () use ($channel) {// Coroutine C: Ok! Ich zeige dir das Datum$channel->push(['C', date(DATE_W3C)]); });for ($i = 3; $i--;) {list($id, $data) = $channel->pop();echo "From {$id}:n {$data}n"; }// Benutzer: Erstaunlich, ich habe alle Informationen zum frühesten Zeitpunkt erhalten!});
$id = SwooleTimer::tick(100, function () {echo " Mach etwas...n"; });SwooleTimer::after(500, function () use ($id) {SwooleTimer::clear($id);echo "⏰ Donen"; });SwooleTimer::after(1000, function () use ($id) {if (!SwooleTimer::exists($id)) {echo " Alles klar!n"; } });
go(function () {$i = 0;while (true) { Co::sleep(0.1);echo „ Mach etwas...n“;if (++$i === 5) {echo „ Donen“;break; } }echo „Alles klar!n“; });
Ab Swoole v4.1.0 haben wir die Möglichkeit hinzugefügt, synchrone PHP-Netzwerkbibliotheken mit einer einzigen Codezeile in Co-Routine-Bibliotheken umzuwandeln.
Rufen Sie einfach die Methode SwooleRuntime::enableCoroutine() oben in Ihrem Skript auf. Im folgenden Beispiel stellen wir eine Verbindung zu PHP-Redis her und lesen gleichzeitig 10.000 Anfragen in 0,1 Sekunden:
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () { ($redis = new Redis)->connect('127.0.0.1', 6379);for ($n = 100; $n--;) {assert($redis->get('awesome') === ' swoole'); } }); } });echo 'use' . (microtime(true) - $s) . ' S';
Durch den Aufruf dieser Methode ersetzt der Swoole-Kernel die ZendVM-Stream-Funktionszeiger. Wenn Sie php_stream-basierte Erweiterungen verwenden, können alle Socket-Vorgänge dynamisch in asynchrone E/A-Vorgänge umgewandelt werden, die von Coroutine zur Laufzeit geplant werden!
Schlafen Sie 10.000 Mal, lesen, schreiben, überprüfen und löschen Sie Dateien 10.000 Mal, verwenden Sie PDO und MySQLi, um 10.000 Mal mit der Datenbank zu kommunizieren, erstellen Sie einen TCP-Server und mehrere Clients, um 10.000 Mal miteinander zu kommunizieren, erstellen Sie einen UDP-Server und mehrere Clients 10.000 Mal miteinander kommunizieren... Alles funktioniert gut in einem Prozess!
Sehen Sie nur, was die Swoole bringt, stellen Sie sich vor ...
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {// ich möchte nur schlafen...for ($c = 100; $c--;) {go(function () {for ($n = 100; $n--;) {usleep(1000); } }); }// 10K Datei lesen und schreibenfor ($c = 100; $c--;) {go(function () use ($c) {$tmp_filename = "/tmp/test-{$c}.php";for ($n = 100; $n--;) {$self = file_get_contents(__FILE__);file_put_contents($tmp_filename, $self);assert(file_get_contents($tmp_filename) === $self); }unlink($tmp_filename); }); }// 10K pdo und mysqli readfor ($c = 50; $c--;) {go(function () {$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8' , 'root', 'root');$statement = $pdo->prepare('SELECT * FROM `user`');for ($n = 100; $n--;) {$statement->execute();assert(count($statement->fetchAll()) > 0); } }); }for ($c = 50; $c--;) {go(function () {$mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');$statement = $ mysqli->prepare('SELECT `id` FROM `user`');for ($n = 100; $n--;) {$statement->bind_result($id);$statement->execute();$statement->fetch();assert($id > 0); } }); }// php_stream TCP-Server und -Client mit 12,8K Anfragen in einem einzigen Prozessfunction tcp_pack(string $data): string{return pack('n', strlen($data)) . $data; }function tcp_length(string $head): int{return unpack('n', $head)[1]; }go(function () {$ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);$socket = stream_socket_server('tcp://0.0.0.0: 9502',$errno, $errstr, STREAM_SERVER_BIND |. $ctx);if (!$socket) {echo "$errstr ($errno)n"; } else {$i = 0;while ($conn = stream_socket_accept($socket, 1)) {stream_set_timeout($conn, 5);for ($n = 100; $n--;) {$data = fread($ conn, tcp_length(fread($conn, 2)));assert($data === "Hallo Swoole Server #{$n}!");fwrite($conn, <span class=