日期:2011-07-01 浏览次数:20534 次
在我和我的小伙伴们如火如荼的开发、测试时发生了“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: