Swoole は、PHP 向けの高いパフォーマンスを備えた、イベント駆動型、非同期、コルーチンベースの同時実行ライブラリです。
DockerでSwooleプログラムを実行する
docker run --rm phpswoole/swoole "php --ri swoole"
使用方法の詳細については、「この画像の使用方法」を参照してください。
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) { $結果 = []; 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, '現地時間は ' です。 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 '新しい redis メッセージ: '.$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 は、PHP のブロッキング io 関数を最下層にフックし、それを非ブロッキング関数に自動的に変換するため、これらの関数をコルーチン内で同時に呼び出すことができます。
ext-curl (symfony と guzzle をサポート)
外部 Redis
外部mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_oracle
ext-pdo_odbc
ストリーム関数 (例: stream_socket_client/stream_socket_server)、TCP/UDP/UDG/Unix/SSL/TLS/FileSystem API/Pipe をサポート
外部ソケット
石鹸外
スリープ/スリープ/time_sleep_until
proc_open
gethostbyname/shell_exec/exec
fread/fopen/fsockopen/fwrite/flock
IDE ヘルパーと API: https://github.com/swoole/ide-helper
Twitter: https://twitter.com/phpswoole
ディスコード: https://discord.swoole.dev
中文社区: https://wiki.swoole.com/#/other/Discussion
Project Awesome Swoole は、Swoole に関連する素晴らしいものの厳選されたリストを管理しています。
Swoole ベースのフレームワークとライブラリ。
Swoole を、Laravel、Symfony、Slim、Yii などの一般的な PHP フレームワークと統合するためのパッケージ。
Swoole に関する書籍、ビデオ、その他の学習教材。
Swoole ベースのアプリケーションを開発するためのデバッグ、プロファイリング、テスト ツール。
コルーチンに適したパッケージとライブラリ。
その他の Swoole 関連のプロジェクトとリソース。
Swoole のネットワーク層はイベントベースであり、基盤となる epoll/kqueue 実装を最大限に活用しているため、数百万のリクエストに非常に簡単に対応できます。
Swoole 4.x は真新しいエンジン カーネルを使用しており、現在はフルタイムの開発者チームがいます。そのため、パフォーマンスの急速な進化という独自の可能性を提供する、PHP の歴史の中でも前例のない時期に突入しています。
Swoole 4.x 以降では、高可用性の組み込みコルーチンがサポートされており、完全に同期されたコードを使用して非同期パフォーマンスを実装できます。追加のキーワードを含まない PHP コード、基礎となる自動コルーチン スケジューリング。
開発者はコルーチンを超軽量スレッドとして理解でき、単一プロセスで数千のコルーチンを簡単に作成できます。
同時実行 MySQL からデータを読み取る 10K リクエストの所要時間はわずか 0.2 秒です。
$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () {$mysql = new SwooleCoroutineMySQL;$mysql->connect(['host' => '127.0.0.1','ユーザー' => 'ルート','パスワード' => 'ルート','データベース' => 'test']);$statement = $mysql->prepare('SELECT * FROM `user`');for ($n = 100; $n--;) {$result = $statement->execute();アサート(カウント($結果) > 0); } }); } });エコー '使用' 。 (microtime(true) - $s) 。 「s」;
単一のイベント ループ上に複数のサービス (TCP、HTTP、Websocket、HTTP2) を作成し、数千のリクエストを簡単に処理できます。
関数 tcp_pack(string $data): string{return Pack('N', strlen($data)) 。 $データ; }関数 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('Hello ' . $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 $サーバー、int $fd、int $reactor_id、string $data) {$server->send($fd, tcp_pack('Hello ' . tcp_unpack($data))); });$server->start();
DNS クエリを実行する場合でも、リクエストを送信する場合でも、応答を受信する場合でも、これらはすべてコルーチンによって自動的にスケジュールされます。
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 = 'スウール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())); });
チャネルはコルーチン間でデータを交換する唯一の方法であり、コルーチン + チャネルの開発の組み合わせは有名な CSP プログラミング モデルです。
Swoole 開発では、通常、チャネルは接続プールの実装やコルーチンの同時実行のスケジュールに使用されます。
次の例では、redis に対して 1,000 の同時リクエストがあります。通常、これは Redis 接続設定の最大数を超えているため、接続例外がスローされますが、チャネルに基づく接続プールはリクエストを完全にスケジュールできます。接続の過負荷を心配する必要はありません。
クラスRedisPool {/**@var SwooleCoroutineChannel */protected $pool;/** * RedisPool コンストラクター。 * @param int $size 最大接続数 */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("redis への接続に失敗しましたサーバー。"); else {$this->put($redis); } } }パブリック関数 get(): SwooleCoroutineRedis{return $this->pool->pop(); }パブリック関数 put(SwooleCoroutineRedis $redis) {$this->pool->push($redis); }パブリック関数 close(): void{$this->pool->close();$this->pool = null; } }go(function () {$pool = new RedisPool();// 最大同時実行数は最大接続数を超えています// でも問題ありません。チャネルがスケジューリングに役立ちますfor ($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); } }); } });
一部の Swoole クライアントは同時実行のために遅延モードを実装していますが、コルーチンとチャネルを組み合わせて柔軟に実装することもできます。
go(function () {// ユーザー: 情報を持ってきてください。// チャンネル: OK! スケジュールは私が担当します。$channel = new SwooleCoroutineChannel;go(function () use ($channel) { // コルーチン A: わかりました。github addr info$addr_info = を紹介します。 Co::getaddrinfo('github.com');$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); });go(function () use ($channel) {// コルーチン B: わかりました! コードがどのようなものかを示します $mirror = Co::readFile(__FILE__);$channel->push(['B ', $ミラー]); });go(function () use ($channel) {// コルーチン C: わかりました! date$channel->push(['C', date(DATE_W3C)]); を示します。 });for ($i = 3; $i--;) {list($id, $data) = $channel->pop();echo "From {$id}:n {$data}n"; }// ユーザー: すごいですね、すべての情報を早い段階で入手できました! });
$id = SwooleTimer::tick(100, function () {echo " 何かをしてください...n"; });SwooleTimer::after(500, function () use ($id) {SwooleTimer::clear($id);echo "⏰ Donen"; });SwooleTimer::after(1000, function () use ($id) {if (!SwooleTimer::exists($id)) {echo " 大丈夫!n"; } });
go(function() {$i = 0;while (true) { Co::sleep(0.1);echo " 何かをしてください...n";if (++$i === 5) {echo " Donen";break; } } echo "大丈夫!n"; });
Swoole v4.1.0 では、1 行のコードを使用して同期 PHP ネットワーク ライブラリをコルーチン ライブラリに変換する機能が追加されました。
スクリプトの先頭で SwooleRuntime::enableCoroutine() メソッドを呼び出すだけです。以下のサンプルでは、php-redis に接続し、0.1 秒で 10,000 のリクエストを同時に読み取ります。
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () { ($redis = 新しい Redis)->connect('127.0.0.1', 6379);for ($n = 100; $n--;) {assert($redis->get('awesome') === 'よだれが出る'); } }); } });エコー '使用' 。 (microtime(true) - $s) 。 「s」;
このメソッドを呼び出すことにより、Swoole カーネルは ZendVM ストリーム関数ポインターを置き換えます。 php_stream ベースの拡張機能を使用すると、すべてのソケット操作を、実行時にコルーチンによってスケジュールされた非同期 IO に動的に変換できます。
10,000 回のスリープ、10,000 回のファイルの読み取り、書き込み、チェック、削除、PDO および MySQLi を使用したデータベースとの 10,000 回の通信、10,000 回相互通信するための TCP サーバーと複数のクライアントの作成、10,000 回の相互通信のための UDP サーバーと複数のクライアントの作成相互に 10,000 回通信します...すべてが 1 つのプロセスでうまく機能します。
Swoole がもたらすものを見て、想像してみてください...
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {// ただ寝たいだけです...for ($c = 100; $c--;) {go(function () {for ($n = 100; $n--;) {usleep(1000); } }); }// 10K ファイルの読み取りと書き込みfor ($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); }リンク解除($tmp_ファイル名); }); }// 10K pdo と 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 単一プロセスで 12.8K のリクエストを含む php_stream tcp サーバーとクライアントfunction tcp_pack(string $data): string{return Pack('n', strlen($data)) 。 $データ; }関数 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 | STREAM_SERVER_LISTEN, $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 === "こんにちは、Swoole サーバーです#{$n}!");fwrite($conn, <span class=