從線上當機引起的問題談開去

nbboy發表於2020-06-21

背景

  公司的外網線上專案連續當機將近2周時間,後來通過查詢日誌,發現是某度蜘蛛大量爬取造成。因為公司花錢進行了SEO推廣,所以也不能直接禁掉蜘蛛,所以一度造成網站打不開的情況。期間增加頻寬,擴容cpu,增強搜尋引擎叢集等方案,都得不到好的效果。

分析

  開始優化程式碼,記錄慢日誌,在雲平臺看慢查詢,漸漸優化慢查詢,但是問題依舊。慢慢通過分析慢日誌注意到如下細節:

這裡我摘取了兩個日誌片段,其實類似日誌片段還有非常多。可以看到將近相同的時間不同的許多程式都進行了GC,而這裡的所謂GC是什麼?
在StartSession.php上看到了如下程式碼:

/**
     * Remove the garbage from the session if necessary.
     *
     * @param  \Illuminate\Session\SessionInterface  $session
     * @return void
     */
    protected function collectGarbage(SessionInterface $session)
    {
        $config = $this->manager->getSessionConfig();

        // Here we will see if this request hits the garbage collection lottery by hitting
        // the odds needed to perform garbage collection on any given request. If we do
        // hit it, we'll call this handler to let it delete all the expired sessions.
        if ($this->configHitsLottery($config)) {
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    }

這裡註釋的意思是有一定概率進行GC,並不是每次都進行GC,先來看下GC做了什麼事情。
因為配置的是檔案session,所以我們找到檔案Session的實現檔案FileSessionHandler.php,摘取GC函式程式碼如下:

/**
     * {@inheritdoc}
     */
    public function gc($lifetime)
    {
        $files = Finder::create()
                    ->in($this->path)
                    ->files()
                    ->ignoreDotFiles(true)
                    ->date('<= now - '.$lifetime.' seconds');

        foreach ($files as $file) {
            $this->files->delete($file->getRealPath());
        }
    }

程式碼的意思很簡單,就是通過搜尋檔案,獲取到過期的session檔案然後進行刪除,這裡的過期時間在session.php的配置裡定義:

 /*
    |--------------------------------------------------------------------------
    | Session Lifetime
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to immediately expire on the browser closing, set that option.
    |
    */

    'lifetime' => 120,

    'expire_on_close' => true,

可以看到預設是2個小時,下面一個引數的意義是是否要在瀏覽器關閉的時候立即過期!剛才說到了,有一定概率執行這個GC,那這個概率多大呢?

/**
     * Determine if the configuration odds hit the lottery.
     *
     * @param  array  $config
     * @return bool
     */
    protected function configHitsLottery(array $config)
    {
        return random_int(1, 100) <= 2;
    }

上面程式碼是我把預設配置替換後的結果,也就是會有2%的可能會觸發一次GC!而一次GC我們要回收兩個小時之前的過期session檔案。如果在併發量稍微有點大的情況下,要刪除的檔案其實是非常可觀的,刪除這個IO操作就是直接導致了cpu負荷直接超載的罪魁禍首。

解決方案

  方法很簡單,就是換driver,比如集中式的redis session方案。

總結

  雖然這次專案的主要負責人不是我,不過通過這次抓取日誌,跟蹤程式碼,分析程式碼學到了很多優化的一手資料。Laravel其實是一個相當靈活的框架,檔案session方式只能在測試環境進行應用,而線上上環境必須上集中式session方式,不然流量起來的時候,就會吃苦頭。Laravel如不經過優化的情況下,他的效能是不高的,好在他也提供了很多可以優化的擴充套件介面,算的上是目前PHP裡相當成熟的框架了。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章