在 Yii 2.0 中,服务器的健康检查的实现
1、在 Yii 2.0 中,健康检查的实现,之前有同事实现了一个版本,感觉有必要再调整一下
1 2 3 4 5 6 7 8 | 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() 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /** * 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 中,健康检查的实现,调整之后的版本
1 2 3 | Yii:: $app ->db->open(); Yii:: $app ->redis->open(); return 200; |
5、将 db 的密码修改为错误的,响应 500 如下,符合预期。如图2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | { "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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | { "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() 无必要,是冗余的实现。
近期评论