記一次使用 laravel-s 記憶體洩漏

seth-shi發表於2022-01-08

環境

  • 使用的是swoole:alpine的映象構建環境
FROM phpswoole/swoole:php7.4-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && cat /etc/apk/repositories

# 快速安裝 PHP 擴充套件 install-php-extensions xxxCOPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/RUN install-php-extensions pcntl redis pdo_mysql

WORKDIR /var/www
COPY . .
RUN chmod -R 0777 storage && chmod -R 0777 bootstrap/cache && composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ && composer install

### 生成 laravels 啟動檔案 + 啟動
RUN php artisan laravels publish --no-interaction
CMD ["php", "bin/laravels", "start", "--env=product"]
  • 安裝laravels的時候可以先註釋Dockerfile最後兩行,先讓環境啟動起來
    ## 把 [/mnt/d/xxxx] 替換成宿主機的程式碼目錄
    docker build . -t test-imagedocker run -d -it -p 80:5200 -v [/mnt/d/xxxx]:/var/dev  --name=test-service -w /var/dev test-image /bin/shdocker exec -it memory-service /bin/sh```
    

錯誤

  • 線上的機器一直報錯日誌
    [2022-01-01 02:29:45 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1641, id=5) abnormal exit, status=255, signal=0[2022-01-01 02:30:24 *1642.0]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65536 bytes) in /var/www/vendor/monolog/monolog/src/Monolog/DateTimeImmutable.php on line 22[2022-01-01 02:30:24 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1642, id=0) abnormal exit, status=255, signal=0[2022-01-01 02:31:07 *1646.2]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/nunomaduro/collision/src/Highlighter.php on line 138[2022-01-01 02:31:07 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1646, id=2) abnormal exit, status=255, signal=0[2022-01-01 02:31:14 *1647.3]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/nunomaduro/collision/src/Adapters/Laravel/Inspector.php on line 30[2022-01-01 02:31:14 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1647, id=3) abnormal exit, status=255, signal=0[2022-01-01 02:32:11 *1644.1]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/vendor/nunomaduro/collision/src/Writer.php on line 1[2022-01-01 02:32:11 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1644, id=1) abnormal exit, status=255, signal=0[2022-01-01 02:32:22 *1645.6]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65536 bytes) in /var/www/vendor/nunomaduro/collision/src/Writer.php on line 135[2022-01-01 02:32:22 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1645, id=6) abnormal exit, status=255, signal=0[2022-01-01 02:33:44 *1653.3]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/vendor/nunomaduro/collision/src/Writer.php on line 1[2022-01-01 02:33:44 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1653, id=3) abnormal exit, status=255, signal=0[2022-01-01 02:33:55 *1650.5]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/monolog/monolog/src/Monolog/Utils.php on line 141[2022-01-01 02:33:55 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1650, id=5) abnormal exit, status=255, signal=0[2022-01-01 02:34:22 *1652.2]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/vendor/filp/whoops/src/Whoops/Run.php on line 1[2022-01-01 02:34:22 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1652, id=2) abnormal exit, status=255, signal=0[2022-01-01 02:34:24 *1649.4]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/vendor/nunomaduro/collision/src/Writer.php on line 1[2022-01-01 02:34:24 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1649, id=4) abnormal exit, status=255, signal=0[2022-01-01 02:34:38 *1651.0]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/vendor/nunomaduro/collision/src/Writer.php on line 193[2022-01-01 02:34:38 $19.0]    WARNING    Server::check_worker_exit_status(): worker(pid=1651, id=0) abnormal exit, status=255, signal=0[2022-01-01 02:36:13 *1656.3]  ERROR  php_swoole_server_rshutdown() (ERRNO 503): Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/vendor/nunomaduro/collision/src/Adapters/Laravel/IgnitionSolutionsRepository.php on line 1```
    

官方文件的說明

關於記憶體洩露

  • 避免使用全域性變數,如一定要,請手動清理或重置。

  • 無限追加元素到全域性變數、靜態變數、單例,將導致記憶體溢位。

    class Test
    {
    public static $array = [];
    public static $string = '';
      // 某控制器
      public function test(Request $req)
      {
       // 記憶體溢位
       Test::$array[] = $req->input('param1');
       Test::$string .= $req->input('param2');
      }
    }
  • 記憶體洩露的檢測方法
    1. 修改config/laravels.phpworker_num=1, max_request=1000000,測試完成後記得改回去;
    2. 增加路由/debug-memory-leak,不設定任何路由中介軟體,用於觀察Worker程式的記憶體變化情況;

    Route::get('/debug-memory-leak', function () {
    global $previous; $current = memory_get_usage(); $stats = [ 'prev_mem' => $previous, 'curr_mem' => $current, 'diff_mem' => $current - $previous, ]; $previous = $current; return $stats; }); ```   3\. 啟動`LaravelS`,請求`/debug-memory-leak`,直到`diff_mem`小於或等於零;如果`diff_mem`一直大於零,說明`全域性中介軟體`或`Laravel框架`可能存在記憶體洩露;
    4\. 完成`步驟3`後,`交替`請求業務路由與`/debug-memory-leak`(建議使用`ab`/`wrk`對業務路由進行大量的請求),剛開始出現的記憶體增漲是正常現象。業務路由經過大量請求後,如果`diff_mem`一直大於零,並且`curr_mem`持續增大,則大概率存在記憶體洩露;如果`curr_mem`始終在一定範圍內變化,沒有持續變大,則大概率不存在記憶體洩露。
    5\. 如果始終沒法解決,[max_request](https://wiki.swoole.com/#/server/setting?id=max_request)是最後兜底的方案。
    ****
    

按照文件修改好配置並新增路由之後, 進入容器操作

docker exec -it memory-service /bin/shwatch -n 1 127.0.0.1:5200/debug-memory-leakEvery 1.0s: curl 127.0.0.1:5200/debug-memory-leak                                                                                                                                                                   2022-01-05 03:48:15
{"prev_mem":22620816,"curr_mem":22626592,"diff_mem":5776}
  • 不斷的觀察,發現diff_mem一直不等於0

  • 之前使用dcat-admin有過session問題,參考了github.com/hhxsv5/laravel-s/issues..., 然後這個問題把App\Providers\AppServiceProvider::class加入到了laravels.register_providers配置中

  • 然後檢視register_providers的邏輯在\Hhxsv5\LaravelS\Illuminate\CleanerManager::cleanProviders方法, 可以看到這裡呼叫了LaravelApplicatonregister方法,並且是強制register

  • 由於\Illuminate\Foundation\Application::register方法,當服務提供者註冊完畢之後, 會執行markAsRegistered

    protected function markAsRegistered($provider)
    {
    $this->serviceProviders[] = $provider;
    
    $this->loadedProviders[get_class($provider)] = true;
    }
  • php-fpm模式下沒什麼問題,但是在laravel-s加持下,laravel-s會把Application這個物件作為整個生命週期來執行. 這就導致了serviceProviders這個陣列會不斷的變大.

原文連結

本作品採用《CC 協議》,轉載必須註明作者和本文連結
當神不再是我們的信仰,那麼信仰自己吧,努力讓自己變好,不辜負自己的信仰!

相關文章