1、在 Yii 2.0 中,健康检查的实现,之前有同事实现了一个版本,感觉有必要再调整一下
Yii::$app->db->open();
if(!Yii::$app->redis->ping()) {
throw new ServerErrorHttpException('error','Redis is unavailable.',500);
} elseif(!Yii::$app->db->getIsActive()) {
throw new ServerErrorHttpException('error','Database is unavailable.',500);
} else {
return 200;
}
2、分析代码发现,Yii::$app->db->getIsActive() 无必要。分别查看相应方法的源码。getIsActive() 中的 $this->pdo 的相关实现已经存在于 open() 中。
/**
* Establishes a DB connection.
* It does nothing if a DB connection has already been established.
* @throws Exception if connection fails
*/
public function open()
{
if ($this->pdo !== null) {
return;
}
if (!empty($this->masters)) {
$db = $this->getMaster();
if ($db !== null) {
$this->pdo = $db->pdo;
return;
}
throw new InvalidConfigException('None of the master DB servers is available.');
}
if (empty($this->dsn)) {
throw new InvalidConfigException('Connection::dsn cannot be empty.');
}
$token = 'Opening DB connection: ' . $this->dsn;
$enableProfiling = $this->enableProfiling;
try {
if ($this->enableLogging) {
Yii::info($token, __METHOD__);
}
if ($enableProfiling) {
Yii::beginProfile($token, __METHOD__);
}
$this->pdo = $this->createPdoInstance();
$this->initConnection();
if ($enableProfiling) {
Yii::endProfile($token, __METHOD__);
}
} catch (/PDOException $e) {
if ($enableProfiling) {
Yii::endProfile($token, __METHOD__);
}
throw new Exception($e->getMessage(), $e->errorInfo, (int) $e->getCode(), $e);
}
}
/**
* Returns a value indicating whether the DB connection is established.
* @return bool whether the DB connection is established
*/
public function getIsActive()
{
return $this->pdo !== null;
}
3、分析代码发现,直接使用命令行。Redis 有很多可以直接从连接中使用的有用的命令。ping 存在于可用的 redis 命令列表中。这个命令经常用来测试一个连接是否还是可用的,或者用来测试一个连接的延时。如图1
4、在 Yii 2.0 中,健康检查的实现,调整之后的版本
Yii::$app->db->open();
Yii::$app->redis->open();
return 200;
5、将 db 的密码修改为错误的,响应 500 如下,符合预期。如图2
{
"name": "Database Exception",
"message": "SQLSTATE[HY000] [1045] Access denied for user 'pcs-api'@'localhost' (using password: YES)",
"code": 1045,
"type": "yii//db//Exception",
"file": "E://wwwroot//pcs-api//vendor//yiisoft//yii2//db//Connection.php",
"line": 637,
"stack-trace": [
"#0 E://wwwroot//pcs-api//api//rests//openconf//CreateAction.php(58): yii//db//Connection->open()",
"#1 [internal function]: api//rests//openconf//CreateAction->run()",
"#2 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Action.php(94): call_user_func_array(Array, Array)",
"#3 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Controller.php(157): yii//base//Action->runWithParams(Array)",
"#4 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Module.php(528): yii//base//Controller->runAction('create', Array)",
"#5 E://wwwroot//pcs-api//vendor//yiisoft//yii2//web//Application.php(103): yii//base//Module->runAction('v1/openconf/cre...', Array)",
"#6 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Application.php(386): yii//web//Application->handleRequest(Object(yii//web//Request))",
"#7 E://wwwroot//pcs-api//api//web//index.php(17): yii//base//Application->run()",
"#8 {main}"
],
"error-info": null,
"previous": {
"name": "Exception",
"message": "SQLSTATE[HY000] [1045] Access denied for user 'pcs-api'@'localhost' (using password: YES)",
"code": 1045,
"type": "PDOException",
"file": "E://wwwroot//pcs-api//vendor//yiisoft//yii2//db//Connection.php",
"line": 705,
"stack-trace": [
"#0 E://wwwroot//pcs-api//vendor//yiisoft//yii2//db//Connection.php(705): PDO->__construct('mysql:host=loca...', 'pcs-api', 'jwpuOHTSX5hOcuX...', NULL)",
"#1 E://wwwroot//pcs-api//vendor//yiisoft//yii2//db//Connection.php(626): yii//db//Connection->createPdoInstance()",
"#2 E://wwwroot//pcs-api//api//rests//openconf//CreateAction.php(58): yii//db//Connection->open()",
"#3 [internal function]: api//rests//openconf//CreateAction->run()",
"#4 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Action.php(94): call_user_func_array(Array, Array)",
"#5 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Controller.php(157): yii//base//Action->runWithParams(Array)",
"#6 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Module.php(528): yii//base//Controller->runAction('create', Array)",
"#7 E://wwwroot//pcs-api//vendor//yiisoft//yii2//web//Application.php(103): yii//base//Module->runAction('v1/openconf/cre...', Array)",
"#8 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Application.php(386): yii//web//Application->handleRequest(Object(yii//web//Request))",
"#9 E://wwwroot//pcs-api//api//web//index.php(17): yii//base//Application->run()",
"#10 {main}"
]
}
}
6、将 redis 的密码修改为错误的,响应 500 如下,符合预期。将 Yii::$app->redis->open(); 替换为:Yii::$app->redis->ping() ,响应是一样的,原因为在执行 ping 命令之前,需要先执行 open()。如图3
{
"name": "Database Exception",
"message": "Redis error: ERR Client sent AUTH, but no password is set/nRedis command was: AUTH 88888888",
"code": 0,
"type": "yii//db//Exception",
"file": "E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php",
"line": 821,
"stack-trace": [
"#0 E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php(789): yii//redis//Connection->parseResponse(Array, '*2//r//n$4//r//nAUTH//r//n$...')",
"#1 E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php(772): yii//redis//Connection->sendCommandInternal('*2//r//n$4//r//nAUTH//r//n$...', Array)",
"#2 E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php(634): yii//redis//Connection->executeCommand('AUTH', Array)",
"#3 E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php(744): yii//redis//Connection->open()",
"#4 E://wwwroot//pcs-api//vendor//yiisoft//yii2-redis//src//Connection.php(709): yii//redis//Connection->executeCommand('PING', Array)",
"#5 E://wwwroot//pcs-api//api//rests//openconf//CreateAction.php(57): yii//redis//Connection->__call('ping', Array)",
"#6 [internal function]: api//rests//openconf//CreateAction->run()",
"#7 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Action.php(94): call_user_func_array(Array, Array)",
"#8 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Controller.php(157): yii//base//Action->runWithParams(Array)",
"#9 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Module.php(528): yii//base//Controller->runAction('create', Array)",
"#10 E://wwwroot//pcs-api//vendor//yiisoft//yii2//web//Application.php(103): yii//base//Module->runAction('v1/openconf/cre...', Array)",
"#11 E://wwwroot//pcs-api//vendor//yiisoft//yii2//base//Application.php(386): yii//web//Application->handleRequest(Object(yii//web//Request))",
"#12 E://wwwroot//pcs-api//api//web//index.php(17): yii//base//Application->run()",
"#13 {main}"
],
"error-info": []
}
7、总结,不论是 db 还是 redis 的连接的检查。最好能够保持统一的检测方式。因此,统一采用 open() 方法。而 Yii::$app->db->getIsActive() 无必要,是冗余的实现。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/250544.html
