Command.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. namespace common\replaces;
  3. /**
  4. * 新增加执行sql时断开重连
  5. * 数据库连接断开异常
  6. * errorInfo = [''HY000',2006,'错误信息']
  7. *
  8. * Class Command
  9. * @package common\replaces
  10. * @author qimall
  11. */
  12. class Command extends \yii\db\Command
  13. {
  14. public $retry;
  15. /**
  16. * 处理修改类型sql的断线重连问题
  17. *
  18. * @return int
  19. * @throws \Exception
  20. * @throws \yii\db\Exception
  21. */
  22. public function execute()
  23. {
  24. try {
  25. return parent::execute();
  26. } catch (\yii\db\Exception $e) {
  27. if ($this->handleException($e)) {
  28. return parent::execute();
  29. }
  30. throw $e;
  31. }
  32. }
  33. /**
  34. * 处理查询类sql断线重连问题
  35. *
  36. * @param string $method
  37. * @param null $fetchMode
  38. * @return mixed
  39. * @throws \Exception
  40. * @throws \yii\db\Exception
  41. */
  42. protected function queryInternal($method, $fetchMode = null)
  43. {
  44. try {
  45. return parent::queryInternal($method, $fetchMode);
  46. } catch (\yii\db\Exception $e) {
  47. \Yii::$app->db->close();
  48. if ($this->handleException($e)) {
  49. return parent::queryInternal($method, $fetchMode);
  50. }
  51. throw $e;
  52. }
  53. }
  54. /**
  55. * 判断该数据库异常是否需要重试.一般情况下链接断开的错误才需要重试
  56. * 2006: MySQL server has gone away
  57. * 2013: Lost connection to MySQL server during query
  58. * 但是实际使用中发现,由于Yii2对数据库异常进行了处理并封装成\yii\db\Exception异常
  59. * 因此2006错误的错误码并不能在errorInfo中获取到,因此需要判断errorMsg内容
  60. * @param \yii\db\Exception $ex
  61. * @return bool
  62. * @throws \yii\db\Exception
  63. */
  64. private function handleException(\yii\db\Exception $e)
  65. {
  66. $errorMsg = $e->getMessage();
  67. if (
  68. strpos($errorMsg, 'MySQL server has gone away') ||
  69. strpos($errorMsg, 'Error while sending QUERY packet') ||
  70. strpos($errorMsg, 'SQLSTATE[HY000]: General error')
  71. ) {
  72. $this->retry = true;
  73. $this->pdoStatement = null;
  74. $this->db->close();
  75. $this->db->open();
  76. return true;
  77. }
  78. if (!empty($e->errorInfo) && in_array($e->errorInfo[1], [2006, 2013])) {
  79. $this->retry = true;
  80. $this->pdoStatement = null;
  81. $this->db->close();
  82. $this->db->open();
  83. return true;
  84. }
  85. return false;
  86. }
  87. /**
  88. * 利用$this->retry属性,标记当前是否是数据库重连
  89. * 重写bindPendingParams方法,当当前是数据库重连之后重试的时候
  90. * 调用bindValues方法重新绑定一次参数.
  91. */
  92. protected function bindPendingParams()
  93. {
  94. if ($this->retry) {
  95. $this->retry = false;
  96. $this->bindValues($this->params);
  97. }
  98. parent::bindPendingParams();
  99. }
  100. }