记一次使用 laravel-s 内存泄漏

环境

  • 使用的是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是最后兜底的方案。


按照文档修改好配置并添加路由之后, 进入容器操作

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 协议》,转载必须注明作者和本文链接
当神不再是我们的信仰,那么信仰自己吧,努力让自己变好,不辜负自己的信仰!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 1

插个眼,我这边也有这种线上报错。。。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!