<返回更多

Swoole简介

2019-11-15    
加入收藏

本文示例代码详见:https://github.com/52fhy/swoole_demo。

简介

Swoole是一个php扩展,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。

Swoole: PHP的异步、并行、高性能网络通信引擎
http://www.swoole.com/

Github:
https://github.com/swoole
https://github.com/matyhtf

安装

Swoole需要使用源码安装。暂无windows版扩展。

源码安装

wget -O swoole.zip https://github.com/swoole/swoole-src/archive/v1.9.11.zip
unzip swoole.zip
cd swoole
phpize
./configure
make && make install

pecl安装

由于pecl是需要编译的,所以需要先安装编译器(已安装编译器可以忽略):

yum install -y gcc gcc-c++ make cmake bison autoconf

然后:

pecl install swoole
pecl install redis

pecl安装扩展完成后会提示添加so文件到php.ini。示例:

Build process completed successfully
Installing '/usr/lib64/php/modules/swoole.so'
install ok: channel://pecl.php.net/swoole-1.9.11
configuration option "php_ini" is not set to php.ini location
You should add "extension=swoole.so" to php.ini

添加示例:

[swoole]
extension = /usr/lib64/php/modules/swoole.so

Swoole的进程模型

运行模式

Swoole目前总共有三种运行模式,默认为多进程模式(SWOOLE_PROCESS)。

稳定版:v1.9.9
预览版:v2.0.7
$serv = new swoole_server(string $host, int $port, int $mode = SWOOLE_PROCESS, int $sock_type = SWOOLE_SOCK_TCP);

实例分析

我们来使用实例进行分析:

<?php
$server = new swoole_server("127.0.0.1",8088);//默认是多进程模式、TCP类型

$server->on('connect', function ($serv, $fd){ });
$server->on('receive', function ($serv, $fd, $from_id, $data){ });
$server->on('close', function ($serv, $fd){ });

$server -> start();

继续在Shell中输入以下命令:

php swoole_tcp_server.php
pstree -ap|grep swoole_tcp_server
 | | `-php,2454 swoole_tcp_server.php
 | | |-php,2456 swoole_tcp_server.php
 | | | `-php,2458 swoole_tcp_server.php

从系统的输出中,我们可以很容看出server其实有3个进程,进程的pid分别是2454、2456、2458,其中2454是2456的父进程,而2456又是2458的父进程。

所以,其实我们虽然看起来只是启动了一个Server,其实最后产生的是三个进程。

这三个进程中,所有进程的根进程(2454),就是所谓的Master进程;而2456进程,则是Manager进程;最后的2458进程,是Worker进程。

基于此,我们简单梳理一下,当执行的start方法之后,发生了什么:

非守护进程模式下,则当前进程直接作为Master进程工作。

所以,一个最基础的Swoole Server,至少需要有3个进程,分别是Master进程、Manager进程和Worker进程。

事实上,一个多进程模式下的Swoole Server中,有且只有一个Master进程;有且只有一个Manager进程;却可以有n个Worker进程。

进程模型

Master进程是一个多线程进程,其中有一组非常重要的线程,叫做Reactor线程(组),每当一个客户端连接上服务器的时候,都会由Master进程从已有的Reactor线程中,根据一定规则挑选一个,专门负责向这个客户端提供维持链接、处理网络IO与收发数据等服务。分包拆包等功能也是在这里完成。

Manager进程,某种意义上可以看做一个代理层,它本身并不直接处理业务,其主要工作是将Master进程中收到的数据转交给Worker进程,或者将Worker进程中希望发给客户端的数据转交给Master进程进行发送。

Manager进程还负责监控Worker进程,如果Worker进程因为某些意外挂了,Manager进程会重新拉起新的Worker进程,有点像Supervisor的工作。而这个特性,也是最终实现热重载的核心机制。

Worker进程其实就是处理各种业务工作的进程,Manager将数据包转交给Worker进程,然后Worker进程进行具体的处理,并根据实际情况将结果反馈给客户端。

