swoole 学习笔记

写的有点乱, 仅供本人参考
官方手册: 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步

  1. onStart
  2. onWorkStart
  3. onReceive
  4. onWorkerStop (worker退出时会回调此函数)
  5. onShutDown (swoole服务停止会回调此函数)

参考: https://blog.csdn.net/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()

  • 平滑重启只对 onWorkerStartonReceive 等在 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,
));





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章