Swoole currently has a rich set of built-in coroutine components that developers can call directly for fast asynchronous non-blocking concurrent programming, saving developers the trouble of implementing the corresponding underlying code themselves:

TCP/UDP Client: Swoole\Coroutine\Client TCP/UDP Server: Swoole\Coroutine\Server HTTP/WebSocket Client: Swoole\Coroutine\HTTP\Client HTTP/WebSocket Server: Swoole\Coroutine\HTTP\Server HTTP2 Client: Swoole\Coroutine\HTTP2\Client Redis Client: Swoole\Coroutine\Redis Mysql Client: Swoole\Coroutine\ Mysql PostgreSQL Client: Swoole\Coroutine\PostgreSQLCopy the code

At the same time, Swoole provides a Coroutine tool set: Swoole\Coroutine, which provides the ability to obtain the current Coroutine ID and reflect calls.

Concurrent programming is implemented through the setDefer mechanism

We use the Redis and MySQL client requests as an example, using the above Swoole\Coroutine\Redis and Swoole\Coroutine\MySQL components, we can implement asynchronous Redis and MySQL clients:

<? PHP $server = new \Swoole\Http\ server ('127.0.0.1', 9588); $server->on('Request', function ($request, $response) { var_dump(time()); $mysql = new Swoole\Coroutine\MySQL(); $mysql - > connect ([' host '= >' 127.0.0.1 ', 'user' = > 'root' and 'password' = > 'root', 'database' = > 'laravel58']); $mysql->setDefer(); $mysql->query('select sleep(3)'); var_dump(time()); $redis1 = new Swoole\Coroutine\Redis(); $redis1 - > connect (127.0.0.1, 6379); $redis1->setDefer(); $redis1->set('hello', 'world'); var_dump(time()); $redis2 = new Swoole\Coroutine\Redis(); $redis2 - > connect (127.0.0.1, 6379); $redis2->setDefer(); $redis2->get('hello'); $result1 = $mysql->recv(); $result2 = $redis2->recv(); var_dump($result1, $result2, time()); $response->end('Request Finish: ' . time()); }); $server->start();Copy the code

Since Swoole automatically starts coroutines in TCP Server and HTTP Server callbacks, there is no need to explicitly start coroutines with the GO keyword, and we can then use MySQL and Redis client coroutine components to initiate requests in callbacks.

To understand how the code above works, you need to understand the setDefer mechanism of the coroutine. Most coroutine components support setDefer, which splits the request-response interface into two steps: sending data and receiving the response results concurrently.

Because most of the time, and send the data to establish the connection “time” compared to “wait for a response time” is negligible, so can be as simple as defer mode, multiple client request and response are concurrent (actually only receive responses are concurrent, establish a connection and sends the request is a serial).

In the code above, for example, setDefer(true) will no longer wait for the server to return the request, but will return true as soon as the request is sent. After that, you can continue to make other Redis, MySQL requests, and finally use the recv() method to receive the response.

We save the above code to coroutine/http.php and start the HTTP server on the terminal:

php coroutine/http.php
Copy the code

Next, make a request to the server in Postman and wait a few seconds to see the response returned:

! [](https://pic2.zhimg.com/80/v2-9f49588ef1983a9f8bd34b8b8f8dc772_720w.jpg)

The first three times are the time when the clients of mysql, redis1 and redis2 initiated requests respectively. As you can see, although mysql sleeps for 3 seconds, the concurrent execution of the three requests has been realized through the mechanism of defer.

Concurrent programming through subcoroutines + channels

In addition to the setDefer mechanism, Swoole also supports concurrent programming via subcoroutines + channels. Let’s rewrite the above code implementation using subcoroutines + channels:

<? PHP $server = new \Swoole\Http\ server ('127.0.0.1', 9588); $server->on('Request', function ($request, $response) { $channel = new \Swoole\Coroutine\Channel(3); go(function () use ($channel) { var_dump(time()); $mysql = new Swoole\Coroutine\MySQL(); $mysql - > connect ([' host '= >' 127.0.0.1 ', 'user' = > 'root' and 'password' = > 'root', 'database' = > 'laravel58']); $result = $mysql->query('select sleep(3)'); $channel->push($result); }); go(function () use ($channel) { var_dump(time()); $redis1 = new Swoole\Coroutine\Redis(); $redis1 - > connect (127.0.0.1, 6379); $result = $redis1->set('hello', 'world'); $channel->push($result); }); go(function () use ($channel) { var_dump(time()); $redis2 = new Swoole\Coroutine\Redis(); $redis2 - > connect (127.0.0.1, 6379); $result = $redis2->get('hello'); $channel->push($result); }); $results = []; for ($i = 0; $i < 3; $i++) { $results[] = $channel->pop(); } $response->end(json_encode([ 'data' => $results, 'time' => time() ])); }); $server->start();Copy the code

We rewrote the MySQL and Redis client connection request calls to be implemented through three subcoroutines, and removed the setDefer setting, because these three subcoroutines are already called concurrently. In addition, since the data between the three subcoroutines is isolated from each other, So we implement data sharing and communication between coroutines through Swoole\Coroutine\Channel, initialize its buffer space to 3, then introduce it into subcoroutines through use method, and put the response result into Channel through push method. Next, at the end of the onRequest callback function on the server side, the data in the Channel is taken out and put into the array $Results through a loop through the POP method. Finally, the result is returned to the client in JSON format through the $response->end() method.

We save the above code to coroutine/http2.php and start the new HTTP server at the terminal with the following command:

php coroutine/http2.php
Copy the code

Request the server again in Postman, change the response format to JSON, and see the following result:

! [](https://pic4.zhimg.com/80/v2-ede34acb821581954c02ea7a0c243f5b_720w.jpg)

MySQL requests take the longest time to execute, so they are at the back. In the terminal that started the server, you can see the three client request times printed out, which are exactly the same, indicating that they are executed concurrently:

! [](https://picb.zhimg.com/80/v2-932b587e6b1a1136c5c7743f9ca81906_720w.jpg)

Obviously, through the subcoroutine + channel can also be very convenient to achieve Redis, MySQL connection pool.

I hope the above content can help you. Many PHPer will encounter some problems and bottlenecks when they are advanced, and they have no sense of direction when writing too many business codes. I have sorted out some information, including but not limited to: Distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, Laravel, YII2, Redis, Swoft, Kafka, Mysql optimization, shell scripting, Docker, microservices, Nginx, etc. Many knowledge points can be free to share with you