fastdlabs / fastD

:rocket: A high performance PHP API framework.
https://fastdlabs.com
MIT License
420 stars 83 forks source link

force connect in initPool() #82

Closed xueron closed 6 years ago

xueron commented 6 years ago

Add a parament to force connect when initPool() called.

Scene: When I start a new process from a worker, a taskworker, or a server managed user process, the new process will copy parent's connections, even if I call initPool() at the beginning of process callback. It's not OK ~

So I make such fix, to use the same way used in onWorkerStart().

use FastD\Pool\PoolInterface;
use FastD\Process\AbstractProcess;
use swoole_process;

class Process extends AbstractProcess
{
    public function handle(swoole_process $swoole_process)
    {
        parent::handle($swoole_process);

        // 激活连接池
        foreach (app() as $service) {
            if ($service instanceof PoolInterface) {
                $service->initPool();
            }
        }
    }

    // balabala
}
JanHuang commented 6 years ago

Yes, thanks guy.

xueron commented 6 years ago

Tips, 亲测确实如此。 @JanHuang 或许可以加在文档提示大家。来源见http://php.net/manual/zh/pdo.connections.php 下面的评论。

if parent process made a mysql connection, use $swoole_prcess->exit(0); in child process, will cause parent's connection lost.

If you want to keep connection after fork exit, you can kill with SIGKILL forked process.

eg.

class WorkerProcess extends Process
{
    protected $pid;

    protected $timeoutTimerId;

    protected $worker_process;

    public function handle(swoole_process $swoole_process)
    {
        $this->pid = getmypid();
        $this->worker_process = $swoole_process;

        // use sigkill to close process
        register_shutdown_function(function () {
            swoole_process::kill(getmypid(), SIGKILL);
        });
    }
}
JanHuang commented 6 years ago

@xueron 是哦,还真是这样子。我在完善文档这块也加上这个说明。感谢提醒。😆

xueron commented 6 years ago
    public function handle(swoole_process $swoole_process)
    {
        parent::handle($swoole_process);

        // use sigkill to close process
        register_shutdown_function(function () {
            swoole_process::kill(getmypid(), SIGKILL);
        });

        // 重新注入Cache服务
        $config = config()->get('cache');
        app()->add('cache', new CachePool($config));
        unset($config);

        // 重新注入DB服务
        $config = config()->get('database', []);
        app()->add('phalcon_db', new DatabasePool($config));
        unset($config);

        // 激活连接池
        foreach (app() as $service) {
            if ($service instanceof PoolInterface) {
                $service->initPool();
            }
        }
    }

@JanHuang 容器里面的连接池对象也得重新new一个出来。对redis的连接也是这个情况。测试许久,目前这样能够既保证每个子进程都是新建数据库和redis的连接,退出后也不会影响父进程自己的连接。

JanHuang commented 6 years ago

@xueron 可以,很感谢你的提醒。方便留个联系方式?方便学习

xueron commented 6 years ago

@JanHuang xueron#xueron.com

子进程会复制父进程所有的文件描述符,并且在子进程退出的时候全部关闭。上述方案也不是非常的稳定。

我测试最稳定的办法是在new一个子进程并且start()之后,立刻调用:

    // 激活连接池
    foreach (app() as $service) {
        if ($service instanceof PoolInterface) {
            $service->initPool();
        }
    }

在父进程里面全部重新连接一次。这样最可靠。

xueron commented 6 years ago

我在写一个定时任务管理器,思路是写一个任务管理进程manager process,通过server的addProcess()进行管理,然后定时按需fork工作子进程,完成定时任务后退出。管理进程和工作进程都会用到数据库连接。上述方案目前测试可行了。

JanHuang commented 6 years ago

感谢提供思路,我会到实测一次,模拟场景