我们可以总结出来上面简单的Server,当客户端连接的时候这个过程中,三种进程之间是怎么协作的:

  1. Client主动Connect的时候,Client实际上是与Master进程中的某个Reactor线程发生了连接。
  2. 当TCP的三次握手成功了以后,由这个Reactor线程将连接成功的消息告诉Manager进程,再由Manager进程转交给Worker进程。
  3. 在这个Worker进程中触发了OnConnect的方法。
  4. 当Client向Server发送了一个数据包的时候,首先收到数据包的是Reactor线程,同时Reactor线程会完成组包,再将组好的包交给Manager进程,由Manager进程转交给Worker。
  5. 此时Worker进程触发OnReceive事件。
  6. 如果在Worker进程中做了什么处理,然后再用Send方法将数据发回给客户端时,数据则会沿着这个路径逆流而上。

Swoole进程/线程结构图:

Swoole简介

 

现在,我们基于上面的例子修改代码,来看看一个简单的多进程Swoole Server的几个基本配置:

PHP5.5或更高版本
PHP7.0.13或更高版本

reactor_num:表示Master进程中,Reactor线程总共开多少个,注意,这个可不是越多越好,因为计算机的CPU是有限的,所以一般设置为与CPU核心数量相同,或者两倍即可。

worker_num:表示启动多少个Worker进程,同样,Worker进程数量不是越多越好,仍然设置为与CPU核心数量相同,或者两倍即可。

我们可以在Shell里运行,使用pstree查看进程模型结构:

php swoole_tcp_server.php
pstree -ap|grep swoole_tcp
 | | `-php,2505 swoole_tcp_server.php
 | | |-php,2507 swoole_tcp_server.php
 | | | |-php,2510 swoole_tcp_server.php
 | | | |-php,2511 swoole_tcp_server.php
 | | | |-php,2512 swoole_tcp_server.php
 | | | `-php,2513 swoole_tcp_server.php

回调函数

Swoole作为Server时,回调函数有很多。但可以简单分个类:
1) 进程启动时执行的:onStart、onManagerStart、onWorkerStart;onWorkerStop、onManagerStop、onShutdown;onWorkerError
2) 客户端交互时触发的:onReceive/onRequest/onPacket/onMessage、onOpen/onConnect、onClose
3) Task:onTask、onFinish
4) Timer:onTimer

事件执行顺序:

实际使用的时候不是所有回调都可以使用的,例如UDP服务器没有onConnect/onClose;例如接收数据,在WebSocket里使用onReceive,在HttpServer使用onRequest,在UDPServer使用onPacket。

示例:

<?php
$server = new swoole_server("127.0.0.1",8088);

$server->set(array(
 'daemonize' => false,
 'reactor_num' => 2,
 'worker_num' => 4
));

$server->on('connect', function ($serv, $fd){ 
 echo "client connect. fd is {$fd}n";
});

$server->on('receive', function ($serv, $fd, $from_id, $data){
 echo "client connect. fd is {$fd}n";
});

$server->on('close', function ($serv, $fd){
 echo "client close. fd is {$fd}n";
});

// 以下回调发生在Master进程
$server->on("start", function (swoole_server $server){
 echo "On master start.n";
});
$server->on('shutdown', function (swoole_server $server){
 echo "On master shutdown.n";
});

// 以下回调发生在Manager进程
$server->on('ManagerStart', function (swoole_server $server){
 echo "On manager start.n";
});
$server->on('ManagerStop', function (swoole_server $server){
 echo "On manager stop.n";
});

// 以下回调也发生在Worker进程
$server->on('WorkerStart', function (swoole_server $server, $worker_id){
 echo "Worker startn";
});
$server->on('WorkerStop', function(swoole_server $server, $worker_id){
 echo "Worker stopn";
});
$server->on('WorkerError', function(swoole_server $server, $worker_id, $worker_pid, $exit_code){
 echo "Worker errorn";
});

$server -> start();

编程须知

(本文完) 
本文优先在公众号"飞鸿影的博客(fhyblog)"发布,欢迎关注公众号及时获取最新文章推送!

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>