MySQL 与 PhalconPHP/PDO 的 ND 连接故障转移
MySQL with ND connection failover with PhalconPHP/PDO
在 Mysql-nd 手册的 this 部分描述了如何在失去与从属 MySQL 服务器的连接时实现推荐的故障转移方法。
我愿意在 PhalconPHP 中实现它。因为我有几个使用 Phalcon 和 Mysql-nd 的重要项目,所以在正确的地方做对我来说真的很重要。
试图找到一些文档,但找不到任何示例甚至可以开始。试图找到 EventManager 方法,查看 Phalcon 文档 here and here 但无法找到透明的方法。
最有吸引力的方法是使用 event manager 捕获错误事件并在连接错误时再次查询。
1 条更新
在阅读了我发现的一些 Phalcon 资源后,可能无法以标准方式 运行 第二次进行相同的查询 - 我的意思是通过某种 PDO 参数或使用 Phalcons' EventManager
附加到 db
服务。我发现的一种可能尝试是 运行 在 db:afterConnection
事件之后进行任何查询,但这不是解决方案。
2 更新
db:afterConnection
很难到达,相反可以在 db:beforeQuery
期间收集所有东西。问题是,Phalcon 的 PDO 是 运行 和 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
,因此当与从站的连接终止时,它无法到达 db:afterConnection
事件。可以在 db:beforeQuery
期间获取 PDO 实例并通过 EventManager
更改此属性,但它什么也没给出,因为即使我能够第二次发送相同的查询,我也找不到办法return 它在适当的位置(在 db:afterQuery
期间不能覆盖查询结果) because obtained statement is not a part of event send, and Eventmanager result is not being used at all:
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源码写一个recommended solution.
更新
可以通过扩展 Phalcon\Db\Adapter\Pdo\Mysql
class:
覆盖 Phalcon\Db\Adapter\Pdo
class 的 executePrepared
方法
namespace Application;
use \PDOException;
class Mysql extends \Phalcon\Db\Adapter\Pdo\Mysql {
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
服务。
在 Mysql-nd 手册的 this 部分描述了如何在失去与从属 MySQL 服务器的连接时实现推荐的故障转移方法。
我愿意在 PhalconPHP 中实现它。因为我有几个使用 Phalcon 和 Mysql-nd 的重要项目,所以在正确的地方做对我来说真的很重要。
试图找到一些文档,但找不到任何示例甚至可以开始。试图找到 EventManager 方法,查看 Phalcon 文档 here and here 但无法找到透明的方法。
最有吸引力的方法是使用 event manager 捕获错误事件并在连接错误时再次查询。
1 条更新
在阅读了我发现的一些 Phalcon 资源后,可能无法以标准方式 运行 第二次进行相同的查询 - 我的意思是通过某种 PDO 参数或使用 Phalcons' EventManager
附加到 db
服务。我发现的一种可能尝试是 运行 在 db:afterConnection
事件之后进行任何查询,但这不是解决方案。
2 更新
db:afterConnection
很难到达,相反可以在 db:beforeQuery
期间收集所有东西。问题是,Phalcon 的 PDO 是 运行 和 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
,因此当与从站的连接终止时,它无法到达 db:afterConnection
事件。可以在 db:beforeQuery
期间获取 PDO 实例并通过 EventManager
更改此属性,但它什么也没给出,因为即使我能够第二次发送相同的查询,我也找不到办法return 它在适当的位置(在 db:afterQuery
期间不能覆盖查询结果) because obtained statement is not a part of event send, and Eventmanager result is not being used at all:
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源码写一个recommended solution.
更新
可以通过扩展 Phalcon\Db\Adapter\Pdo\Mysql
class:
Phalcon\Db\Adapter\Pdo
class 的 executePrepared
方法
namespace Application;
use \PDOException;
class Mysql extends \Phalcon\Db\Adapter\Pdo\Mysql {
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
服务。