一些关于ThinkPhp 与 swoole 的事

下面写一些关于ThinkPhp 与 swoole 的事

1:TP跟swoole 可不可以结合?
答案是很明显的,网上应该也有很多整好的框架,我没有测试过,这里介绍我是怎么整合的

主要用到TP 的自定义命令行,不了解的可以看文档
文档:https://www.kancloud.cn/manual/thinkphp5/235129

简单说说

环境:首先安装好PHP7 &TP5以及swoole 拓展,这些不在这里讨论

然后
第一 :按照文档 配置好 command 类,并且在命令行测试通过

第二:在Command 类的execute 方法里创建swoole 服务

class SwooleTest extends Command
{

    protected function configure()
    {
        $this->setName('swooletest')->setDescription('Start SwooleTest WebSocket!');
    }

    protected function execute(Input $input, Output $output)
    {
        //创建websocket服务器对象,监听0.0.0.0:9502端口
        $ws = new \swoole_websocket_server("0.0.0.0", 9501);
        $ws->set(array(
            'worker_num' => 1,//处理客户端连接的线程数,如只有一条阻塞后所有操作都被阻塞
            'heartbeat_idle_time' => 30,
            'heartbeat_check_interval' => 20,
            'daemonize' => false, //是否作为守护进程,此配置一般配合log_file使用
            'max_request' => 1000,// 当某个work_task请求数(收到消息数)达到某数量时会关闭此线程并重新起一条线程,以防内存泄漏
            'log_file'    => './swoole.log',
            'log_level'=>3,
            'task_worker_num' => 2 //
        ));

        $ws->on('open', [$this, 'onOpen']);
        //$ws->on('handshake', [$this, 'onHandshake']); //握手处理
        $ws->on('message', [$this, 'onMessage']);
        $ws->on('close', [$this, 'onClose']);
        $ws->on('shutdown', [$this, 'onShutdown']);
        $ws->on('WorkerStart', [$this, 'onWorkerStart']);
        $ws->on("Task", array($this, 'onTask'));
        $ws->on("Finish", array($this, 'onFinish'));

        $ws->start();
    }

....

第三: 在linux 命令行运行 think php swooletest 命令,正常即可创建 swoole服务,并且客户端可以正常连接,里面对数据库的操作可以封装到一个control里,即可以正常使用model类的各种操作

这样算是把swoole 跟TP 结合起来了,可是…这里面有大坑!!!

问题
1:swoole 是内存常驻,TP 里的单例是否会造成内存泄漏?

2:swoole 可能出现多线程,TP如若多线程同时使用一个数据库连接会是什么情况?

3:swoole对象有自己的生命周期,跟TP 对象生命周期会有和关联跟影响?

先说说使用中遇到的问题

1: 数据库连接报 connection has gone away

这个swoole 文档里有提到,就是数据库服务到把连接关了,TP 数据库配置里加上重连配置就可以
‘break_reconnect’ => true,

2:偶尔出现 package out of order 异常

这个就大头了,因为不是必现测试了挺久,开始以为是TP 跟swoole 不兼容 于是手写PDO 连接,但发现问题依旧,然后怀疑是PDO 的问题,于是换为mysqli驱动…
奇迹般 问题好像解决了.
我就傻傻的认为这是PDO 驱动的问题…知道后来仔细看了swoole文档 跟TP的源码 才发现问题远远没有这么简单!

swoole生命周期:
swoole 是内存常驻的,任何的static,global变量 以及php自带的超全局变量 都将是内存常驻,与sever的生命周期相同!!!
参见 文档:https://wiki.swoole.com/wiki/page/354.html

关于TP model类使用 我前面有一篇 源码分析,这里不累赘
https://blog.csdn.net/lqb3732842/article/details/79878067

分析 :
TP 初始化时会根据数据库配置 初始化一个 pdo数据库连接,并且是以static 方式保存的,也就是说,在tp里启动一次swoole 有且只有一个pdo连接,并且这个链接生命周期是跟服务器一致的!!

这里简单解析下 swoole 里的几个配置,文档说的不清不楚的

1:worker_num //对应于WorkerStart 处理客户端连接的线程数(包括onmessage,onopen ..等回调),如只设置为1阻塞后所有操作都被阻塞

ps: 一个 worker_num 理论上应该对应一个数据库连接,并且该连接的生命周期应该跟该线程保存一致,所以最好在 WorkerStart 时创建一个连接并保存,
在onmessage/onopen 等回调里通过 sever->worker_pid 确定使用哪个连接(防止多线程用错连接报错),另注意其生命周期(static,globe 等)

2:max_request:// 当某个work线程请求数(收到消息数)达到设定数量时会关闭此线程并重新起一条新的线程(关闭线程后会回收该线程的所有对象),以防内存泄漏

3:task_worker_num: //task 任务的线程数,跟 worker_num 类似

当swoole 服务器配置的 work_num 数>1时,2条线程会同时使用 一个pdo 链接,这时如果出现并发 则会报 package out of order 异常

可是 为什么用 mysqi 驱动会没这个事呢?
后来我测试了,当mysqli驱动链接数据库时 2条线程同时争抢一个数据库链接时后面的线程会被阻塞等待,直到前面的线程使用完成….!!,而PDO 会直接报异常!

所以 如果需要用TP 跟swoole 一定 务必将 work_num 设置为1 并且不能使用 task,因为task 进程也会使用同一个pdo链接,这就注定常规TP不能跟swoole 一起好好玩!!!

那一定要用怎么解决呢?
我的建议是 自己封装一个POD 数据库连接,在swoole 里连接数据库用自己封装的,并且在onWorkStart 时就初始化数据库连接,并且以work_id 为key 保存到全局数组中
在onmessage 等回到中 通过server->worker_pid 获得全局的数据库连接操作数据库,这样每条线程只会实例化一次数据库连接,并不会造成连接泄漏,根本也不需要什么数据库连接池!

swoole 里特别需要注意的是,不能随便使用 static,globe,是用这些的对象生命周期会跟server 一致,直到服务器关闭才会回收内存!!!
并且 这些对象是全局唯一的,不像普通web 服务每次接口访问都是内存独立的,记住,swoole 是内存常驻的,内存常驻的,常驻的!!

猜你喜欢

转载自blog.csdn.net/lqb3732842/article/details/79880919