Swoole adalah pustaka konkurensi berbasis coroutine yang digerakkan oleh peristiwa, asinkron, dan berkinerja tinggi untuk PHP.
Jalankan program Swoole oleh Docker
menjalankan buruh pelabuhan --rm phpswoole/swoole "php --ri swoole"
Untuk detail cara menggunakannya, lihat: Cara Menggunakan Gambar Ini.
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) { $hasil = []; Co::join([go(function () use (&$result) {$result['google'] = file_get_contents("https://www.google.com/"); }),go(function () gunakan (&$hasil) {$hasil['taobao'] = file_get_contents("https://www.taobao.com/"); }) ]);$response->end(json_encode($hasil)); });$http->mulai();
Corun(fungsi() {Cogo(fungsi() {sementara(1) {tidur(1);$fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30);echo ketakutan ($fp, 8192), PHP_EOL; } });Cogo(function() {$fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);sementara(1) {$conn = stream_socket_accept($fp) ;fwrite($conn, 'Waktu setempat adalah ' .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 'Pesan redis baru: '.$channelName, "==>", $message, PHP_EOL; }); } });Cogo(function() {$redis = Redis baru();$redis->connect('127.0.0.1', 6379);$count = 0; while(true) {sleep(2);$redis- >publish('test','halo, dunia, count='.$count++); } }); });
Swoole mengaitkan fungsi pemblokiran io PHP di lapisan bawah dan secara otomatis mengubahnya menjadi fungsi non-pemblokiran, sehingga fungsi-fungsi ini dapat dipanggil secara bersamaan di coroutine.
ext-curl (Mendukung symfony dan membuang waktu)
ext-redis
ext-mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_Oracle
ext-pdo_odbc
fungsi aliran (misalnya stream_socket_client/stream_socket_server), Mendukung TCP/UDP/UDG/Unix/SSL/TLS/FileSystem API/Pipe
soket ext
ex-sabun
tidur/usleep/time_sleep_until
proc_open
gethostbyname/shell_exec/exec
fread/fopen/fsockopen/fwrite/flock
Pembantu IDE & API: https://github.com/swoole/ide-helper
Twitter: https://twitter.com/phpswoole
Perselisihan: https://discord.swoole.dev
Sumber: https://wiki.swoole.com/#/other/discussion
Project Awesome Swoole menyimpan daftar kurasi hal-hal mengagumkan yang terkait dengan Swoole, termasuk
Kerangka kerja dan perpustakaan berbasis Swoole.
Paket untuk mengintegrasikan Swoole dengan framework PHP populer, termasuk Laravel, Symfony, Slim, dan Yii.
Buku, video, dan materi pembelajaran lainnya tentang Swoole.
Alat debugging, pembuatan profil, dan pengujian untuk mengembangkan aplikasi berbasis Swoole.
Paket dan pustaka yang ramah coroutine.
Proyek dan sumber daya terkait Swoole lainnya.
Lapisan jaringan di Swoole berbasis peristiwa dan memanfaatkan sepenuhnya implementasi epoll/kqueue yang mendasarinya, sehingga sangat mudah untuk melayani jutaan permintaan.
Swoole 4.x menggunakan kernel mesin baru dan sekarang memiliki tim pengembang penuh waktu, jadi kita memasuki periode yang belum pernah terjadi sebelumnya dalam sejarah PHP yang menawarkan kemungkinan unik untuk evolusi kinerja yang cepat.
Swoole 4.x atau lebih baru mendukung coroutine bawaan dengan ketersediaan tinggi, dan Anda dapat menggunakan kode yang disinkronkan sepenuhnya untuk menerapkan kinerja asinkron. Kode PHP tanpa kata kunci tambahan apa pun, yang mendasari penjadwalan coroutine otomatis.
Pengembang dapat memahami coroutine sebagai thread yang sangat ringan, dan Anda dapat dengan mudah membuat ribuan coroutine dalam satu proses.
Permintaan 10K konkurensi untuk membaca data dari MySQL hanya membutuhkan 0,2 detik!
$s = microtime(true);Corun(function() {untuk ($c = 100; $c--;) {go(function () {$mysql = new SwooleCoroutineMySQL;$mysql->connect(['host' => '127.0.0.1','pengguna' => 'root','password' => 'root','database' => 'test']);$pernyataan = $mysql->mempersiapkan('PILIH * DARI `pengguna`');untuk ($n = 100; $n--;) {$hasil = $pernyataan->execute();assert(count($result) > 0 ); } }); } });gema 'gunakan ' . (waktu mikro(benar) - $s) . ' S';
Anda dapat membuat beberapa layanan dalam satu loop peristiwa: TCP, HTTP, Websocket, dan HTTP2, serta menangani ribuan permintaan dengan mudah.
fungsi tcp_pack(string $data): string{return pack('N', strlen($data)) . $data; }fungsi tcp_unpack(string $data): string{return substr($data, 4, unpack('N', substr($data, 0, 4))[1]); }$tcp_options = ['open_length_check' => benar,'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('permintaan', fungsi ( SwooleHttpRequest $permintaan, SwooleHttpResponse $respons) {$response->end('Halo ' .$request->rawcontent()); });// websocket$server->on('message', function (SwooleWebSocketServer $server, SwooleWebSocketFrame $frame) {$server->push($frame->fd, 'Halo ' . $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, ke dalam $fd, ke dalam $reaktor_id, string $data) {$server->send($fd, tcp_pack('Halo ' .tcp_unpack($data))); });$server->mulai();
Baik Anda melakukan kueri DNS atau mengirim permintaan atau menerima respons, semua ini dijadwalkan oleh coroutine secara otomatis.
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 = SwooleCoroutineHttp2Client('localhost', 9501);$http2_client->connect();$http2_request = SwooleHttp2Request baru;$http2_request->method = 'POST';$ http2_request->data = 'Swoole Http2';$http2_client->kirim($http2_request);$http2_response = $http2_client->recv();var_dump($http2_response->data); });go(fungsi () gunakan ($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())); });
Saluran adalah satu-satunya cara untuk bertukar data antar coroutine, kombinasi pengembangan Coroutine + Channel adalah model pemrograman CSP yang terkenal.
Dalam pengembangan Swoole, Channel biasanya digunakan untuk mengimplementasikan kumpulan koneksi atau menjadwalkan coroutine secara bersamaan.
Dalam contoh berikut, kami memiliki seribu permintaan secara bersamaan ke redis. Biasanya, ini telah melampaui jumlah maksimum pengaturan koneksi Redis dan akan memunculkan pengecualian koneksi, namun kumpulan koneksi berdasarkan Saluran dapat menjadwalkan permintaan dengan sempurna. Kami tidak perlu khawatir tentang kelebihan koneksi.
kelas RedisPool {/**@var SwooleCoroutineChannel */protected $pool;/** * Konstruktor RedisPool. * @param int $ukuran maks koneksi */fungsi publik __konstruksi(int $ukuran = 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("gagal menghubungkan server redis."); } else {$ini->put($redis); } } }fungsi publik get(): SwooleCoroutineRedis{return $this->pool->pop(); }fungsi publik put(SwooleCoroutineRedis $redis) {$ini->kumpulan->push($redis); }fungsi publik close(): void{$this->pool->close();$this->pool = null; } }go(function () {$pool = new RedisPool();// jumlah konkurensi maks lebih dari maks koneksi// tetapi tidak masalah, saluran akan membantu Anda dengan penjadwalan ($c = 0; $c < 1000; $ c++) {go(fungsi () gunakan ($pool, $c) {untuk ($n = 0; $n < 100; $n++) {$redis = $pool->get();assert($redis->set("luar biasa-{$c}-{$n}", 'swoole'));assert($redis->get("luar biasa-{$c }-{$n}") === 'swoole');assert($redis->delete("luar biasa-{$c}-{$n}"));$pool->put($redis); } }); } });
Beberapa klien Swoole menerapkan mode penangguhan untuk konkurensi, namun Anda masih dapat menerapkannya secara fleksibel dengan kombinasi coroutine dan saluran.
go(function () {// Pengguna: Saya ingin Anda memberikan saya beberapa informasi kembali.// Saluran: Oke! Saya akan bertanggung jawab untuk penjadwalan.$channel = new SwooleCoroutineChannel;go(function () use ($channel) { // Coroutine A: Oke! Saya akan menunjukkan kepada Anda github addr info$addr_info = Co::getaddrinfo('github.com');$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); });go(function () use ($channel) {// Coroutine B: Oke! Saya akan menunjukkan seperti apa kode Anda$mirror = Co::readFile(__FILE__);$channel->push(['B ', $cermin]); });go(function () use ($channel) {// Coroutine C: Oke! Saya akan tunjukkan date$channel->push(['C', date(DATE_W3C)]); });for ($i = 3; $i--;) {list($id, $data) = $channel->pop();echo "Dari {$id}:n {$data}n"; }// Pengguna: Luar biasa, saya mendapatkan semua informasi secepatnya!});
$id = SwooleTimer::tick(100, function () {echo " Lakukan sesuatu...n"; });SwooleTimer::after(500, function () gunakan ($id) {SwooleTimer::clear($id);echo "⏰ Selesai"; });SwooleTimer::after(1000, function () use ($id) {if (!SwooleTimer::exists($id)) {echo " Baiklah!n"; } });
go(fungsi () {$i = 0;sementara (benar) { Co::sleep(0.1);echo " Lakukan sesuatu...n";if (++$i === 5) {echo " Selesai";break; } }gema " Baiklah!n"; });
Pada Swoole v4.1.0, kami menambahkan kemampuan untuk mengubah pustaka jaringan PHP sinkron menjadi pustaka rutin bersama menggunakan satu baris kode.
Cukup panggil metode SwooleRuntime::enableCoroutine() di bagian atas skrip Anda. Pada contoh di bawah ini kita terhubung ke php-redis dan secara bersamaan membaca 10 ribu permintaan dalam 0,1 detik:
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {untuk ($c = 100; $c--;) {go(function () { ($redis = Redis baru)->connect('127.0.0.1', 6379);for ($n = 100; $n--;) {assert($redis->get('awesome') === ' pusing'); } }); } });gema 'gunakan ' . (waktu mikro(benar) - $s) . ' S';
Dengan memanggil metode ini, kernel Swoole menggantikan penunjuk fungsi aliran ZendVM. Jika Anda menggunakan ekstensi berbasis php_stream, semua operasi soket dapat dikonversi secara dinamis menjadi IO asinkron yang dijadwalkan oleh coroutine saat runtime!
Tidur 10K kali, membaca, menulis, memeriksa dan menghapus file 10K kali, menggunakan PDO dan MySQLi untuk berkomunikasi dengan database 10K kali, membuat server TCP dan banyak klien untuk berkomunikasi satu sama lain 10K kali, membuat server UDP dan banyak klien untuk berkomunikasi satu sama lain 10K kali... Semuanya bekerja dengan baik dalam satu proses!
Lihat saja apa yang dibawa Swoole, bayangkan saja...
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {// saya hanya ingin tidur...untuk ($c = 100; $c--;) {go(function () {untuk ($n = 100; $n--;) {usleep(1000); } }); }// 10 ribu file dibaca dan ditulis untuk ($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_namafile, $self);assert(file_get_contents($tmp_namafile) === $self); }batalkan tautan($tmp_namafile); }); }// 10K pdo dan mysqli dibaca ($c = 50; $c--;) {go(function () {$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8' , 'root', 'root');$pernyataan = $pdo->prepare('SELECT * FROM `user`');for ($n = 100; $n--;) {$statement->execute();assert(count($statement->fetchAll()) > 0); } }); }untuk ($c = 50; $c--;) {go(function () {$mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');$pernyataan = $ mysqli->mempersiapkan('PILIH `id` DARI `pengguna`');untuk ($n = 100; $n--;) {$statement->bind_result($id);$statement->execute();$statement->fetch();assert($id > 0); } }); }// php_stream server & klien tcp dengan 12,8 ribu permintaan dalam satu prosesfungsi tcp_pack(string $data): string{return pack('n', strlen($data)) . $data; }fungsi 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;sementara ($conn = stream_socket_accept($socket, 1)) {stream_set_timeout($conn, 5);for ($n = 100; $n--;) {$data = fread($ samb, tcp_length(fread($conn, 2)));assert($data === "Halo Server Swoole #{$n}!");fwrite($sambungan, <span class=