phper成长之路:swoole学习笔记(一)
ccwgpt 2024-09-29 10:04 28 浏览 0 评论
简单点去理解,Swoole是PHP的一个扩展,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySql,Redis,数据库连接池,AsycTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。Swoole内置了Http/WebSocket服务器/客户端,Http2.0服务端。
Swoole需要源码安装,目前不支持Windows版本。
官网:
http://www.swoole.com/
Github:
https://github.com/swoole
https://github.com/matyhtf
源码安装:
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.so文件到php.ini
[swoole] extension = /usr/lib64/php/modules/swoole.so
版本选择:
Swoole版本:稳定版:V1.9.9,预览版:V2.0.7
PHP版本:5.5或更高,7.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);
实例分析
<?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方法之后,发生了什么:
- 守护进程模式下,当前进程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 Server的几个基本配置:
<?php
$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时,回调函数有很多。但可以简单分个类:
- 进程启动时执行的:onStart、onManagerStart、onWorkerStart;onWorkerStop、onManagerStop、onShutdown;onWorkerError
- 客户端交互时触发的:onReceive/onRequest/onPacket/onMessage、onOpen/onConnect、onClose
- Task:onTask、onFinish
- 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。
<?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 start\n"; });
$server->on('WorkerStop', function(\swoole_server $server, $worker_id){
echo "Worker stop\n"; });
$server->on('WorkerError', function(\swoole_server $server, $worker_id, $worker_pid, $exit_code){
echo "Worker error\n"; });
$server -> start();
- 不要在代码中执行sleep以及其他睡眠函数,这样会导致整个进程阻塞
- exit/die是危险的,会导致worker进程退出
- 可通过register_shutdown_function来捕获致命错误,在进程异常退出时做一些请求工作,具体参看/wiki/page/305.html
- PHP代码中如果有异常抛出,必须在回调函数中进行try/catch捕获异常,否则会导致工作进程退出
- swoole不支持set_exception_handler,必须使用try/catch方式处理异常
- Worker进程不得共用同一个Redis或MySQL等网络服务客户端,Redis/MySQL创建连接的相关代码可以放到onWorkerStart回调函数中。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理。持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。具体参考/wiki/page/325.html
- 不能使用类的属性保存客户端连接信息,因为一个worker进程可以处理多个客户端连接,导致类属性数据错乱。常量则是可以的。
相关推荐
- React 开发翻车现场!这 6 个救命技巧,90% 工程师居然现在才知道
-
前端圈最近都在卷React18新特性,可咱开发时踩的坑却一个比一个离谱!组件卡死、状态乱套、路由错乱...别担心!今天分享6个超实用的React实战技巧,让你轻松拿捏开发难题,代码直接...
- Web前端:React JS越来越受欢迎,它的主要优点为什么要使用它?
-
ReactJS是一个开源JavaScript库,用于为单页应用程序构建用户界面,它还为不同的移动应用程序提供视图层,并创建可重用的UI组件。 我们可以在Web应用程序的数据中创建特定的更改,而...
- 性能焦虑!前端人必看!5 个 React 组件优化神技! 颠覆你的认知!
-
在前端开发的赛道上,性能优化就像一场永不停歇的马拉松。作为前端工程师,你是否常常为React组件的性能问题头疼不已?页面加载缓慢、组件频繁重渲染,这些痛点分分钟让开发进度受阻。别担心!今天就来分享...
- React 实战必学!99% 工程师踩过的 5 大坑,3 招教你轻松破解
-
前端开发的小伙伴们,咱就是说,React现在可是前端界的“顶流明星”,热度一直居高不下!但用它开发项目的时候,是不是总有那么些瞬间,让你怀疑人生,对着屏幕疯狂抓头发?别慌!今天就给大家分享几个超实...
- 惬意!午间一道 React 题,轻松拿捏前端面试小技巧
-
忙碌了一上午,眼睛酸涩、脑子发懵?别急着刷短视频“放空”,不如花几分钟和我一起“品尝”一道React面试题小甜点!就像在阳光洒满窗台的午后,泡一杯热茶,惬意又能悄悄涨知识,何乐而不为?最近,...
- 一起深入盘点 2025 年 React 发展的 10个趋势?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.React服务器组件React服务...
- 前端掉坑血泪史!4 个 React 性能优化绝招让页面秒开
-
在前端圈子里摸爬滚打这么多年,我发现React开发时踩坑的经历大家都大同小异。页面加载慢、组件频繁重渲染、状态管理混乱……这些痛点,相信不少前端工程师都感同身受。别愁!今天就给大家分享4个超...
- 前端人崩溃瞬间!5 招 React 实战技巧让项目起死回生
-
有没有在写React项目时,遇到页面卡顿到怀疑人生、数据更新不及时、代码逻辑混乱到无从下手的情况?别慌!作为摸爬滚打多年的前端老炮,今天就把5个救命级的React实战技巧倾囊相授,帮你轻松...
- 8.3K star!React Bits,让你拥有全网几乎所有动画效果
-
前端开源项目101专栏:一个能让你更快接触到高质量开源项目的地方。我会探索分享精选101个高质量的开源项目。这是系列的第7篇文章,分享一套拥有计划全网所有动画效果,且创意最丰富的动画React组...
- 开始学习React - 概览和演示教程
-
#头条创作挑战赛#本文同步本人掘金平台的原创翻译:https://juejin.cn/post/6844903823085944846当我刚开始学习JavaScript的时候,我就听说了React,但...
- 阿里AI工具Web Dev上线!一句话生成React网页
-
5月11日,阿里巴巴推出全新AI工具“WebDev”,支持用户通过一句话指令生成网页应用。该工具集成HTML、CSS、JavaScript三大前端核心技术,并统一采用React框架实现,可在数秒内创...
- JS流行框架/库排名Top100,看看你熟知的Js排第几
-
权威的JavaScript趋势榜stats.js.org每15分钟根据github上的stars和forks总数实时汇总出JavaScript开源项目的流行度排名,一起来看看你所熟知的项目排名第几...
- 新手如何搭建个人网站
-
ElementUl是饿了么前端团队推出的桌面端UI框架,具有是简洁、直观、强悍和低学习成本等优势,非常适合初学者使用。因此,本次项目使用ElementUI框架来完成个人博客的主体开发,欢迎大家讨论...
- 站在巨人肩膀上的 .NET 通用权限开发框架:Admin.NET
-
站在巨人肩膀上的.NET通用权限开发框架Admin.NET是一个面向.NET程序员的低代码平台,java平台类似的框架有ruoyi,芋道,JeelowCode等。这类框架普遍采用前后端分离的开发技...
- Python+selenium自动化之判定元素是否存在
-
在测试过程中,我碰到过这类的问题,使用find_element却找不到某个元素而产生异常,这就需要在操作某个元素之前判定该元素是否存在,而selenium中没有判定元素是否存在的方法,或者判定相同的元...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- React 开发翻车现场!这 6 个救命技巧,90% 工程师居然现在才知道
- Web前端:React JS越来越受欢迎,它的主要优点为什么要使用它?
- 性能焦虑!前端人必看!5 个 React 组件优化神技! 颠覆你的认知!
- React 实战必学!99% 工程师踩过的 5 大坑,3 招教你轻松破解
- 惬意!午间一道 React 题,轻松拿捏前端面试小技巧
- 一起深入盘点 2025 年 React 发展的 10个趋势?
- 前端掉坑血泪史!4 个 React 性能优化绝招让页面秒开
- 前端人崩溃瞬间!5 招 React 实战技巧让项目起死回生
- 8.3K star!React Bits,让你拥有全网几乎所有动画效果
- 开始学习React - 概览和演示教程
- 标签列表
-
- MVC框架 (46)
- spring框架 (46)
- 框架图 (58)
- bootstrap框架 (43)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- laravel框架 (46)
- express框架 (43)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- grpc框架 (55)
- ppt框架 (48)
- 内联框架 (52)
- winform框架 (46)
- gui框架 (44)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)