swoole是一个php扩展,提供了php语言的异步多线程服务器,异步tcp/udp网络客户端,异步mysql,异步redis,数据库连接池,asynctask,消息队列,毫秒定时器,异步文件读写,异步dns查询。 swoole内置了http/websocket服务器端/客户端、http2.0服务器端。
swoole: php的异步、并行、高性能网络通信引擎
<a href="http://www.swoole.com/">http://www.swoole.com/</a>
github:
<a href="https://github.com/swoole">https://github.com/swoole</a>
<a href="https://github.com/matyhtf">https://github.com/matyhtf</a>
swoole需要使用源码安装。暂无windows版扩展。
由于pecl是需要编译的,所以需要先安装编译器(已安装编译器可以忽略):
然后:
pecl安装扩展完成后会提示添加so文件到php.ini。示例:
添加示例:
建议使用的版本(截止时间2017-6-3)
<code>1.9.x</code> 分支已进入特性锁定期,不再开发新功能,仅修复bug。
最低版本:
建议<code>1.8.6+</code>。php7建议使用<code>1.9.2+</code>。
建议使用的php版本
使用<code>tp3.1+</code>框架的朋友升级到<code>php7.1.0</code>可能会出现rewrite失效问题。建议<code>php7.0.x</code>系列。
快速查看当前swoole的版本:
我们来使用实例进行分析:
继续在shell中输入以下命令:
从系统的输出中,我们可以很容看出server其实有3个进程,进程的pid分别是2454、2456、2458,其中2454是2456的父进程,而2456又是2458的父进程。
所以,其实我们虽然看起来只是启动了一个server,其实最后产生的是三个进程。
这三个进程中,所有进程的根进程(2454),就是所谓的<code>master</code>进程;而2456进程,则是<code>manager</code>进程;最后的2458进程,是<code>worker</code>进程。
基于此,我们简单梳理一下,当执行的start方法之后,发生了什么:
守护进程模式下,当前进程fork出master进程,然后退出,master进程触发onmasterstart事件。
master进程启动成功之后,fork出manager进程,并触发onmanagerstart事件。
manager进程启动成功时候,fork出worker进程,并触发onworkerstart事件。
非守护进程模式下,则当前进程直接作为master进程工作。
所以,一个最基础的swoole server,至少需要有3个进程,分别是master进程、manager进程和worker进程。
事实上,一个多进程模式下的swoole server中,有且只有一个master进程;有且只有一个manager进程;却可以有n个worker进程。
<code>master</code>进程是一个多线程进程,其中有一组非常重要的线程,叫做<code>reactor</code>线程(组),每当一个客户端连接上服务器的时候,都会由master进程从已有的reactor线程中,根据一定规则挑选一个,专门负责向这个客户端提供维持链接、处理网络io与收发数据等服务。分包拆包等功能也是在这里完成。
<code>manager</code>进程,某种意义上可以看做一个代理层,它本身并不直接处理业务,其主要工作是将master进程中收到的数据转交给worker进程,或者将worker进程中希望发给客户端的数据转交给master进程进行发送。
<code>manager</code>进程还负责监控worker进程,如果worker进程因为某些意外挂了,manager进程会重新拉起新的worker进程,有点像supervisor的工作。而这个特性,也是最终实现热重载的核心机制。
<code>worker</code>进程其实就是处理各种业务工作的进程,manager将数据包转交给worker进程,然后worker进程进行具体的处理,并根据实际情况将结果反馈给客户端。
我们可以总结出来上面简单的server,当客户端连接的时候这个过程中,三种进程之间是怎么协作的:
client主动connect的时候,client实际上是与master进程中的某个reactor线程发生了连接。
当tcp的三次握手成功了以后,由这个reactor线程将连接成功的消息告诉manager进程,再由manager进程转交给worker进程。
在这个worker进程中触发了onconnect的方法。
当client向server发送了一个数据包的时候,首先收到数据包的是reactor线程,同时reactor线程会完成组包,再将组好的包交给manager进程,由manager进程转交给worker。
此时worker进程触发onreceive事件。
如果在worker进程中做了什么处理,然后再用send方法将数据发回给客户端时,数据则会沿着这个路径逆流而上。
swoole进程/线程结构图:

现在,我们基于上面的例子修改代码,来看看一个简单的多进程swoole server的几个基本配置:
<code>reactor_num</code>:表示master进程中,reactor线程总共开多少个,注意,这个可不是越多越好,因为计算机的cpu是有限的,所以一般设置为与cpu核心数量相同,或者两倍即可。
<code>worker_num</code>:表示启动多少个worker进程,同样,worker进程数量不是越多越好,仍然设置为与cpu核心数量相同,或者两倍即可。
我们可以在shell里运行,使用pstree查看进程模型结构:
1) 进程启动时执行的:onstart、onmanagerstart、onworkerstart;onworkerstop、onmanagerstop、onshutdown;onworkererror
2) 客户端交互时触发的:onreceive/onrequest/onpacket/onmessage、onopen/onconnect、onclose
3) task:ontask、onfinish
4) timer:ontimer
事件执行顺序:
所有事件回调均在<code>$server->start</code>后发生
服务器关闭程序终止时最后一次事件是<code>onshutdown</code>
服务器启动成功后,<code>onstart/onmanagerstart/onworkerstart</code>会在不同的进程内并发执行。
<code>onreceive/onconnect/onclose/ontimer</code>在worker进程(包括task进程)中各自触发
worker/task进程启动/结束时会分别调<code>用onworkerstart/onworkerstop</code>
<code>ontask</code>事件仅在task进程中发生
onfinish事件仅在worker进程中发生
<code>onstart/onmanagerstart/onworkerstart</code> 3个事件的执行顺序是不确定的
udp协议下只有<code>onreceive</code>事件,没有<code>onconnect/onclose</code>事件
如果未设置<code>onpacket</code>回调函数,收到udp数据包默认会回调<code>onreceive</code>函数
<code>onopen</code>事件回调是可选的:当websocket客户端与服务器建立连接并完成握手后会回调此函数
实际使用的时候不是所有回调都可以使用的,例如udp服务器没有<code>onconnect/onclose</code>;例如接收数据,在websocket里使用onreceive,在httpserver使用onrequest,在udpserver使用onpacket。
示例:
不要在代码中执行<code>sleep</code>以及其他睡眠函数,这样会导致整个进程阻塞
<code>exit/die</code>是危险的,会导致worker进程退出
php代码中如果有异常抛出,必须在回调函数中进行<code>try/catch</code>捕获异常,否则会导致工作进程退出
swoole不支持<code>set_exception_handler</code>,必须使用<code>try/catch</code>方式处理异常
不能使用类的属性保存客户端连接信息,因为一个worker进程可以处理多个客户端连接,导致类属性数据错乱。常量则是可以的。