前言
傳統(tǒng)的nginx+FPM模式的PHP程序而言算行,每次請求FPM的worker都會連接一次mysql,然后請求結(jié)束便會斷開連接州邢。對于并發(fā)小的應用來說這不會有什么問題,但是對于高并發(fā)的應用來說褪子,頻繁建立連接Connect和銷毀連接Close,數(shù)據(jù)庫便會成為瓶頸嫌褪,相信不少人也遇到過to many connection的mysql報錯吧。
連接池的優(yōu)勢
連接池采用的是長連接模式笼痛,會一直保持與MySQL的連接,用完后會重新放回連接池缨伊,從而節(jié)省了建立連接和斷開連接的消耗,大大降低了系統(tǒng)IO的消耗倘核,一定程度上提高了程序的并發(fā)性能泣侮。如果連接池空閑活尊,就從連接池分配一個連接隶校,否則蛹锰,請求將被加入到等待隊列中。
實現(xiàn)
我們采用swoole實現(xiàn)mysql連接池
連接池類
<?php
require_once "MysqlDB.php";
class MysqlPool
{
private static $instance;
private $pool;
private $config;
private $pool_get_timeout;
/**
* 獲取mysql進程池單例
* @param null $config
* @return MysqlPool
*/
public static function getInstance($config = null)
{
if (empty(self::$instance)) {
if (empty($config)) {
throw new RuntimeException("mysql config is empty");
}
self::$instance = new static($config);
}
return self::$instance;
}
public function __construct($config)
{
if (empty($this->pool)) {
$this->config = $config;
$this->pool = new \Swoole\Coroutine\Channel($config['pool_size']);
for ($i = 0; $i < $config['pool_size']; $i++) {
\go(function() use ($config) {
$mysql = new MysqlDB();
$res = $mysql->connect($config['mysql']);
if ($res === false) {
throw new RuntimeException("Failed to connect mysql server");
} else {
$this->pool->push($mysql);
}
});
}
}
}
public function get()
{
if ($this->pool->length() > 0) {
$mysql = $this->pool->pop($this->config['pool_get_timeout']);
if (false === $mysql) {
throw new RuntimeException("Pop mysql timeout");
}
return $mysql;
} else {
throw new RuntimeException("Pool length <= 0");
}
}
public function recycle(MysqlDB $mysql){
$this->pool->push($mysql);
}
/**
* 獲取連接池長度
* @return mixed
*/
public function getPoolSize(){
return $this->pool->length();
}
}
數(shù)據(jù)庫DB類
<?php
class MysqlDB
{
private $connection;
public function connect($config)
{
$connection = new \Swoole\Coroutine\MySQL();
$res = $connection->connect($config);
if ($res === false) {
throw new RuntimeException($connection->connect_error, $connection->errno);
} else {
$this->connection = $connection;
}
return $res;
}
public function query($sql){
$result = $this->connection->query($sql);
return $result;
}
}
在HTTP協(xié)程服務(wù)器中創(chuàng)建連接池
<?php
require_once "MysqlPool.php";
\Co\run(function () {
$server = new \Co\Http\Server("0.0.0.0", 9501, false);
$pool = MysqlPool::getInstance([
'pool_size'=>5,
'pool_get_timeout'=>1,
'timeout'=>1,
'charset'=>'utf8',
'strict_type'=>false,
'fetch_mode'=>true,
'mysql'=>[
'host'=>'127.0.0.1',
'port'=>'3306',
'user'=>'homestead',
'password'=>'secret',
'database'=>'blog',
]
]);
$server->handle('/', function ($request, $response) use ($pool){
$mysql = $pool->get();
$res = $mysql->query("select id,phone,username from user limit 1");
var_dump($res);
$pool->recycle($mysql);
$response->end("<h1>Test</h1>");
});
$server->handle('/test', function ($request, $response) {
$response->end("<h1>Test</h1>");
});
$server->handle('/stop', function ($request, $response) use ($server) {
$response->end("<h1>Stop</h1>");
$server->shutdown();
});
$server->start();
});