基于 SMProxy 通过协程调度实现 MySQL 连接池

简介

我们之前都是基于 LaravelS 扩展在 Laravel 框架中使用 Swoole,但是 LaravelS 不支持数据库连接池,只能实现数据库长连接,而对于 PHP 应用来说,数据库连接池在提升性能方面确有显著功效,我们可以基于 Swoole 提供的异步任务或者协程来自己实现数据库连接池,不过还有一个现成的扩展可以使用,那就是SMProxy,SMProxy 是一个基于 Swoole 开发的 MySQL 数据库连接池,今天我们就以这个扩展为例演示如何在 Laravel 中使用数据库连接池。

实现原理

与传统 PHP 应用中数据库短连接(每次操作需要重新建立连接)不同,SMProxy 将数据库连接作为对象实例存储在内存中,当用户需要访问数据库时,第一次会建立连接,后面并不会建立新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,也不会将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。连接的建立、断开都由连接池自身来管理。

同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。超出最大连接数会采用协程挂起,等到有连接关闭再恢复协程继续操作。

功能特性

  • 支持读写分离
  • 支持数据库连接池,能够有效解决 PHP 带来的数据库连接瓶颈
  • 支持 SQL92 标准
  • 采用协程调度
  • 支持多个数据库连接,多个数据库,多个用户,灵活搭配
  • 遵守 MySQL 原生协议,跨语言,跨平台的通用中间件代理
  • 支持 MySQL 事务
  • 支持 HandshakeV10 协议版本
  • 完美兼容 MySQL5.5 - 8.0
  • 兼容各大框架,无缝提升性能

安装配置

环境要求

  • Swoole >= 2.1.3
  • PHP >= 7.0

安装

我们可以通过从 Github 克隆一个独立的 SMProxy 项目到本地,以便可以被多个项目共用,然后通过 composer install 安装该项目的依赖:

git clone https://github.com/louislivi/SMProxy.git
cd SMProxy
composer install --no-dev

配置

安装成功,需要通过 conf/database.jsonconf/server.json 来配置 SMProxy,前者主要用于配置 MySQL 数据库信息,比如本地测试的话配置信息如下(以 laradock 作为开发环境):

{
  "database": {
    "account": {
      "root": {
        "user": "root",
        "password": "root"
      }
    },
    "serverInfo": {
      "server1": {
        "write": {
          "host": ["mysql"],
          "port": 3306,
          "timeout": 2,
          "account": "root"
        },
        "read": {
          "host": ["mysql"],
          "port": 3306,
          "timeout": 2,
          "account": "root"
        }
      }
    },
    "databases": {
      "todolist": {    // 数据库名称
        "serverInfo": "server1",
        "startConns": "swoole_cpu_num()*10",     // 初始连接数
        "maxSpareConns": "swoole_cpu_num()*10",  // 最大空闲连接数
        "maxSpareExp": 3600,
        "maxConns": "swoole_cpu_num()*20",       // 最大连接数
        "charset": "utf8mb4"
      }
    }
  }
}

后者主要用于配置 SMProxy 服务器信息(ROOT 表示当前 SMProxy 项目的根目录,同样以 laradock 作为示例开发环境):

{
  "server": {
    "user": "root",          // SMProxy 账户
    "password": "123456",    // SMProxy 密码
    "charset": "utf8mb4",    
    "host": "workspace",       // SMProxy 监听地址
    "port": "3366",          // SMProxy 监听端口
    "mode": "SWOOLE_PROCESS",
    "sock_type": "SWOOLE_SOCK_TCP",
    "logs": {
      "open":true,
      "config": {
        "system": {
          "log_path": "ROOT/logs",
          "log_file": "system.log",
          "format": "Y/m/d"
        },
        "mysql": {
          "log_path": "ROOT/logs",
          "log_file": "mysql.log",
          "format": "Y/m/d"
        }
      }
    },
    "swoole": {
      "worker_num": "swoole_cpu_num()",
      "max_coro_num": 6000,
      "open_tcp_nodelay": true,
      "daemonize": true,
      "heartbeat_check_interval": 60,
      "heartbeat_idle_time": 600,
      "reload_async": true,
      "log_file": "ROOT/logs/swoole.log",
      "pid_file": "ROOT/logs/pid/server.pid"
    },
    "swoole_client_setting": {
      "package_max_length": 16777215
    },
    "swoole_client_sock_setting": {
      "sock_type": "SWOOLE_SOCK_TCP"
    }
  }
}

运行

我们可以在 SMProxy 项目下通过如下命令启动 SMProxy:

./bin/SMProxy start

基于SMProxy实现数据库连接池

启动成功后,通过 ps -ef | grep SMProxy 查看开启的 MySQL 客户端连接实例:

基于SMProxy实现数据库连接池

通过这些实例就可以连接到 MySQL 数据库进行操作,我们可以通过 SMProxy 账户、密码、监听地址和端口连接到数据库。

在 Laravel 中配置

修改 Laravel 环境配置文件 .env 数据库相关配置信息如下,以便可以通过 SMProxy 数据库连接池与数据库进行交互:

DB_CONNECTION=mysql
DB_HOST=workspace          // 与 server.json 中的 host 一致
DB_PORT=3366               // 与 server.json 中的 port 一致
DB_DATABASE=todolist       // 与 database.json 中的数据库名称一致
DB_USERNAME=root           // 与 server.json 中的 user 一致
DB_PASSWORD=123456         // 与 server.json 中的 password 一致

这样 Laravel 中的所有数据库(MySQL)操作默认都是通过基于 SMProxy 的数据库连接池进行的。你可以通过 ab 压力测试或者 Telescope 查看数据库操作耗时对比使用连接池与不使用连接池的性能差别,测试中因超出最大连接数会采用协程挂起等到有连接关闭再恢复协程继续操作,所以并发量与配置文件 maxConns 设置的不合适,会导致比不使用连接池慢,主要是为了控制连接数。

更多明细请参考 SMProxy 官方文档

上一篇: 基于 Swoole 实现协程篇(三):在 Laravel 框架中使用协程

下一篇: Swoole 的底层架构及运行原理