swoole 2.0正式版发布了。2.0版本最大的更新是增加了对协程(coroutine)的支持。正式版已同时支持php5和php7。基于swoole2.0协程php开发者可以已同步的方式编写代码,底层自动进行协程调度,转变为异步io。解决了传统异步编程嵌套回调的问题。
目前swoole底层内置的协程客户端组件包括:udpclient、tcpclient、httpclient、redisclient、mysqlclient,基本涵盖了开发者常用的几种通信协议。协程组件只能在服务器的onconnect、onrequest、onreceive、onmessage 回调函数中使用。
注意,swoole 2.0.5以前的版本还是灰度测试版本,可能会存在问题。 beta是因为协程是全新的版本。
咱们就以上面的示例代码为例,说一说协程的执行流程。
http server监听9051端口。当有相关事件发生时,如有数据到达,就会执行绑定到request上的回调函数。在执行回调函数之前,会创建一个协程。这时,会保存cpu寄存器的状态和zendvm stack信息。
在回调函数执行过程中,如果遇到io操作,如<code>$tcp_cli->connect(</code>,就会保存当前的状态,并让出cpu使用权。当前请求执行被挂起。
让cpu出使用权后,cpu就可以用于处理其他事件。如处理其他客户端的request请求。
当被挂起的请求,又有新的事件发生,如上面<code>$tcp_cli->connect()</code>的数据已经返回。这时,会使用挂起前保存的状态信息恢复,然后继续执行回调函数。
如果在执行过程中,再次遇到io操作,会继续执行保存状态和让出cpu使用权。
这些io操作都是非阻塞的,即发送请求和获取数据分为两步。当请求发送完毕后,就会进行状态保存和让出cpu使用权。在等待请求数据返的这段时间,cpu可以执行一些其他程序。这样就可以充分利用cpu。
swoole的协程是基于 setjmp 、 longjmp 实现的。swoole为每个协程都分配了空间,用于保存协程切换时的状态信息。进行协程切换时会自动保存zend vm的内存状态(主要是eg全局内存和vm stack)。当回调函数执行完毕后,会自动销毁分配的空间。
什么时候会创建协程?在server的onconnect、onrequest、onreceive、onmessage 回调函数被执行前会创建一个协程。
协程创建的方法是<code>coro_create</code>。相关源码可以查看<code>swoole_coroutine.c</code>文件。
<code>coro_create</code>方法中主要进行了如下操作:
什么时候会让出cpu执行权?当回调函数中遇到异步io的时候,会让出cpu执行权。如,代码中的connect操作。下面,我们就以connect操作为例,看看让出cpu执行权时都做了那些操作。
connect的相关代码在<code>swoole_coroutine.c</code>文件中。代码如下:
所谓的协程信息主要就是当前的上下文执行信息。<code>coro_save</code>方法在<code>swoole_coroutine.c</code>文件中。代码如下:
<code>coro_yield</code>方法的作用是让出cpu执行权。代码在<code>swoole_coroutine.c</code>文件中。
在这个方法中主要进行了还原栈信息和longjump操作。
corog.origin_vm_stack 这些栈信息的初始化在<code>coro_init</code>方法中。记录了协程执行前的状态。
当异步io有数据返回后,会进行协程恢复。协程恢复的方法是<code>coro_resume</code>。在<code>swoole_coroutine.c</code>文件中。代码如下:
可见,创建协程和恢复协程的整体代码结构差不多。
当回到函数执行完毕后,会结束协程。
<code>coro_close</code>方法用于结束协程。源码在<code>swoole_coroutine.c</code>文件中。