基于 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.json
和 conf/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
启动成功后,通过 ps -ef | grep SMProxy
查看开启的 MySQL 客户端连接实例:
通过这些实例就可以连接到 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 官方文档。
6 Comments
在 Homestead 中试了一下,感觉用了还没有不用速度快呀。是我压测的姿势不对么
同样一个表,同样的代码。
哈哈 是的 这个跟并发数和连接数有关系 协程调度需要消耗 CPU 资源 如果设置不当的话适得其反 所以新手不建议使用 看完 Swoole 我觉得真要处理高并发问题还是用 Go 或 Java 去做吧 Go 的学习成本并不高
感觉一个 PHP 的站长在安利我们别的语言的优势... 哈哈
哈哈 Swoole 确实是借鉴别的语言的 不论是异步非阻塞通信 还是协程 前者借鉴的是 NodeJS 后者借鉴的是 Go,只不过把这些实现搬到 PHP 扩展里面来实现了,就好比一个是模仿 一个是原生 显然原生的更好一些
运行这句 ./bin/SMProxy start 的时候提示
查看bin文件夹里面有 SMProxy
这么运行试试:php bin/SMProxy start