这节来学习Swoole最基础的Server和Client。会通过创建一个tcp Server来讲解。
server
<?php
class Server { private $serv; public function __construct() { $this->serv = new Swoole\Server('127.0.0.1', 9501);
我们启动服务端server:
$ php server.php
0
我们来分析整个server 启动的步骤:
- 启动php server.php后,当前进程fork出Master进程,然后退出。
- Master进程启动成功之后,fork出Manager进程,并触发OnManagerStart事件。
- Manager进程启动成功时候,fork出Worker进程,并触发OnWorkerStart事件。
同步client
server端好了,那么就会需要client端来连接,swoole里面client分为同步和异步,先来一个同步clent客户端。
<?php
同步client是同步阻塞的。一整套connect->send()->rev()->close()是同步进行的。
所以,如果是大量的循环数据,就不适合同步client了:
比如下面:
<?php
打印的结果就是顺序执行。要是想要异步了。
异步client
<?php
这样就是一个异步的client了,处理更快,但是只支持php的cli模式。
server与client交互
总结一下client与server的连接过程:
- Client主动Connect的时候,Client实际上是与Master进程中的某个Reactor线程发生了连接。
- 当TCP的三次握手成功了以后,由这个Reactor线程将连接成功的消息告诉Manager进程,再由Manager进程转交给Worker进程。
- 在这个Worker进程中触发了OnConnect的方法。
- 当Client向Server发送了一个数据包的时候,首先收到数据包的是Reactor线程,同时Reactor线程会完成组包,再将组好的包交给Manager进程,由Manager进程转交给Worker。
- 此时Worker进程触发OnReceive事件。
- 如果在Worker进程中做了什么处理,然后再用Send方法将数据发回给客户端时,数据则会沿着这个路径逆流而上。
关于上面说到的几个进程,解释下:
Master进程是一个多线程进程,其中有一组非常重要的线程,叫做Reactor线程(组),每当一个客户端连接上服务器的时候,都会由Master进程从已有的Reactor线程中,根据一定规则挑选一个,专门负责向这个客户端提供维持链接、处理网络IO与收发数据等服务。
而Manager进程,某种意义上可以看做一个代理层,它本身并不直接处理业务,其主要工作是将Master进程中收到的数据转交给Worker进程,或者将Worker进程中希望发给客户端的数据转交给Master进程进行发送。另外,Manager进程还负责监控Worker进程,如果Worker进程因为某些意外挂了,Manager进程会重新拉起新的Worker进程,有点像Supervisor的工作。
Worker进程了,顾名思义,Worker进程其实就是处理各种业务工作的进程,Manager将数据包转交给Worker进程,然后Worker进程进行具体的处理,并根据实际情况将结果反馈给客户端。
task_worker
在swoole中work进程分为EventWorker和TaskWorker,对应的配置文件设置为:
$this->serv->set([
'worker_num' => 10,
worker是基于event触发,而task则是manager直接生成的子进程。那么他们有什么区别呢?
共同点是:他们都是最底层负责处理业务的进程。
Swoole的业务逻辑部分是同步阻塞运行的,如果遇到一些耗时较大的操作,例如访问数据库、广播消息等,就会影响服务器的响应速度。因此Swoole提供了Task功能,将这些耗时操作放到另外的进程去处理,当前woker进程继续执行后面的逻辑。运行Task,需要在swoole服务中配置参数task_worker_num,即可开启task功能。此外,必须给swoole_server绑定两个回调函数:onTask和onFinish。这两个回调函数分别用于执行Task任务和处理Task任务的返回结果。
先来写一个demo,来如何用 taskWoker来处理业务。
taskServer.php
<?php
taskClient.php 异步的客户端
运行一下:
php taskServer.php
php taskClient.php
服务端打印:
$ php task_server.php
1.9.0 onStart
1Client Connect. Get Message From Client 1:12345 Continue Handle Worker This Task 0 from Worker 3 Data: {"fd":1} Task 0 Handle 0 times... Task 0 Handle 1 times... Task 0 finish Result: Task 0's result
客户端打印:
$ php task_client.php
Enter Msg:12345 Enter Msg:Received: Data in Task 0
这里面有几点需要注意:
1. 运行Task,必须要在swoole服务中配置参数task_worker_num,此外,必须给swoole_server绑定两个回调函数:onTask和onFinish。
2. onTash 要return 数据
3. onFinish 会接收到onTash的数据,标记成完成。
4. swoole_event_add 把输入绑定成事件,这个后续将,这样client就可以连续的多次输入。
swoole的架构
上面说了这么,图表总结一下swoole结构:
swoole采用 多线程Reactor+多进程Worker
![swoole架构]()
swoole的处理连接流程图如下:
![swoole处理流程图]()
当请求到达时,swoole是这样处理的:
请求到达 Main Reactor
|
|
Main Reactor根据Reactor的情况,将请求注册给对应的Reactor
(每个Reactor都有epoll。用来监听客户端的变化)
|
|
客户端有变化时,交给worker来处理
|
|
worker处理完毕,通过进程间通信(比如管道、共享内存、消息队列)发给对应的reactor。
|
|
reactor将响应结果发给相应的连接
|
|
请求处理完成
因为reactor基于epoll,所以每个reactor可以处理无数个连接请求。 如此,swoole就轻松的处理了高并发。
参考资料:
http://rango.swoole.com/archives/305
https://github.com/szyhf/swoole_study/blob/master/Swoole%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%A8%A1%E5%9E%8B.md
http://happyliu.blog.51cto.com/501986/1574923
https://segmentfault.com/a/1190000007614502