资讯专栏INFORMATION COLUMN

Swoole笔记(一)

SHERlocked93 / 483人阅读

摘要:修复添加超过万个以上定时器时发生崩溃的问题增加模块,下高性能序列化库修复监听端口设置无效的问题等。线程来处理网络事件轮询,读取数据。当的三次握手成功了以后,由这个线程将连接成功的消息告诉进程,再由进程转交给进程。此时进程触发事件。

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

简介

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
版本选择

建议使用的版本(截止时间2017-6-3)

稳定版:v1.9.9
预览版:v2.0.7
1.9.x 分支已进入特性锁定期,不再开发新功能,仅修复BUG。

最低版本:
建议1.8.6+。PHP7建议使用1.9.2+

建议使用的PHP版本

PHP5.5或更高版本
PHP7.0.13或更高版本
使用TP3.1+框架的朋友升级到PHP7.1.0可能会出现rewrite失效问题。建议PHP7.0.x系列。

快速查看当前swoole的版本:

php --ri swoole

1.8.6~1.8.13 都是小范围BUG修复及功能新增。其中 1.8.11 增加SIGRTMIN信号处理函数,用于重新打开日志文件。

1.8.6 版本是一个重要的BUG修复版本,主要修复了PHP7环境下HttpServer、TCPClient、HttpClient、Redis等客户端存在的内存泄漏、崩溃问题。

1.9.0 版本增加了多项新特性,修复了多个已知问题。1.9版本是100%向下兼容1.8的,用户可无缝升级。

1.9.1 修复PHP7下启用opcache导致崩溃的问题;重构reopen log file特性,收到SIGRTMIN信号后重新打开日志文件并重定向标准输出 等。

1.9.2 修复PHP7下发生zend_mm_heap corrupted的问题 等。

1.9.4 修复WebSocket服务器默认onRequest方法内存泄漏问题 等。

1.9.5 增加pid_file选项,在Server启动时将主进程ID写入指定的文件 等。

1.9.6 修复添加超过1万个以上定时器时发生崩溃的问题;增加swoole_serialize模块,PHP7下高性能序列化库;修复监听UDP端口设置onPacket无效的问题 等。

1.9.9 修复Http2客户端POST数据时协议错误问题 等。

1.9.11 修复WebSocket服务器onOpen回调函数存在内存泄漏的问题;修复Http服务器文件上传在5.6版本发生崩溃的问题;优化添加Task和Timer的定时器性能,提升分支预测成功率 等。

Swoole的进程模型 运行模式

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

# Base模式(SWOOLE_BASE)
传统的异步非阻塞Server,reactor和worker是同一个角色。TCP连接是在worker进程中维持的。
如果客户端连接之间不需要交互,可以使用BASE模式。如Memcache、Http服务器等。

# 线程模式
多线程Worker模式,Reactor线程来处理网络事件轮询,读取数据。得到的请求交给Worker线程去处理。

缺点:一个线程发生内存错误,整个进程会全部结束。
由于PHP的ZendVM在多线程模式存在内存错误,多线程模式在v1.6.0版本后已关闭。

# 进程模式
与多线程Worker模式不同的是,线程换成了进程。Reactor线程来处理网络事件轮询,读取数据。得到的请求交给Worker进程去处理。适合业务逻辑非常复杂的场景。如WebSocket服务器等。
$serv = new swoole_server(string $host, int $port, int $mode = SWOOLE_PROCESS, int $sock_type = SWOOLE_SOCK_TCP);
实例分析

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

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方法之后,发生了什么:

守护进程模式下,当前进程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进程。

进程模型

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

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

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

Worker进程其实就是处理各种业务工作的进程,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的几个基本配置:

set(array(
    "demonize" => false,//是否后台运行
    "reactor_num" => 2,
    "worker_num" => 4
));

$server -> start();

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

事件执行顺序:

所有事件回调均在$server->start后发生

服务器关闭程序终止时最后一次事件是onShutdown

服务器启动成功后,onStart/onManagerStart/onWorkerStart会在不同的进程内并发执行。

onReceive/onConnect/onClose/onTimer在worker进程(包括task进程)中各自触发

worker/task进程启动/结束时会分别调用onWorkerStart/onWorkerStop

onTask事件仅在task进程中发生

