在Mysqlnd手册的这一部分中,描述了如何在失去与从属Mysql服务器的连接时实现推荐的故障切换方式。
我愿意在PhalconPHP中实现它。由于我有几个使用Phalcon和Mysql-nd的重要项目,所以在正确的地方完成它对我来说非常重要。
试图找到一些文档,但找不到任何示例。我试图找到EventManager的方法,在这里和这里查看Phalcon文档,但找不到透明的方法。
最吸引人的方法是使用事件管理器捕获错误事件,并在连接出错时再次查询。
1次更新
在阅读了一些Phalcon源代码后,我发现可能没有办法以标准方式第二次运行相同的查询——我的意思是通过某种PDO参数或使用附加到db
服务的Phalcon的EventManager
。我发现一种可能的尝试是在db:afterConnection
事件之后实际运行任何查询,但这不是一种解决方案。
2更新
db:afterConnection
很难到达,相反,可以在db:beforeQuery
期间收集所有内容。问题是,PDO是由Phalcon使用PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
运行的,所以当与从机的连接失效时,它无法到达db:afterConnection
事件。在db:beforeQuery
期间可以获得PDO实例,并通过EventManager
更改此属性,但它没有给出任何信息,因为即使我能够第二次发送相同的查询,我也无法找到将其返回到适当位置的方法(在db:afterQuery
期间无法覆盖查询结果),因为获得的语句不是事件发送的一部分,并且Eventmanager结果根本没有被使用:
if typeof statement == "object" {
if typeof eventsManager == "object" {
eventsManager->fire("db:afterQuery", this, bindParams);
}
return new ResultPdo(this, statement, sqlStatement, bindParams, bindTypes);
}
目前,它似乎已通过配置修复:
{
"db-cluster": {
"master": {
"master": {
"host": "master.local",
"port": 3306
}
},
"slave": {
"slave-1": {
"host": "slave-1.local",
"port": 3306
},
"slave-2": {
"host": "slave-2.local",
"port": 3306
},
"slave-3": {
"host": "slave-3.local",
"port": 3306
}
},
"filters": {
"roundrobin": []
},
"failover": {
"strategy": "loop_before_master",
"remember_failed": true,
"max_retries": 1
},
"server_charset": "utf8"
}
}
若服务器无法访问,那个么它就会回退到其他服务器,问题是它试图连接到无法访问的服务器至少3秒钟。对此的演练是:
$eventsManager = new EventsManager();
$connection->setEventsManager($eventsManager);
$eventsManager->attach('db:beforeQuery', function($event, $connection) {
// fix: if slave does not respond, without this it goes over 3 seconds before trying next one
!defined('DST') && define('DST', ini_get('default_socket_timeout'));
ini_set("default_socket_timeout", 1);
});
$eventsManager->attach('db:afterQuery', function($event, $connection) {
ini_set('default_socket_timeout', defined('DST') ? DST : 60);
});
即使它现在可以工作(当slave无法访问时,它最多会挂在它上1秒——这仍然是一段很长的时间),它仍然不允许我从PHP源代码连接来编写推荐的解决方案。
更新
通过扩展PhalconDbAdapterPdoMysql
类:,可以从PhalconDbAdapterPdo
类覆盖executePrepared
方法
namespace Application;
use PDOException;
class Mysql extends PhalconDbAdapterPdoMysql {
public function executePrepared(statement, placeholders, dataTypes) {
try {
!defined('DST') && define('DST', ini_set('default_socket_timeout', 1));
$stmt = parent::executePrepared(statement, placeholders, dataTypes);
ini_set('default_socket_timeout', DST ?: 60);
return $stmt;
} catch(PDOException $e) {
if(/* logic to find [2002, 2003, 2005] sql errors */) {
return $this->executePrepared(statement, placeholders, dataTypes);
}
throw $e;
}
}
}
并用它构建db
服务。