Swoole es una biblioteca de concurrencia basada en corrutinas, asincrónica y controlada por eventos con alto rendimiento para PHP.
Ejecute el programa Swoole mediante Docker
ventana acoplable ejecutar --rm phpswoole/swoole "php --ri swoole"
Para obtener detalles sobre cómo usarlo, consulte: Cómo usar esta imagen.
https://wiki.swoole.com/
$http = new SwooleHttpServer('127.0.0.1', 9501);$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);$http->on('solicitud', función ($solicitud, $respuesta) { $resultado = []; Co::join([go(function () use (&$resultado) {$resultado['google'] = file_get_contents("https://www.google.com/"); }),go(función () use (&$resultado) {$resultado['taobao'] = file_get_contents("https://www.taobao.com/"); }) ]);$respuesta->end(json_encode($resultado)); });$http->inicio();
Corun(función() {Cogo(función() {mientras(1) {dormir(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, 'La hora local es ' . date('n/j/Y g:i a')); } });Cogo(function() {$redis = new Redis();$redis->connect('127.0.0.1', 6379); while(true) {$redis->subscribe(['prueba'], función ($instancia, $nombredelcanal, $mensaje) {echo 'Nuevo mensaje de redis: '.$nombredelcanal, "==>", $mensaje, PHP_EOL; }); } });Cogo(function() {$redis = new Redis();$redis->connect('127.0.0.1', 6379);$count = 0; while(true) {sleep(2);$redis- >publicar('prueba','hola mundo, contar='.$contar++); } }); });
Swoole engancha la función io de bloqueo de PHP en la capa inferior y la convierte automáticamente en una función sin bloqueo, de modo que estas funciones se puedan llamar simultáneamente en corrutinas.
ext-curl (Soporte para Symfony y Guzzle)
ext-redis
ext-mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_oracle
ext-pdo_odbc
funciones de transmisión (por ejemplo, stream_socket_client/stream_socket_server), admite TCP/UDP/UDG/Unix/SSL/TLS/FileSystem API/Pipe
enchufes externos
jabón ext
dormir/nosotrosdormir/tiempo_dormir_hasta
proc_abierto
gethostbyname/shell_exec/exec
fread/fopen/fsockopen/fwrite/rebaño
Ayudante de IDE y API: https://github.com/swoole/ide-helper
Gorjeo: https://twitter.com/phpswoole
Discordia: https://discord.swoole.dev
Traducción: https://wiki.swoole.com/#/other/discussion
Project Awesome Swoole mantiene una lista seleccionada de cosas increíbles relacionadas con Swoole, que incluyen
Marcos y bibliotecas basados en Swoole.
Paquetes para integrar Swoole con marcos PHP populares, incluidos Laravel, Symfony, Slim y Yii.
Libros, vídeos y otros materiales de aprendizaje sobre Swoole.
Herramientas de depuración, creación de perfiles y prueba para desarrollar aplicaciones basadas en Swoole.
Paquetes y bibliotecas compatibles con rutinas.
Otros proyectos y recursos relacionados con Swoole.
La capa de red en Swoole está basada en eventos y aprovecha al máximo la implementación subyacente de epoll/kqueue, lo que hace que sea realmente fácil atender millones de solicitudes.
Swoole 4.x utiliza un kernel de motor completamente nuevo y ahora cuenta con un equipo de desarrolladores a tiempo completo, por lo que estamos entrando en un período sin precedentes en la historia de PHP que ofrece una posibilidad única para una rápida evolución en el rendimiento.
Swoole 4.x o posterior admite la rutina integrada con alta disponibilidad y puede utilizar código totalmente sincronizado para implementar el rendimiento asincrónico. Código PHP sin palabras clave adicionales, la programación automática de rutinas subyacente.
Los desarrolladores pueden entender las corrutinas como subprocesos ultraligeros y usted puede crear fácilmente miles de corrutinas en un solo proceso.
¡Las solicitudes simultáneas de 10.000 para leer datos de MySQL tardan solo 0,2 segundos!
$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () {$mysql = new SwooleCoroutineMySQL;$mysql->connect(['host' => '127.0.0.1','usuario' => 'raíz','contraseña' => 'raíz','base de datos' => 'prueba']);$sentencia = $mysql->prepare('SELECT * FROM `usuario`');for ($n = 100; $n--;) {$resultado = $declaración->ejecutar();assert(count($resultado) > 0 ); } }); } });echo 'usar' . (microtiempo(verdadero) - $s) . ' s';
Puede crear múltiples servicios en un único bucle de eventos: TCP, HTTP, Websocket y HTTP2, y manejar fácilmente miles de solicitudes.
función tcp_pack(cadena $datos): cadena{return pack('N', strlen($datos)) . $datos; }función tcp_unpack(cadena $datos): cadena{return substr($datos, 4, unpack('N', substr($datos, 0, 4))[1]); }$tcp_options = ['open_length_check' => verdadero,'package_length_type' => 'N','package_length_offset' => 0,'package_body_offset' => 4];
$servidor = new SwooleWebSocketServer('127.0.0.1', 9501, SWOOLE_BASE);$server->set(['open_http2_protocol' => true]);// http && http2$server->on('solicitud', función ( SwooleHttpRequest $solicitud, SwooleHttpResponse $respuesta) {$respuesta->end('Hola ' . $solicitud->rawcontent()); });// websocket$servidor->on('mensaje', función (SwooleWebSocketServer $servidor, SwooleWebSocketFrame $marco) {$servidor->push($marco->fd, 'Hola ' . $marco->datos); });// tcp$tcp_server = $servidor->listen('127.0.0.1', 9502, SWOOLE_TCP);$tcp_server->set($tcp_options);$tcp_server->on('recibir', función (SwooleServer $ servidor, int $fd, int $reactor_id, cadena $datos) {$servidor->send($fd, tcp_pack('Hola ' . tcp_unpack($datos))); });$servidor->start();
Ya sea que realice consultas DNS, envíe solicitudes o reciba respuestas, todo esto se programa automáticamente mediante rutina.
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(función () {// http2$http2_client = nuevo SwooleCoroutineHttp2Client('localhost', 9501);$http2_client->connect();$http2_request = nuevo SwooleHttp2Request;$http2_request->method = 'POST';$ http2_request->data = 'Lana 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())); });
El canal es la única forma de intercambiar datos entre rutinas; la combinación de desarrollo de Coroutine + Channel es el famoso modelo de programación CSP.
En el desarrollo de Swoole, Channel se usa generalmente para implementar un grupo de conexiones o programar rutinas concurrentes.
En el siguiente ejemplo, tenemos mil solicitudes simultáneas para redistribuir. Normalmente, esto ha excedido la configuración de número máximo de conexiones de Redis y generará una excepción de conexión, pero el grupo de conexiones basado en el canal puede programar solicitudes perfectamente. No tenemos que preocuparnos por la sobrecarga de la conexión.
clase RedisPool {/**@var SwooleCoroutineChannel */protected $pool;/** * Constructor de RedisPool. * @param int $tamaño máximo de conexiones */función pública __construct(int $tamaño = 100) {$this->pool = nuevo SwooleCoroutineChannel($tamaño);para ($i = 0; $i < $tamaño; $i++) {$redis = nuevo SwooleCoroutineRedis();$res = $redis->connect('127.0 .0.1', 6379);if ($res == false) {throw new RuntimeException("no se pudo conectar el servidor redis."); } más {$this->put($redis); } } }función pública get(): SwooleCoroutineRedis{return $this->pool->pop(); } función pública poner(SwooleCoroutineRedis $redis) {$this->pool->push($redis); }función pública cerrar(): void{$this->pool->close();$this->pool = null; } }go(function () {$pool = new RedisPool();// el número máximo de concurrencia es mayor que el máximo de conexiones// pero no hay problema, el canal te ayudará con la programación para ($c = 0; $c < 1000; $ c++) {go(función () uso ($pool, $c) {para ($n = 0; $n < 100; $n++) {$redis = $pool->get();assert($redis->set("increíble-{$c}-{$n}", 'swoole'));assert($redis->get("increíble-{$c }-{$n}") === 'swoole');assert($redis->delete("impresionante-{$c}-{$n}"));$pool->put($redis); } }); } });
Algunos clientes de Swoole implementan el modo de aplazamiento para la concurrencia, pero aún puedes implementarlo de manera flexible con una combinación de corrutinas y canales.
go(function () {// Usuario: Necesito que me traigas información.// Canal: ¡OK! Seré responsable de la programación.$channel = new SwooleCoroutineChannel;go(function () use ($channel) { // Corrutina A: ¡Ok! Te mostraré la dirección de github info$addr_info = Co::getaddrinfo('github.com');$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); });go(function () use ($channel) {// Coroutine B: ¡Ok! Te mostraré cómo se ve tu código$mirror = Co::readFile(__FILE__);$channel->push(['B ', $espejo]); });go(function () use ($channel) {// Coroutine C: ¡Ok! Te mostraré la fecha$channel->push(['C', date(DATE_W3C)]); });for ($i = 3; $i--;) {lista($id, $datos) = $canal->pop();echo "De {$id}:n {$datos}n"; }// Usuario: ¡Increíble, obtuve toda la información lo antes posible!});
$id = SwooleTimer::tick(100, function () {echo " Hacer algo...n"; });SwooleTimer::after(500, function () use ($id) {SwooleTimer::clear($id);echo "⏰ Hecho"; });SwooleTimer::after(1000, función () use ($id) {if (!SwooleTimer::exists($id)) {echo " ¡Muy bien!n"; } });
ir(función () {$i = 0;mientras (verdadero) { Co::sleep(0.1);echo " Haz algo...n";if (++$i === 5) {echo " Donen";break; } }echo " ¡Está bien! n"; });
A partir de Swoole v4.1.0, agregamos la capacidad de transformar bibliotecas de red PHP síncronas en bibliotecas de rutinas usando una sola línea de código.
Simplemente llame al método SwooleRuntime::enableCoroutine() en la parte superior de su script. En el siguiente ejemplo, nos conectamos a php-redis y simultáneamente leemos 10.000 solicitudes en 0,1 s:
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {para ($c = 100; $c--;) {go(function () { ($redis = nuevo Redis)->connect('127.0.0.1', 6379);for ($n = 100; $n--;) {assert($redis->get('impresionante') === ' lana'); } }); } });echo 'usar' . (microtiempo(verdadero) - $s) . ' s';
Al llamar a este método, el kernel Swoole reemplaza los punteros de función de flujo de ZendVM. Si usa extensiones basadas en php_stream, todas las operaciones de socket se pueden convertir dinámicamente para que sean IO asíncronas programadas por rutina en tiempo de ejecución.
Duerma 10.000 veces, lea, escriba, verifique y elimine archivos 10.000 veces, use PDO y MySQLi para comunicarse con la base de datos 10.000 veces, cree un servidor TCP y múltiples clientes para comunicarse entre sí 10.000 veces, cree un servidor UDP y múltiples clientes para comunicarse entre sí 10.000 veces... ¡Todo funciona bien en un solo proceso!
Sólo mira lo que trae el Swoole, imagínate...
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {// solo quiero dormir...for ($c = 100; $c--;) {go(function () {para ($n = 100; $n--;) {usleep(1000); } }); }// Lectura y escritura de archivos de 10K para ($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); }desvincular($tmp_filename); }); }// 10K pdo y 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--;) {$declaración->ejecutar();assert(count($declaración->fetchAll()) > 0); } }); }for ($c = 50; $c--;) {go(function () {$mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');$statement = $ mysqli->prepare('SELECT `id` FROM `usuario`');for ($n = 100; $n--;) {$declaración->bind_result($id);$declaración->ejecutar();$declaración->fetch();assert($id > 0); } }); }// php_stream servidor tcp y cliente con 12,8K solicitudes en función de proceso único tcp_pack(string $data): string{return pack('n', strlen($data)) . $datos; }función tcp_length(cadena $cabeza): int{return unpack('n', $cabeza)[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 === "Hola servidor Swoole #{$n}!");fwrite($conn, <span class=