1. 程式人生 > >Yii2實現mysql斷線重連[轉載]

Yii2實現mysql斷線重連[轉載]

最近遇到“Yii2實現mysql斷線重連”問題,找了好久資料,最後找到這篇文件是說明了該情況的,感謝這位作者的分享,記錄下來,必備以後查閱。

原文連結:https://www.yiichina.com/topic/7296

Yii2實現資料庫斷線重連

一、前話
在工作中,有時候一些後臺指令碼需要長時間的執行,同時可能在連線資料庫後,長時間不與資料庫服務端互動。此時,伺服器可能會斷開與客戶端的連線。從而客戶端再次互動時就會出現"MySQL server has gone away"連線丟失。
此次修改達到的效果:斷線重連機制對應用層完全透明,無需自己重複傳送請求。

正題
一、重寫\yii\db\Command類中的execute 與 queryInternal方法

<?php
    namespace common\components;

    use Yii;

    /**
     * 新增加執行sql時斷開重連
     * 資料庫連線斷開異常
     * errorInfo = [''HY000',2006,'錯誤資訊']
     * Class Command
     * @package common\components
     */
    class Command extends \yii\db\Command
    {
        const EVENT_DISCONNECT = 'disconnect';

        /**
         * 處理修改型別sql的斷線重連問題
         * @return int
         * @throws \Exception
         * @throws \yii\db\Exception
         */
        public function execute()
        {
            try{
                return parent::execute();
            }catch(\Exception $e){
                if($this->handleException($e))
                    return parent::execute();
                throw $e;
            }
        }

        /**
         * 處理查詢類sql斷線重連問題
         * @param string $method
         * @param null $fetchMode
         * @return mixed
         * @throws \Exception
         * @throws \yii\db\Exception
         */
        protected function queryInternal($method, $fetchMode = null)
        {
            try{
                return parent::queryInternal($method, $fetchMode);
            }catch(\Exception $e){
                if($this->handleException($e))
                    return parent::queryInternal($method, $fetchMode);
                throw $e;
            }
        }

        /**
         * 處理執行sql時捕獲的異常資訊
         * 並且根據異常資訊來決定是否需要重新連線資料庫
         * @param \Exception $e
         * @return bool true: 需要重新執行sql false: 不需要重新執行sql
         */
        private function handleException(\Exception $e)
        {
            //如果不是yii\db\Exception異常丟擲該異常或者不是MySQL server has gone away
            $offset = stripos($e->getMessage(),'MySQL server has gone away');
            if(($e instanceof \yii\db\Exception) == false OR $offset === false)
                //OR $e->errorInfo[0] != 'HY000' OR $e->errorInfo[1] != 2006)
                return false;

            $this->trigger(static::EVENT_DISCONNECT);

            //將pdo設定從null
            $this->pdoStatement = NULL;
            //$this->db->resetPdo();
            $this->db->close();
            return true;
        }
    }

使用在common/config/main-local.php裡修改

<?php
    return [
        'components' => [
            'db' => [
                'class'   => 'yii\db\Connection',
                'commandClass' => 'common\components\Command',// 加上這一條配置,Yii2 解決2006 MySQL server has gone away問題
                'username' => 'XXX',
                'password' => 'XXX',
                'dsn' => 'mysql:host=XXX;dbname=XXX;port=3306',
            ],
        ],
    ];

修改中遇到的問題:
眼尖的同學可能已經看到了在上面的"handleException"函式中有段註釋了的程式碼"OR $e->errorInfo[0] != ‘HY000’ OR e &gt; e r r o r I n f o [ 1 ] ! = 2006 &quot; &quot; e-&gt;errorInfo[1] != 2006&quot; 被&quot; offset = stripos($e->getMessage(),‘MySQL server has gone away’);" 代替了。那是因為在斷線的情況下,客戶端首次請求時會產生一個"Error"與一個"PDOException"。由於Yii2在底層實現了一個錯誤處理函式。在捕獲到錯誤後會轉換成一個"ErrorException"導致"PDOException"被覆蓋了。同時在"PDOException"中的錯誤狀態碼也獲取不到了。如果你有更好的方案也希望您能在下面的留言中一起交流