Swoole é uma biblioteca de simultaneidade assíncrona, orientada a eventos e baseada em corrotinas com alto desempenho para PHP.
Execute o programa Swoole do Docker
docker run --rm phpswoole/swoole "php --ri swoole"
Para obter detalhes sobre como usá-lo, consulte: Como usar esta imagem.
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) { $resultado = []; 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($resultado)); });$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, 'A hora local é ' . date('n/j/Y g:i a')); } });Cogo(function() {$redis = new Redis();$redis->connect('127.0.0.1', 6379);while(true) {$redis->subscribe(['test'], função ($instance, $channelName, $message) {echo 'Nova mensagem 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','olá, mundo, contagem='.$count++); } }); });
Swoole conecta a função io de bloqueio do PHP na camada inferior e a converte automaticamente em uma função sem bloqueio, para que essas funções possam ser chamadas simultaneamente em corrotinas.
ext-curl (Suporta symfony e guzzle)
ext-redis
ext-mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_oracle
ext-pdo_odbc
funções de stream (por exemplo, stream_socket_client/stream_socket_server), suporta TCP/UDP/UDG/Unix/SSL/TLS/FileSystem API/Pipe
soquetes ext
ext-sabonete
dormir/usleep/time_sleep_until
proc_open
gethostbyname/shell_exec/exec
fread/fopen/fsockopen/fwrite/flock
Auxiliar IDE e API: https://github.com/swoole/ide-helper
Twitter: https://twitter.com/phpswoole
Discord: https://discord.swoole.dev
中文社区: https://wiki.swoole.com/#/other/discussion
O Project Awesome Swoole mantém uma lista com curadoria de coisas incríveis relacionadas ao Swoole, incluindo
Estruturas e bibliotecas baseadas em Swoole.
Pacotes para integrar o Swoole com estruturas PHP populares, incluindo Laravel, Symfony, Slim e Yii.
Livros, vídeos e outros materiais de aprendizagem sobre Swoole.
Ferramentas de depuração, criação de perfil e teste para desenvolvimento de aplicativos baseados em Swoole.
Pacotes e bibliotecas compatíveis com corrotinas.
Outros projetos e recursos relacionados ao Swoole.
A camada de rede no Swoole é baseada em eventos e aproveita ao máximo a implementação subjacente de epoll/kqueue, facilitando muito o atendimento de milhões de solicitações.
O Swoole 4.x usa um kernel de mecanismo totalmente novo e agora conta com uma equipe de desenvolvedores em tempo integral, portanto estamos entrando em um período sem precedentes na história do PHP que oferece uma possibilidade única de rápida evolução no desempenho.
Swoole 4.x ou posterior oferece suporte à corrotina integrada com alta disponibilidade e você pode usar código totalmente sincronizado para implementar desempenho assíncrono. Código PHP sem quaisquer palavras-chave adicionais, o agendamento automático de corrotinas subjacente.
Os desenvolvedores podem entender as corrotinas como threads ultraleves e você pode criar facilmente milhares de corrotinas em um único processo.
Solicitações simultâneas de 10K para leitura de dados do MySQL levam apenas 0,2s!
$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--;) {$resultado = $statement->execute();assert(count($result) > 0 ); } }); } });echo 'usar' . (microtempo(verdadeiro) - $s) . 's';
Você pode criar vários serviços em um único loop de eventos: TCP, HTTP, Websocket e HTTP2, e lidar facilmente com milhares de solicitações.
função tcp_pack(string $dados): string{return pack('N', strlen($dados)) . $dados; }função tcp_unpack(string $dados): string{return substr($dados, 4, unpack('N', substr($dados, 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 $ solicitação, SwooleHttpResponse $ resposta) {$response->end('Olá ' . $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 $ servidor, int $fd, int $reactor_id, string $data) {$servidor->send($fd, tcp_pack('Olá ' . tcp_unpack($data))); });$servidor->start();
Quer você consulte DNS, envie solicitações ou receba respostas, tudo isso é agendado automaticamente pela corrotina.
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()->dados); });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())); });
Canal é a única forma de troca de dados entre corrotinas, a combinação de desenvolvimento de Corotina + Canal é o famoso modelo de programação CSP.
No desenvolvimento Swoole, Channel geralmente é usado para implementar pool de conexões ou agendar corrotinas simultâneas.
No exemplo a seguir, temos mil solicitações simultâneas de redis. Normalmente, isso excedeu a configuração de número máximo de conexões Redis e gerará uma exceção de conexão, mas o pool de conexões baseado no canal pode agendar solicitações perfeitamente. Não precisamos nos preocupar com sobrecarga de conexão.
classe RedisPool {/**@var SwooleCoroutineChannel */protected $pool;/** * Construtor RedisPool. * @param int $tamanho máximo de conexões */função pública __construct(int $tamanho = 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("falha ao conectar o servidor redis."); } else {$this->put($redis); } } }função pública get(): SwooleCoroutineRedis{return $this->pool->pop(); }função pública colocada(SwooleCoroutineRedis $redis) {$this->pool->push($redis); }função pública close(): void{$this->pool->close();$this->pool = null; } }go(function () {$pool = new RedisPool();// num máximo de simultaneidade é maior que o máximo de conexões// mas não tem problema, o canal vai te ajudar com o agendamento for ($c = 0; $c < 1000; $ c++) {go(function () use ($pool, $c) {for ($n = 0; $n < 100; $n++) {$redis = $pool->get();assert($redis->set("incrível-{$c}-{$n}", 'swoole'));assert($redis->get("incrível-{$c }-{$n}") === 'swoole');assert($redis->delete("incrível-{$c}-{$n}"));$pool->put($redis); } }); } });
Alguns clientes do Swoole implementam o modo de adiamento para simultaneidade, mas você ainda pode implementá-lo de forma flexível com uma combinação de corrotinas e canais.
go(function() {// Usuário: Preciso que você me traga algumas informações.// Canal: OK! Eu serei responsável pelo agendamento.$channel = new SwooleCoroutineChannel;go(function () use ($channel) { // Corotina A: Ok! Vou mostrar o endereço do github info$addr_info = Co::getaddrinfo('github.com');$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]); });go(function () use ($channel) {// Corotina B: Ok! Vou mostrar como é o seu código$mirror = Co::readFile(__FILE__);$channel->push(['B ', $espelho]); });go(function () use ($channel) {// Corotina C: Ok, vou mostrar a data$channel->push(['C', date(DATE_W3C)]); });for ($i = 3; $i--;) {list($id, $data) = $canal->pop();echo "De {$id}:n {$data}n"; }// Usuário: Incrível, recebi todas as informações o mais rápido possível!});
$id = SwooleTimer::tick(100, function () {echo "Faça alguma coisa...n"; });SwooleTimer::after(500, function () use ($id) {SwooleTimer::clear($id);echo "⏰ Donen"; });SwooleTimer::after(1000, function () use ($id) {if (!SwooleTimer::exists($id)) {echo " Tudo bem!n"; } });
go(function() {$i = 0;while (true) { Co::sleep(0.1);echo " Faça alguma coisa...n";if (++$i === 5) {echo " Donen";break; } }echo "Tudo bem!n"; });
A partir do Swoole v4.1.0, adicionamos a capacidade de transformar bibliotecas de rede PHP síncronas em bibliotecas de co-rotina usando uma única linha de código.
Basta chamar o método SwooleRuntime::enableCoroutine() na parte superior do seu script. No exemplo abaixo, nos conectamos ao php-redis e lemos simultaneamente 10 mil solicitações em 0,1s:
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {for ($c = 100; $c--;) {go(function () { ($redis = novo Redis)->connect('127.0.0.1', 6379);for ($n = 100; $n--;) {assert($redis->get('awesome') === ' swoole'); } }); } });echo 'usar' . (microtempo(verdadeiro) - $s) . 's';
Ao chamar esse método, o kernel Swoole substitui os ponteiros de função de fluxo ZendVM. Se você usar extensões baseadas em php_stream, todas as operações de soquete poderão ser convertidas dinamicamente para IO assíncronas agendadas pela corrotina em tempo de execução!
Durma 10 mil vezes, leia, grave, verifique e exclua arquivos 10 mil vezes, use PDO e MySQLi para se comunicar com o banco de dados 10 mil vezes, crie um servidor TCP e vários clientes para se comunicarem entre si 10 mil vezes, crie um servidor UDP e vários clientes para comuniquem-se uns com os outros 10 mil vezes... Tudo funciona bem em um processo!
Veja só o que o Swoole traz, imagine só...
SwooleRuntime::enableCoroutine();$s = microtime(true);Corun(function() {// eu só quero dormir...for ($c = 100; $c--;) {go(function () {for ($n = 100; $n--;) {usleep(1000); } }); }// leitura e gravação de arquivo 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 e 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 `usuário`');for ($n = 100; $n--;) {$statement->bind_result($id);$statement->execute();$statement->fetch();assert($id > 0); } }); }// Servidor e cliente tcp php_stream com 12,8 mil solicitações em uma única função de processo tcp_pack(string $data): string{return pack('n', strlen($data)) . $dados; }função 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);se (!$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 === "Olá Servidor Swoole #{$n}!");fwrite($conn, <span class=