日期:2011-07-01 浏览次数:20618 次
在我和我的小伙伴们如火如荼的开发、测试时发生了“mysql server too many connections”的错误,稍微排查了一下,发现是php后台进程建立了大量的链接,而没有关闭。服务器环境大约如下php5.3.x 、mysqli API、mysqlnd 驱动。代码情况是这样:
//后台进程A
/*
配置信息
'mysql'=>array(
'driver'=>'mysqli',
// 'driver'=>'pdo',
// 'driver'=>'mysql',
'host'=>'192.168.111.111',
'user'=>'root',
'port'=>3306,
'dbname'=>'dbname',
'socket'=>'',
'pass'=>'pass',
'persist'=>true, //下面有提到哦,这是持久链接的配置
),
*/
$config=Yaf_Registry::get('config');
$driver = Afx_Db_Factory::DbDriver($config['mysql']['driver']); //mysql mysqli
$driver::debug($config['debug']); //注意这里
$driver->setConfig($config['mysql']); //注意这里
Afx_Module::Instance()->setAdapter($driver); //注意这里,哪里不舒服,就注意看哪里。
$queue=Afx_Queue::Instance();
$combat = new CombatEngine();
$Role = new Role(1,true);
$idle_max=isset($config['idle_max'])?$config['idle_max']:1000;
while(true)
{
$data = $queue->pop(MTypes::ECTYPE_COMBAT_QUEUE, 1);
if(!$data){
usleep(50000); //休眠0.05秒
++$idle_count;
if($idle_count>=$idle_max)
{
$idle_count=0;
Afx_Db_Factory::ping();
}
continue;
}
$idle_count=0;
$Role->setId($data['attacker']['role_id']);
$Property = $Role->getModule('Property');
$Mounts = $Role->getModule('Mounts');
//............
unset($Property, $Mounts/*.....*/);
}
从这个后台进程代码中,可以看出“$Property”变量以及“$Mounts”变量频繁被创建,销毁。而ROLE对象的getModule方法是这样写的
//ROLE对象的getModule方法
class Role extends Afx_Module_Abstract
{
public function getModule ($member_class)
{
$property_name = '__m' . ucfirst($member_class);
if (! isset($this->$property_name))
{
$this->$property_name = new $member_class($this);
}
return $this->$property_name;
}
}
//Property 类
class Property extends Afx_Module_Abstract
{
public function __construct ($mRole)
{
$this->__mRole = $mRole;
}
}
可以看出getModule方法只是模拟单例,new了一个新对象返回,而他们都继承了Afx_Module_Abstract类。Afx_Module_Abstract类大约代码如下:
abstract class Afx_Module_Abstract
{
public function setAdapter ($_adapter)
{
$this->_adapter = $_adapter;
}
}
类Afx_Module_Abstract中关键代码如上,跟DB相关的,就setAdapter一个方法,回到“后台进程A”,setAdapter方法是将Afx_Db_Factory::DbDriver($config['mysql']['driver'])的返回,作为参数传了进来。继续看下Afx_Db_Factory类的代码
class Afx_Db_Factory
{
const DB_MYSQL = 'mysql';
const DB_MYSQLI = 'mysqli';
const DB_PDO = 'pdo';
public static function DbDriver ($type = self::DB_MYSQLI)
{
switch ($type)
{
case self::DB_MYSQL:
$driver = Afx_Db_Mysql_Adapter::Instance();
break;
case self::DB_MYSQLI: