首页 > 技术分享 > Swoole
收藏

Swoole异步MySQL连接超时,Swoole实现MySQL连接心跳检测机制

10/22 16:20
大潇博客 原创文章,转载请标明出处

在PHP中,访问MySQL数据库往往是性能提升的瓶颈。而MySQL连接池我想大家都不陌生,这是一个很好的提升数据库访问性能的方式。传统的MySQL连接池,是预先申请一定数量的连接,每一个新的请求都会占用其中一个连接,请求结束后再将连接放回池中,如果所有连接都被占用,新来的连接则会进入等待状态。


知道了MySQL连接池的实现原理,再来看如何使用Swoole实现一个连接池。
首先,Swoole允许开启一定量的Task Worker进程,我们可以让每个进程都拥有一个MySQL连接,并保持这个连接,这样,我们就创建了一个连接池。
其次,设置swoole的dispatch_mode为抢占模式(主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker)。这样,每个task都会被投递给闲置的Task Worker,保证了每个新的task都会被闲置的Task Worker处理。如果全部Task Worker都被占用,则会进入等待队列。


mysql连接默认等待时间(wait_timeout)是28800秒(8个小时),也就是说客户端建立一个连接,没有主动断开连接,也没有任何操作的情况下,MySQL会在到达设置的wait_timeout后断开这个连接。

问题由此产生,由于Swoole常驻内存,进程一直存在,如果使用Swoole异步处理数据,可能会出现长时间未发送数据给Swoole服务端,若此异步中存在MySQL相关操作,就可能出现因超过MySQL的wait_timeout等待时间,而被强制关闭连接的情况。

此时查看Swoole进程是正常的,但程序却不能正常执行,守护模式下若打印了日志,可能看到如下错误:

ERROR php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Uncaught mysqli_sql_exception: MySQL server has gone away in...


解决此问题,可在程序中加入心跳检测机制,定时操作MySQL,使其一直保持连接状态。

Swoole的TCP服务内置了心跳检测功能,只需要设置 heartbeat_check_interval 和 heartbeat_idle_time 即可开启。但这种心跳检测仅面向客户端请求。

'heartbeat_idle_time' => 60, //表示一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭

'heartbeat_check_interval' => 10 //表示每60秒遍历一次


对于服务来说,最好的办法是手动创建一个心跳检测机制。

原理比较简单,即做一个定时器,定时发送请求,保持MySQL连接,在启动Swoole的时候,同时启动定时器。

定时器要单独开启一个进程,否则会和单前的HTTP服务器冲突,并发出警告:

PHP Warning: Swoole\Server::start(): eventLoop has already been created, unable to start Swoole\Http\Server


正确做法:

use Swoole\Process;

$http = new Swoole\Http\Server("0.0.0.0", 9501);

//创建子进程

$process = new Process(function () {

//默认定时器在执行回调函数时会自动创建协程

Swoole\Timer::tick(2000, function (int $timer_id) { //单位:毫秒

echo "coro-----1 " . Coroutine::getcid() . " start\n";

});

});

$http->addProcess($process);

使用class的方式:

public function __construct(){

$this->db_factroy = new db_factroy();

$this->serv = new Swoole\Http\Server("127.0.0.1", 3311);

$this->serv->set([

'worker_num' => 4,

'max_request' => 1,

'task_worker_num' => 50,

'daemonize' => $this->daemonize,

'log_file' => $_SERVER['DOCUMENT_ROOT'].'caches/swoole_logs' . '/'.date('Ymd').'.log'

]);

$this->serv->on('Request', [$this, 'onRequest']);

$this->serv->on("Task", [$this, 'onTask']);

$this->serv->on("Finish", [$this, 'onFinish']);

$this->serv->on("WorkerStart", [$this, 'onWorkerStart']);

//创建子进程,创建定时器(心跳检测)

$this->process = new Process(function(){

Swoole\Timer::tick(7200000, function() {

$this->Connection();

});

});

$this->serv->addProcess($this->process);

$this->serv->start();

}

//心跳检测

public function Connection(){

$this->log_name = date('Ymd');

$this->db_factroy->select("","aid","aid desc","1","user");

}


时间原因,先记录这些。

参考文章:

https://blog.csdn.net/sunrj_niu/article/details/129706148

https://blog.csdn.net/weixin_33923762/article/details/92145939

https://developer.aliyun.com/article/1547450


打赏

阅读排行

大家都在搜

博客维护不易,感谢你的肯定
扫码打赏,建议金额1-10元
  • 15601023311