關於ThinkPHP執行長時間任務可能導致PHP使用內存越來越大的問題


ThinkPHP執行長時間任務時,可能導致PHP使用內存越來越大,最后因為內存超出配置限額而程序掛掉。  

其實這在很久以前就無意之中發現的一個問題。

3.x之前有這個問題,5.0以后的,應該是已修復了的(我看了下5.0.6)。

這個問題,一句話說明,是因為ThinkPHP在記錄系統日志的時候出現的問題(ThinkPHP在運行過程中,會記錄自己的運行日志,根據是否開啟DEBUG模式,記錄的日志信息有不同,不多說)。

因為一個進程執行完,會在很多地方打點記錄日志,但是為了提高寫日志文件的效率,ThinkPHP先是給所有日志信息記錄在內存里(一個數組),最后一次性寫入文件。正是因為這個科學的做法,導致了可能出現的不科學的結果,那就是,在長時間執行循環任務時,尤其是會循環操作數據庫時而又開啟了DEBUG模式的情況下(日志信息會記錄數據庫語句等,信息較多),就會有大量的日志信息,越來越多的存入這個數組里,造成內存占用越來越大,最終可能導致程序奔潰。

看代碼:

    /**
* 文件:ThinkPHP\Library\Think\Log.class.php * 記錄日志 並且會過濾未經設置的級別 * @static * @access public * @param string $message 日志信息 * @param string $level 日志級別 * @param boolean $record 是否強制記錄 * @return void
*/ static function record($message,$level=self::ERR,$record=false) { if($record || false !== strpos(C('LOG_LEVEL'),$level)) { self::$log[] = "{$level}: {$message}\r\n"; } }

如果遇到這種情況,修改一下此方法即可。這里提供一個最簡單的修改方法(在未開啟DEBUG模式的情況下,直接不記錄日志):

    static function record($message,$level=self::ERR,$record=false) {
        if( !APP_DEBUG ) return false; //在debug為開啟情況下,不記錄日志 by ztg  
        if($record || false !== strpos(C('LOG_LEVEL'),$level)) {
            self::$log[] =   "{$level}: {$message}\r\n";
        }
    }

這樣,當執行長時間的循環任務時,給DEBUG關閉后,則無日志記錄,可不會再出現這種情況了。

 

另外,附帶ThinkPHP5.0后的代碼,可以看出,在保存日志的機制上,已經完全變了,會循環的定量保存,然后清空內容,避免這種情況:

    /**
     * 記錄調試信息
     * @param mixed  $msg  調試信息
     * @param string $type 信息類型
     * @return void
     */
    public static function record($msg, $type = 'log')
    {
        self::$log[$type][] = $msg;
        if (IS_CLI && count(self::$log[$type]) > 100) {
            // 命令行下面日志寫入改進
            self::save();
        }
    }



    /**
     * 保存調試信息
     * @return bool
     */
    public static function save()
    {
        if (!empty(self::$log)) {
            if (is_null(self::$driver)) {
                self::init(Config::get('log'));
            }

            if (!self::check(self::$config)) {
                // 檢測日志寫入權限
                return false;
            }

            if (empty(self::$config['level'])) {
                // 獲取全部日志
                $log = self::$log;
                if (!App::$debug && isset($log['debug'])) {
                    unset($log['debug']);
                }
            } else {
                // 記錄允許級別
                $log = [];
                foreach (self::$config['level'] as $level) {
                    if (isset(self::$log[$level])) {
                        $log[$level] = self::$log[$level];
                    }
                }
            }

            $result = self::$driver->save($log);
            if ($result) {
                self::$log = [];
            }

            return $result;
        }
        return true;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM