写的有点乱, 仅供本人参考
官方手册: https://wiki.swoole.com/
协程
,是为了提高并发的,如果我的应用就没有高并发,或者必须要用某些无法异步化 IO 的操作 (例如上文的 MongoDB),那么你完全可以不开启一键协程化,关闭 enable_coroutine
,多开一些 Worker
进程,这就是和 Fpm/Apache
是一样的模型了,值得一提的是由于 Swoole 是常驻进程的,即使同步 IO 性能也会有很大提升,实际应用中也有很多公司这样做。
PHP-FPM vs Swoole
https://learnku.com/articles/9450/php-fpm-vs-swoole
MINIT -> RINIT -> 执行PHP脚本 -> RSHUTDOWN -> MSHUTDOWN
PHP-FPM
每个请求都是在执行2~4
步, 只有停止PHP-FPM, 才会执行MSHUTDOWN
swoole
在第3
步后进入swoole的生命周期, 只有停止swoole服务, 才会退出然后继续php的4, 5步
- onStart
- onWorkStart
- onReceive
- onWorkerStop (worker退出时会回调此函数)
- onShutDown (swoole服务停止会回调此函数)
参考: https://blog.youkuaiyun.com/sb___itfk/article/details/52957539
Server中对象的4层生命周期 (重要)
https://wiki.swoole.com/wiki/page/354.html
程序全局期
在Server->start
之前就创建好的对象,在Worker
进程内对这些对象进行写操作时,会自动从共享内存中分离,变为进程全局对象
程序全局期include/require
的代码,必须在整个程序shutdown
时才会释放,reload
无效
进程全局期
Worker
进程启动后创建的对象(onWorkerStart
中创建的对象),在这个子进程存活周期之内,是常驻内存的,onConnect/onReceive/onClose
中都可以去访问它
每个Worker
子进程处理的请求数超过max_request
配置后,就会自动销毁
进程全局对象所占用的内存是在当前子进程内存堆的,并非共享内存,对此对象的修改仅在当前Worker
进程中有效
进程期include/require
的文件,在reload
后就会重新加载
会话期
会话期是在onConnect
后创建,或者在第一次onReceive/onRequest
时创建,onClose
时销毁
请求期
指一个完整的请求发来,也就是onReceive/onRequest
收到请求开始处理,直到返回结果发送response
请求期对象与普通PHP程序中的对象就是一样的。请求到来时创建,请求结束后销毁
Server内存管理机制
https://wiki.swoole.com/wiki/page/p-zend_mm.html
Master 进程、Reactor 线程、Worker 进程、Task 进程、Manager 进程的区别与联系
https://wiki.swoole.com/#/learn?id=diff-process
Server 的两种运行模式介绍 (SWOOLE_BASE 和 SWOOLE_PROCESS)
https://wiki.swoole.com/#/learn?id=server的两种运行模式介绍
SWOOLE_PROCESS
如最下图的模型 有Master
进程、Reactor
线程、Worker
进程、Task
进程、Manager
进程
SWOOLE_BASE
没有Master
进程,如nginx
一样,连接请求进来的时候,所有的 Worker
进程去争抢这一个连接
当设置 worker_num=1
时,没有Manager
进程
Swoole 如何正确的重启服务
https://wiki.swoole.com/#/question/use?id=swoole如何正确的重启服务
$server->reload()
- 平滑重启只对
onWorkerStart
或onReceive
等在 Worker 进程中include/require
的 PHP 文件有效 - Server 启动前就已经
include/require
的 PHP 文件,不能通过平滑重启重新加载 - 对于 Server 的配置即
$serv->set()
中传入的参数设置,必须关闭 / 重启整个 Server 才可以重新加载 - Server 可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有 Worker 进程
全局变量
协程使得原有的异步逻辑同步化,但是在协程的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及 static 变量的一致性。
在 PHP-FPM
下可以通过全局变量获取到请求的参数,服务器的参数等,在 Swoole 内,无法 通过 $_GET/$_POST/$_REQUEST/$_SESSION/$_COOKIE/$_SERVER
等 $_
开头的变量获取到任何属性参数。
可以使用 context
用协程 id 做隔离,实现全局变量的隔离。
是否可以共用 1 个 Redis 或 MySQL 连接
绝对不可以。必须每个进程单独创建 Redis、MySQL、PDO 连接,其他的存储客户端同样也是如此。
原因是如果共用 1 个连接,那么返回的结果无法保证被哪个进程处理,持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。
创建连接的相关代码可以放到 onWorkerStart
回调函数中
swoole 协程 与 go 协程 区别
https://wiki.swoole.com/wiki/page/p-differences_with_go.html
- Swoole的协程调度器是单线程的,因此不存在数据同步问题,同一时间只会有一个协程在运行
- Go协程调度器是多线程的,同一时间可能会有多个协程同时执行
- Swoole协程中操作全局变量是不需要加锁,Go是多线程需加锁避免数据错乱
swoole多进程间通信需要 Swoole\Table
(共享内存) 或 Swoole\Atomic
(无锁计算器), 加锁用 Swoole\Lock
(进程间锁)
配置 (Server::set())
https://wiki.swoole.com/#/server/setting
worker_num
设置启动的 Worker
进程数。【默认值:CPU 核数】
心跳检测1 (主动
发送心跳包)
open_tcp_keepalive
$server->set(array(
'open_tcp_keepalive' => 1,
'tcp_keepidle' => 4, //4s没有数据传输就进行检测
'tcp_keepinterval' => 1, //1s探测一次
'tcp_keepcount' => 5, //探测的次数,超过5次后还没回包close此连接
));
心跳检测2 (仅支持TCP连接
) 被动
等待数据包
heartbeat_check_interval
【默认值:false】
此选项表示每隔多久轮循一次,单位为秒,没有向服务器发送任何数据,此连接将被强制关闭。
heartbeat_idle_time
连接最大允许空闲的时间【未设置时默认为 heartbeat_check_interval
的两倍】
配合客户端使用 swoole_timer_tick
定时发送数据包
解决内存泄漏问题
max_request
设置 worker
进程的最大任务数。【默认值:0 即不会退出进程】
一个 worker
进程在处理完超过此数值的任务后将自动退出,进程退出后会释放所有内存和资源
TCP沾包问题
TCP 协议是流式的,数据包没有边界,并发高了就会有粘包问题
解决方案:
固定包头 + 包体协议
$server->set(array(
'open_length_check' => true,
'package_max_length' => 81920,
'package_length_type' => 'n', //see php pack()
'package_length_offset' => 0,
'package_body_offset' => 4,
));