Join 4,000+ others and never miss out on new tips, tutorials, and more.
4.x is outdated, please check the latest version 22.x
Latest version:
pecl install openswoole-22.1.2
Version: PHP: 7.1+ and Swoole: 4.4.0+
A coroutine can be simply understood as a thread, but this thread is in user mode and does not require the participation of the operating system. The cost of creating, destroying and switching is very low. Unlike threads, the coroutine cannot use multiple CPU core because it operates in user space but Swoole provides a multi-process model to tackle this limitation.
Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing certain code to be suspended and resumed, this action mostly happens when the system is busy waiting for I/O operations to complete, like a database query waiting to return a result, instead of waiting for the result and doing nothing the program is freed up to do something else until the result is available, achieving concurrency.
Open Swoole server creates one coroutine for each request to the server and switches coroutines based on I/O status automatically, this happens within the coroutine schedular.
The advantages of Swoole Coroutines are:
Swoole\Coroutine::create
Swoole\Coroutine::set
Swoole\Coroutine::dnsLookup
Swoole\Coroutine::getCid
Swoole\Coroutine::getPcid
Swoole\Coroutine::exists
Swoole\Coroutine::getElapsed
Swoole\Coroutine::getContext
Swoole\Coroutine::enableScheduler
Swoole\Coroutine::disableScheduler
Swoole\Coroutine::yield
Swoole\Coroutine::resume
Swoole\Coroutine::defer
Swoole\Coroutine::suspend
Swoole\Coroutine::sleep
Swoole\Coroutine::gethostbyname
Swoole\Coroutine::getaddrinfo
Swoole\Coroutine::exec
Swoole\Coroutine::readFile
Swoole\Coroutine::writeFile
Swoole\Coroutine::stats
Swoole\Coroutine::getBackTrace
Swoole\Coroutine::list
Swoole\Coroutine::enableCoroutine
Swoole\Coroutine::cancel
Swoole\Coroutine::isCanceled
Swoole\Coroutine::select
Swoole\Coroutine\map
<?php
Co\run(function()
{
go(function()
{
Co::sleep(1);
echo "Done 1\n";
});
go(function()
{
Co::sleep(1);
echo "Done 2\n";
});
});
The above example shows two coroutines being created using the go
function, inside each go
call is a coroutine which sleeps for 1 second and outputs text to indicate they have finished, because coroutines switch context based on I/O or when a program is busy, other coroutines can execute while another is waiting or is busy, the example above is using the sleep call to simulate a busy coroutine.
Coroutines must be executed within a coroutine context, a context is just a wrapped call using Co\run
and is required because it allows Swoole to run your coroutine under the internal coroutine scheduler.
A coroutine context is created with the callback function: request
, receive
, connect
in a Swoole\Server
or Swoole\HTTP\Server
for you, so you can start using coroutines straight away within those callbacks.
But ou can also create a coroutine context with Co\run yourself if you are not using a Swoole server.
You cannot nest any Co\run
calls because the coroutine scheduler will have already been started and you cannot run two inside of each other, you must create another context instead.
Coroutines have access to the same memory space, they can conflict with modifying memory which could be dependant by another coroutine as they are running within user space, on the same thread.
To solve the problem of conflicting memory access we have channels, they are used for the communication between coroutines.
In short, a channel is a non-blocking primitive for sending and receiving array like data between 2 or more coroutines, please visit the channels documentation for more information.
Swoole adds support for a range of different coroutine clients where they create a coroutine context for you.
You can use the following coroutine clients within a coroutine context:
Open Swoole provides a runtime hook which allows application code to run synchronously but all underlying I/O operations are run asynchronously. Swoole does this by hooking (or taking control) of native PHP functions and running them using coroutines to achieve concurrent execution.
This can have a major benefit on performance because most I/O is slow and by using the runtime hook you can allow other coroutines to operate while waiting for I/O operations to complete.
To learn more take a look at the Swoole Runtime Hook Flags.
Here is a quick list of supported PHP libraries:
And many more are supported. To learn more, visit the Runtime Hook Flag documentation.
When using coroutines you can set different configuration options to indicate how Swoole should operate, one of the most important configurations is the maximin number Coroutine that can be created at any given time:
The max number (max_coroutine
) of coroutines that can be created by the Swoole server, if this limit is reached new coroutines cannot be created and a 503
response error is returned when using the Swoole HTTP Server. The default value is 3000.
For more configuration options for coroutines, see the Swoole\Coroutine::set method.
$_
global variables may change during coroutine switching because they use the same thread/address spaceWeb applications or servers contain a lot of different functionality and usually are responsible for performing many different tasks, they handle requests which itself is like a small program until it sends back a response.
During the time one request is handled it would be beneficial if the server could also handle another request at the same time, concurrently. Allowing progress to be made on both requests simultaneously based on when one or another is waiting for I/O operations or busy/idle. This speeds up requests dramatically and allows the developer to still write synchronous code. There is no point in waiting around for I/O to be returned when we can do something else during that time.
Coroutines enable higher throughput because they allow for concurrent connections and don't waste time when a coroutine is busy/idle.
Think about the situation when you want to get some data from both a Redis Server and a MySQL server.
The normal process is doing them one after another but with coroutines we can perform each task simultaneously. By using coroutines to get data from a Redis and MySQL server we can reduce the latency because while one is busy waiting for I/O the other can start its request to query the MySQL server, operating concurrently and achieving super efficiently on the server.