onFinish事件仅在worker进程中发生

onStart/onManagerStart/onWorkerStart 3个事件的执行顺序是不确定的

UDP协议下只有onReceive事件,没有onConnect/onClose事件

如果未设置onPacket回调函数,收到UDP数据包默认会回调onReceive函数

onOpen事件回调是可选的:当WebSocket客户端与服务器建立连接并完成握手后会回调此函数

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

示例:

set(array(
    "daemonize" => false,
    "reactor_num" => 2,
    "worker_num" => 4
));

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

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

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

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

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

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

$server -> start();
编程须知

不要在代码中执行sleep以及其他睡眠函数,这样会导致整个进程阻塞

exit/die是危险的,会导致worker进程退出

可通过register_shutdown_function来捕获致命错误,在进程异常退出时做一些请求工作,具体参看/wiki/page/305.html

PHP代码中如果有异常抛出,必须在回调函数中进行try/catch捕获异常,否则会导致工作进程退出

swoole不支持set_exception_handler,必须使用try/catch方式处理异常

Worker进程不得共用同一个RedisMySQL等网络服务客户端,Redis/MySQL创建连接的相关代码可以放到onWorkerStart回调函数中。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理。持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。具体参考/wiki/page/325.html

不能使用类的属性保存客户端连接信息,因为一个worker进程可以处理多个客户端连接,导致类属性数据错乱。常量则是可以的。

欢迎关注公众号及时获取最新文章推送!


推荐!每月仅需$2.5,即可拥有配置SSD的VPS!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/23065.html

相关文章

  • swoole 学习笔记swoole 真的优秀个框架

    摘要:所以,我感觉学习,需要一个引导。学习参考链接喵星球特别厉害的一个官网的几个框架文档优势先看一下的结构设计的应用场景的设计项目中引入一个简易的具体参考一篇文章,集合和的简易实例项目中引入结合致敬 前期学完了操作系统,特别是知道了进程,线程 调度的问题。回来看,发现了很多细节。第三次看文档,最初经一个高手朋友的介绍,看swoole,说文档写的贼好。但是看的时候,也是很一头雾水,只知道把de...

    13651657101 评论0 收藏0
  • PHP多进程系列笔记(五)

    摘要:消息队列更常见的用途是主进程分配任务,子进程消费执行。子进程前面加了个,这是为了防止父进程还未往消息队列中加入内容直接退出。 前面几节都是讲解pcntl扩展实现的多进程程序。本节给大家介绍swoole扩展的swoole_process模块。 swoole多进程 swoole_process 是swoole提供的进程管理模块,用来替代PHP的pcntl扩展。 首先,确保安装的swoole...

    qianfeng 评论0 收藏0
  • swoole 学习笔记

    摘要:安装准备或更高版本或更高版本安装扩展配置支持编辑在其中加入扩展支持服务端代码服务端客户端代码测试代码在命令模式下输入重新再开一个窗口可以用检测如服务端客户端如上,表示服务端启动,客户端连接服务端返回消息,一个闭环形成开发者头条 1.安装准备 php-5.3.10 // 或更高版本 gcc-4.4 //或更高版本 make autoconf 2.安装swoole扩展 w...

    justCoding 评论0 收藏0
  • Swoole笔记(二)

    摘要:配置了参数后将会启用功能,务必要注册个事件回调函数。周期性定时器一次性定时器清除定时器定时器回调函数注意最大不得超过。进程中不能添加定时器。建议在回调里写定时器。定时器示例防止重复每隔触发一次后执行此函数 本文示例代码详见:https://github.com/52fhy/swoo...。 重新打开日志 在1.8.11及之后版本支持重新打开日志:向Server主进程发送SIGRTMIN...

    jsummer 评论0 收藏0
  • Swoole笔记(五)

    摘要:超过此数量后,新进入的连接将被拒绝。表示连接最大允许空闲的时间。当出错时底层会认为是恶意连接,丢弃数据并强制关闭连接。在启动时自动将进程的写入到文件,在关闭时自动删除文件。 配置说明 $server->set(array( daemonize => true, log_file => /www/log/swoole.log, reactor_num => 2, ...

    zgbgx 评论0 收藏0

发表评论

0条评论

SHERlocked93

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<