Laravel常驻进程内存泄漏

Laravel改造完整版传送门

背景

编写了一个常驻进程的脚本结果发现内存在不断上涨最后导致溢出,经过排查发现是日志的问题,之后做了一个测试如下:

for ($i = 0; $i < 1000; $i++) {
  	Log::info('[INFO]:' . $i);
    Log::error('[ERROR]:' . $i);
    Log::notice('[NOTICE]:' . $i);
    Log::debug('[DEBUG]:' . $i);
    if ($i%100==0) {
        var_dump(memory_get_usage());
    }
}

var_dump(memory_get_usage());

运行结果

4846848
5008736
5118264
5235984
5321256
5422592
5573080
5658032
5742984
5828256
5945368

由结果可以看出内存在不断上涨。

解决办法:

先附上解决办法执行:

一劳永逸

composer remove --dev facade/ignitio

手动释放

app()->make(LogRecorder::class)->reset();

参考地址: https://learnku.com/laravel/t/53565

分析过程如下:

1. 先上一个Xhprof对系统进行初筛

附 Xhprof 结果:

Laravel安装Xhprof教程

在这里插入图片描述

在这里插入图片描述

由此分析得出结果是由于logger导致的且与monolog无关。

2. 代码分析

2.1 大胆猜测

根据上图得知问题是由\Illuminate\Log\Logger::writeLog导致

protected function writeLog($level, $message, $context)
{
    $this->logger->{$level}($message = $this->formatMessage($message), $context);

    $this->fireLogEvent($level, $message, $context);
}

这里有一个事件触发,那么有可能是事件触发导致的。

protected function fireLogEvent($level, $message, array $context = [])
{
     // If the event dispatcher is set, we will pass along the parameters to the
     // log listeners. These are useful for building profilers or other tools
     // that aggregate all of the log messages for a given "request" cycle.
     if (isset($this->dispatcher)) {
         $this->dispatcher->dispatch(new MessageLogged($level, $message, $context));
     }
 }

2.2 细致排查

根据fireLogEvent方法得知是对MessageLogged的监听

全局搜索MessageLogged有关的监听逐一排查

在这里插入图片描述

应该是LogRecorder的问题

public function register(): self
{
      $this->app['events']->listen(MessageLogged::class, [$this, 'record']);

      return $this;
}

发现有回调record

public function record(MessageLogged $event): void
{
    if ($this->shouldIgnore($event)) {
        return;
    }

    $this->logMessages[] = LogMessage::fromMessageLoggedEvent($event);
}

先将其注释掉看看结果

结果发现内存不再增高,排查问题结束!

2.3 得出结论

结论

$this->logMessages[] = LogMessage::fromMessageLoggedEvent($event);

导致内存增高应该是没有地方释放,再看这个类存在reset

public function reset(): void
{
     $this->logMessages = [];
 }
2.3.1 方案一:手动释放

那么调用到这个应该就可以了。

app()->make(LogRecorder::class)->reset();

OK,问题解决,但这样不是长久之计。

2.3.1 方案二:一劳永逸

那么需要查找LogRecorder的调用。

\Facade\Ignition\IgnitionServiceProvider

public function boot()
{
    if ($this->app->runningInConsole()) {
        $this->publishes([
            __DIR__.'/../config/flare.php' => config_path('flare.php'),
        ], 'flare-config');

        $this->publishes([
            __DIR__.'/../config/ignition.php' => config_path('ignition.php'),
        ], 'ignition-config');
    }

    $this
        ->registerViewEngines()
        ->registerHousekeepingRoutes()
        ->registerLogHandler()
        ->registerCommands();

    if ($this->app->bound('queue')) {
        $this->setupQueue($this->app->get('queue'));
    }
    
    $this->app->make(QueryRecorder::class)->register();
    $this->app->make(LogRecorder::class)->register();
    $this->app->make(DumpRecorder::class)->register();
}


protected function setupQueue(QueueManager $queue)
{
      $queue->looping(function () {
          $this->app->get(Flare::class)->reset();

          if (config('flare.reporting.report_queries')) {
              $this->app->make(QueryRecorder::class)->reset();
          }

          $this->app->make(LogRecorder::class)->reset();

          $this->app->make(DumpRecorder::class)->reset();
      });
  }

发现只有Queue才会释放内存。。。。

查找了一圈除了改源码就剩手动不断释放了。

查找这个包的作用,最后发现只是报错展示使用,决定将包删除,问题彻底解决。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值