Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.
docker run --rm phpswoole/swoole " php --ri swoole "
For details on how to use it, see: How to Use This Image .
$ http = new Swoole \ Http \ Server ( '127.0.0.1' , nine thousand five hundred and one ); $ 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 ();
Co \run ( function () { Co \go ( function () { while ( one ) { sleep( one ); $ fp = stream_socket_client(" tcp://127.0.0.1:8000 ", $ errno , $ errstr , thirty ); echo fread( $ fp , eight thousand one hundred and ninety-two ), PHP_EOL ; } }); Co \go ( function () { $ fp = stream_socket_server(" tcp://0.0.0.0:8000 ", $ errno , $ errstr , STREAM_SERVER_BIND | STREAM_SERVER_LISTEN ); while ( one ) { $ conn = stream_socket_accept( $ fp ); fwrite( $ conn , 'The local time is ' . date( 'n/j/Y g:i a' )); } }); Co \go ( function () { $ redis = new Redis (); $ redis -> connect ( '127.0.0.1' , six thousand three hundred and seventy-nine ); while ( true ) { $ redis -> subscribe ([ 'test' ], function ( $ instance , $ channelName , $ message ) { echo 'New redis message: ' . $ channelName , " ==> ", $ message , PHP_EOL ; }); } }); Co \go ( function () { $ redis = new Redis (); $ redis -> connect ( '127.0.0.1' , six thousand three hundred and seventy-nine ); $ count = zero ; while ( true ) { sleep( two ); $ redis -> publish ( 'test' , 'hello, world, count=' . $ count ++); } }); });
ext-curl (Support symfony and guzzle ) ext-redis ext-mysqli ext-pdo_mysql ext-pdo_pgsql ext-pdo_sqlite ext-pdo_oracle ext-pdo_odbc stream functions (e.g. stream_socket_client / stream_socket_server ), Supports TCP / UDP / UDG / Unix / SSL/TLS / FileSystem API / Pipe ext-sockets ext-soap sleep / usleep / time_sleep_until proc_open gethostbyname / shell_exec / exec fread / fopen / fsockopen / fwrite / flock
-
IDE Helper & API : https://github.com/swoole/ide-helper -
Twitter : https://twitter.com/phpswoole -
Discord : https://discord.swoole.dev -
Chinese Community : https://wiki.swoole.com/#/other/discussion
-
Swoole-based frameworks and libraries. -
Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii. -
Books, videos, and other learning materials about Swoole. -
Debugging, profiling, and testing tools for developing Swoole-based applications. -
Coroutine-friendly packages and libraries. -
Other Swoole related projects and resources.
$ s = microtime( true ); Co \run ( function () { for ( $ c = one hundred ; $ c --;) { go( function () { $ mysql = new Swoole \ Coroutine \ MySQL ; $ mysql -> connect ([ 'host' => '127.0.0.1' , 'user' => 'root' , 'password' => 'root' , 'database' => 'test' ]); $ statement = $ mysql -> prepare ( 'SELECT * FROM `user`' ); for ( $ n = one hundred ; $ n --;) { $ result = $ statement -> execute (); assert(count( $ result ) > zero ); } }); } }); echo 'use ' . (microtime( true ) - $ s ) . ' s' ;
function tcp_pack ( string $ data ): string { return pack( 'N' , strlen( $ data )) . $ data ; } function tcp_unpack ( string $ data ): string { return substr( $ data , four , unpack( 'N' , substr( $ data , zero , four ))[ one ]); } $ tcp_options = [ 'open_length_check' => true , 'package_length_type' => 'N' , 'package_length_offset' => zero , 'package_body_offset' => four ];
$ server = new Swoole \ WebSocket \ Server ( '127.0.0.1' , nine thousand five hundred and one , SWOOLE_BASE ); $ server -> set ([ 'open_http2_protocol' => true ]); // http && http2
$ server -> on ( 'request' , function ( Swoole \ Http \ Request $ request , Swoole \ Http \ Response $ response ) { $ response -> end ( 'Hello ' . $ request -> rawcontent ()); }); // websocket
$ server -> on ( 'message' , function ( Swoole \ WebSocket \ Server $ server , Swoole \ WebSocket \ Frame $ frame ) { $ server -> push ( $ frame -> fd , 'Hello ' . $ frame -> data ); }); // tcp
$ tcp_server = $ server -> listen ( '127.0.0.1' , nine thousand five hundred and two , SWOOLE_TCP ); $ tcp_server -> set ( $ tcp_options ); $ tcp_server -> on ( 'receive' , function ( Swoole \ Server $ server , int $ fd , int $ reactor_id , string $ data ) { $ server -> send ( $ fd , tcp_pack( 'Hello ' . tcp_unpack( $ data ))); }); $ server -> start ();
go( function () { // http
$ http_client = new Swoole \ Coroutine \ Http \ Client ( '127.0.0.1' , nine thousand five hundred and one ); 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 Swoole \ Coroutine \ Http2 \ Client ( 'localhost' , nine thousand five hundred and one ); $ http2_client -> connect (); $ http2_request = new Swoole \ Http2 \ Request ; $ 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 Swoole \ Coroutine \ Client ( SWOOLE_TCP ); $ tcp_client -> set ( $ tcp_options ); $ tcp_client -> connect ( '127.0.0.1' , nine thousand five hundred and two ); $ tcp_client -> send (tcp_pack( 'Swoole Tcp' )); var_dump(tcp_unpack( $ tcp_client -> recv ())); });
class RedisPool { /**@var \Swoole\Coroutine\Channel */
protected $ pool ; /**
* RedisPool constructor.
* @param int $size max connections
*/
public function __construct ( int $ size = one hundred ) { $ this -> pool = new \ Swoole \ Coroutine \ Channel ( $ size ); for ( $ i = zero ; $ i < $ size ; $ i ++) { $ redis = new \ Swoole \ Coroutine \ Redis (); $ res = $ redis -> connect ( '127.0.0.1' , six thousand three hundred and seventy-nine ); if ( $ res == false ) { throw new \ RuntimeException (" failed to connect redis server. "); } else { $ this -> put ( $ redis ); } } } public function get (): \ Swoole \ Coroutine \ Redis { return $ this -> pool -> pop (); } public function put ( \ Swoole \ Coroutine \ Redis $ redis ) { $ this -> pool -> push ( $ redis ); } public function close (): void { $ this -> pool -> close (); $ this -> pool = null ; } } go( function () { $ pool = new RedisPool (); // max concurrency num is more than max connections
// but it's no problem, channel will help you with scheduling for ( $ c = zero ; $ c < one thousand ; $ c ++) { go( function () use ( $ pool , $ c ) { for ( $ n = zero ; $ n < one hundred ; $ 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 ); } }); } });
go( function () { // User: I need you to bring me some information back.
// Channel: OK! I will be responsible for scheduling.
$ channel = new Swoole \ Coroutine \ Channel ; go( function () use ( $ channel ) { // Coroutine A: Ok! I will show you the 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: Ok! I will show you what your code look like
$ mirror = Co :: readFile (__FILE__); $ channel -> push ([ 'B' , $ mirror ]); }); go( function () use ( $ channel ) { // Coroutine C: Ok! I will show you the date
$ channel -> push ([ 'C' , date( DATE_W3C )]); }); for ( $ i = three ; $ i --;) { list ( $ id , $ data ) = $ channel -> pop (); echo " From { $ id } : \n { $ data }\n"; } // User: Amazing, I got every information at earliest time! });
$ id = Swoole \ Timer :: tick ( one hundred , function () { echo " ⚙️ Do something... \n"; }); Swoole \ Timer :: after ( five hundred , function () use ( $ id ) { Swoole \ Timer :: clear ( $ id ); echo " ⏰ Done \n"; }); Swoole \ Timer :: after ( one thousand , function () use ( $ id ) { if (! Swoole \ Timer :: exists ( $ id )) { echo " ✅ All right! \n"; } });
go( function () { $ i = zero ; while ( true ) { Co :: sleep ( zero point one ); echo " 📝 Do something... \n"; if (++ $ i === five ) { echo " 🛎 Done \n"; break ; } } echo " 🎉 All right! \n"; });
Swoole \ Runtime :: enableCoroutine (); $ s = microtime( true ); Co \run ( function () { for ( $ c = one hundred ; $ c --;) { go( function () { ( $ redis = new Redis )-> connect ( '127.0.0.1' , six thousand three hundred and seventy-nine ); for ( $ n = one hundred ; $ n --;) { assert( $ redis -> get ( 'awesome' ) === 'swoole' ); } }); } }); echo 'use ' . (microtime( true ) - $ s ) . ' s' ;
Swoole \ Runtime :: enableCoroutine (); $ s = microtime( true ); Co \run ( function () { // i just want to sleep... for ( $ c = one hundred ; $ c --;) { go( function () { for ( $ n = one hundred ; $ n --;) { usleep( one thousand ); } }); } // 10K file read and write for ( $ c = one hundred ; $ c --;) { go( function () use ( $ c ) { $ tmp_filename = " /tmp/test- { $ c } .php "; for ( $ n = one hundred ; $ n --;) { $ self = file_get_contents(__FILE__); file_put_contents( $ tmp_filename , $ self ); assert(file_get_contents( $ tmp_filename ) === $ self ); } unlink( $ tmp_filename ); }); } // 10K pdo and mysqli read for ( $ c = fifty ; $ 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 = one hundred ; $ n --;) { $ statement -> execute (); assert(count( $ statement -> fetchAll ()) > zero ); } }); } for ( $ c = fifty ; $ c --;) { go( function () { $ mysqli = new Mysqli ( '127.0.0.1' , 'root' , 'root' , 'test' ); $ statement = $ mysqli -> prepare ( 'SELECT `id` FROM `user`' ); for ( $ n = one hundred ; $ n --;) { $ statement -> bind_result ( $ id ); $ statement -> execute (); $ statement -> fetch (); assert( $ id > zero ); } }); } // php_stream tcp server & client with 12.8K requests in single process
function tcp_pack ( string $ data ): string { return pack( 'n' , strlen( $ data )) . $ data ; } function tcp_length ( string $ head ): int { return unpack( 'n' , $ head )[ one ]; } go( function () { $ ctx = stream_context_create([ 'socket' => [ 'so_reuseaddr' => true , 'backlog' => one hundred and twenty-eight ]]); $ 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 = zero ; while ( $ conn = stream_socket_accept( $ socket , one )) { stream_set_timeout( $ conn , five ); for ( $ n = one hundred ; $ n --;) { $ data = fread( $ conn , tcp_length(fread( $ conn , two ))); assert( $ data === " Hello Swoole Server # { $ n } ! "); fwrite( $ conn , tcp_pack(" Hello Swoole Client # { $ n } ! ")); } if (++ $ i === one hundred and twenty-eight ) { fclose( $ socket ); break ; } } } }); for ( $ c = one hundred and twenty-eight ; $ c --;) { go( function () { $ fp = stream_socket_client(" tcp://127.0.0.1:9502 ", $ errno , $ errstr , one ); if (! $ fp ) { echo " $ errstr ( $ errno ) \n"; } else { stream_set_timeout( $ fp , five ); for ( $ n = one hundred ; $ n --;) { fwrite( $ fp , tcp_pack(" Hello Swoole Server # { $ n } ! ")); $ data = fread( $ fp , tcp_length(fread( $ fp , two ))); assert( $ data === " Hello Swoole Client # { $ n } ! "); } fclose( $ fp ); } }); } // udp server & client with 12.8K requests in single process go( function () { $ socket = new Swoole \ Coroutine \ Socket ( AF_INET , SOCK_DGRAM , zero ); $ socket -> bind ( '127.0.0.1' , nine thousand five hundred and three ); $ client_map = []; for ( $ c = one hundred and twenty-eight ; $ c --;) { for ( $ n = zero ; $ n < one hundred ; $ n ++) { $ recv = $ socket -> recvfrom ( $ peer ); $ client_uid = "{ $ peer [ 'address' ]} : { $ peer [ 'port' ]}"; $ id = $ client_map [ $ client_uid ] = ( $ client_map [ $ client_uid ] ?? - one ) + one ; assert( $ recv === " Client: Hello # { $ id } ! "); $ socket -> sendto ( $ peer [ 'address' ], $ peer [ 'port' ], " Server: Hello # { $ id } ! "); } } $ socket -> close (); }); for ( $ c = one hundred and twenty-eight ; $ c --;) { go( function () { $ fp = stream_socket_client(" udp://127.0.0.1:9503 ", $ errno , $ errstr , one ); if (! $ fp ) { echo " $ errstr ( $ errno ) \n"; } else { for ( $ n = zero ; $ n < one hundred ; $ n ++) { fwrite( $ fp , " Client: Hello # { $ n } ! "); $ recv = fread( $ fp , one thousand and twenty-four ); list ( $ address , $ port ) = explode( ':' , (stream_socket_get_name( $ fp , true ))); assert( $ address === '127.0.0.1' && ( int ) $ port === nine thousand five hundred and three ); assert( $ recv === " Server: Hello # { $ n } ! "); } fclose( $ fp ); } }); } }); echo 'use ' . (microtime( true ) - $ s ) . ' s' ;
As with any open source project, Swoole always provides the most reliable stability and the most powerful features in the latest released version . Please ensure as much as possible that you are using the latest version.
-
Linux, OS X or Cygwin, WSL -
PHP 7.2.0 or later (The higher the version, the better the performance.) -
GCC 4.8 or later
pecl install swoole
git clone https://github.com/swoole/swoole-src.git && \ cd swoole-src
phpize && \ ./configure && \ make && make install
for example:
./configure --enable-openssl --enable-sockets
--enable-openssl or --with-openssl-dir=DIR --enable-sockets --enable-mysqlnd (need mysqlnd, it just for supporting $mysql->escape method) --enable-swoole-curl
⚠️ If you upgrade from source, don't forget to
make clean before you upgrade your swoole
pecl upgrade swoole cd swoole-src && git pull && make clean && make && sudo make install -
if you change your PHP version, please re-run phpize clean && phpize then try to compile
git clone https://github.com/swoole/ext-async.git cd ext-async phpize ./configure make -j 4 sudo make install
-
On the open source Techempower Web Framework benchmarks Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon. -
You can just run Benchmark Script to quickly test the maximum QPS of Swoole-HTTP-Server on your machine.
-
Report issues and feedback -
Submit fixes, features via Pull Request -
Write/polish documentation