如何在 Swoole 中优雅的实现 MySQL 连接池

如何在 Swoole 中优雅的实现 MySQL 连接池

一、为什么需要连接池 ?#

数据库连接池指的是程序和数据库之间保持一定数量的连接不断开,
并且各个请求的连接可以相互复用,
减少重复连接数据库带来的资源消耗,
一定程度上提高了程序的并发性能。

二、连接池实现要点#

  • 协程:使用 MySQL 协程客户端。

使用 MySQL 协程客户端,是为了能在一个 Worker 阻塞的时候,
让出 CPU 时间片去处理其他的请求,提高整个 Worker 的并发能力。

  • 连接池存储介质:使用 \swoole\coroutine\channel 通道。

使用 channel 能够设置等待时间,等待其他的请求释放连接。
并且在等待期间,同样也可以让出 CPU 时间片去处理其他的请求。

假设选择 array 或 splqueue,无法等待其他的请求释放连接。
那么在高并发下的场景下,可能会出现连接池为空的现象。
如果连接池为空了,那么 pop 就直接返回 null 了,导致连接不可用。

注:因此不建议选择 array 或 splqueue。

三、连接池的具体实现#

Copy
<?php use Swoole\Coroutine\Channel; use Swoole\Coroutine\MySQL; class MysqlPool { private $min; // 最小连接数 private $max; // 最大连接数 private $count; // 当前连接数 private $connections; // 连接池 protected $freeTime; // 用于空闲连接回收判断 public static $instance; /** * MysqlPool constructor. */ public function __construct() { $this->min = 10; $this->max = 100; $this->freeTime = 10 * 3600; $this->connections = new Channel($this->max + 1); } /** * @return MysqlPool */ public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * 创建连接 * @return MySQL */ protected function createConnection() { $conn = new MySQL(); $conn->connect([ 'host' => 'mysql', 'port' => '3306', 'user' => 'root', 'password' => 'root', 'database' => 'fastadmin', 'timeout' => 5 ]); return $conn; } /** * 创建连接对象 * @return array|null */ protected function createConnObject() { $conn = $this->createConnection(); return $conn ? ['last_used_time' => time(), 'conn' => $conn] : null; } /** * 初始化连接 * @return $this */ public function init() { for ($i = 0; $i < $this->min; $i++) { $obj = $this->createConnObject(); $this->count++; $this->connections->push($obj); } return $this; } /** * 获取连接 * @param int $timeout * @return mixed */ public function getConn($timeout = 3) { if ($this->connections->isEmpty()) { if ($this->count < $this->max) { $this->count++; $obj = $this->createConnObject(); } else { $obj = $this->connections->pop($timeout); } } else { $obj = $this->connections->pop($timeout); } return $obj['conn']->connected ? $obj['conn'] : $this->getConn(); } /** * 回收连接 * @param $conn */ public function recycle($conn) { if ($conn->connected) { $this->connections->push(['last_used_time' => time(), 'conn' => $conn]); } } /** * 回收空闲连接 */ public function recycleFreeConnection() { // 每 2 分钟检测一下空闲连接 swoole_timer_tick(2 * 60 * 1000, function () { if ($this->connections->length() < intval($this->max * 0.5)) { // 请求连接数还比较多,暂时不回收空闲连接 return; } while (true) { if ($this->connections->isEmpty()) { break; } $connObj = $this->connections->pop(0.001); $nowTime = time(); $lastUsedTime = $connObj['last_used_time']; // 当前连接数大于最小的连接数,并且回收掉空闲的连接 if ($this->count > $this->min && ($nowTime - $lastUsedTime > $this->freeTime)) { $connObj['conn']->close(); $this->count--; } else { $this->connections->push($connObj); } } }); } } $httpServer = new swoole_http_server('127.0.0.1',9501); $httpServer->set(['work_num' => 1]); $httpServer->on('WorkerStart', function ($request, $response) { MysqlPool::getInstance()->init()->recycleFreeConnection(); }); $httpServer->on('Request', function ($request, $response){ $conn = MysqlPool::getInstance()->getConn(); $conn->query('SELECT * FROM fa_admin WHERE id=1'); MysqlPool::getInstance()->recycle($conn); }); $httpServer->start();

四、总结#

  • 定时维护空闲连接到最小值。
  • 使用用完数据库连接之后,需要手动回收连接到连接池。
  • 使用 channel 作为连接池的存储介质。
posted @   Yxh_blogs  阅读(3056)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示
CONTENTS