Swoole php-cp
php-cp是Swoole组织开发的一个PHP扩展,可以本地代理MySQL、Redis连接,提供连接池,读写分离,负载均衡,慢查询日志,大数据块日志等功能。相比原生PHP的数据库连接,php-cp连接池可以缓存连接,免去一些重复新建、回收数据库连接带来的时间消耗和IO消耗;并且php-cp连接定时ping数据库,使得连接不会太久没活动而被回收;在连接数超过限额时,php-cp提供了排队机制,而不是直接拒绝,所以php-cp是值得高并发的PHP项目引入使用的。
php-cp提供了代替PDO的类:pdoProxy,所以在主流PHP框架中引入php-cp是十分简便的。在php-cp的README中,提供了Yii、CI和ThinkPHP等框架的集成样例,但是少了Laravel,所以这里介绍一下在Laravel项目中怎样集成php-cp。
安装php-cp扩展
首先,下载php-cp源码,编译安装:
1 | $ cd tmp |
编译成功后,将extension=connect_pool.so
添加到php-cli和php-fpm的php.ini配置文件中,这样PHP启动的时候就会加载connect_pool扩展。
启动pool-server
php-cp提供了现成的连接池脚本,只需要按照以下配置,便能启动pool-server连接池服务:
1 | $ sudo cp ./config.ini.example /etc/pool.ini #根据需求修改配置内容 |
/etc/pool.ini
是pool_server
脚本指定的配置路径,大家可以根据自己的需求,调整/etc/pool.ini
里面的配置参数。比如,PDO数据源,Laravel的连接格式一般是:
1 | ['mysql:host=127.0.0.1;port=3306;dbname=forge'] |
若是请求的连接在配置中找不到对应的数据源,pool_server会自动新建一个数据源,新的数据源信息可以通过sudo pool_server status
查看。
Laravel集成
Laravel集成php-cp的代码我已经上传到github,laravel-swoole-cp,主要是三个文件:
- laravel/framework/src/Illuminate/Database/MySqlSwooleProxyConnection.php
- laravel/framework/src/Illuminate/Database/Connectors/MySqlSwooleProxyConnector.php
- laravel/framework/src/Illuminate/Database/Connectors/ConnectionFactory.php
只要将这三个文件放到Laravel项目的vendor文件夹下便可。MySqlSwooleProxyConnection.php
和MySqlSwooleProxyConnector.php
是模仿Laravel框架自有的MySqlConnection.php
和MySqlConnector.php
新增编写的;而ConnectionFactory.php
则是在Laravel框架原有的基础上修改的,主要是调用MySqlSwooleProxyConnection
和MySqlSwooleProxyConnector
这两个新增类。
ConnectionFactory.php
修改的地方:
1 |
|
注意,这里将php-cp的数据库连接驱动命名为mysql-cp
,所以在Laravel项目的数据库配置config/database.php
里应该这样配置php-cp的连接驱动:
1 |
|
问题来了
一切准备就绪,执行一句DB::table('users')->find();
,却报错了(我好像明白了为什么没人提供php-cp的Laravel连接驱动):
解决办法
经过排查,发现当数据获取模式设为PDO::FETCH_OBJ
,pool_server的worker就会退出,其他获取模式就运行正常,那么应该是变量序列化传输的问题。原来php-cpV1.5.0版本(目前最新版)针对PHP7环境,使用自家的swoole_serialize序列化方法,而我的系统正好装的是PHP7,于是想:用相对主流的序列化方法msgpack-php替换swoole_serialize的话可能会解决这个问题。
替换后,重新编译安装php-cp,果然问题解决了。替换后的php-cp源码我已经上传到github,breeze2/php-cp,若有需要可以下载安装。
最后
php-cp主要作用是应对高并发,假设MySQL数据库的最大连接数max_connections
是100,当请求连接超过100的时候,若是PHP直接访问数据库,MySQL会直接拒绝多出来的连接;而通过php-cp访问数据库,php-cp有排队机制应对多出来的连接,php-cp的连接池可以长驻内存,也免去了大量的数据库连接线程新建、回收带来的消耗。
综合来说,一般并发量不高的网站也用不上php-cp;而对于大型高并发的网站,使用数据库中间件更为合理,因为php-cp是本地代理,有些限制了单机效能。只是看到php-cp没有相应的Laravel连接驱动,强迫症发作,写了一个而已。