内存持续上涨直到服务器宕机,关闭控制器常驻内存后正常

wangerwu

项目从开始就开启了控制器常驻内存。一直发现项目在服务器内存会上涨但不会降回去这个问题,但是幅度不大。

而且只要重启了项目就没了,所以就没怎么管。

在一个更新也比较频繁,每次都是启动一个新项目然后关闭旧项目,所以这个问题就没怎么管。

然后最近加了个接口给前端本地批量触发,所以这个问题就暴雷了,服务器内存会持续上涨且不会降回去,直到宕机。

人工+AI排查了,没有内存泄漏。然后AI指出可能是 controller_reuse = true 导致的问题,说的有理有据,我也反复用AI排查了很多次,答案都是一致的,而且确实关闭控制器常驻内存就解决了。

在开启控制器常驻内存的情况下前端本地几千条批量请求,服务器内存(8GB)就占满了,关闭后再测试也就只上涨1~3%的使用率。

下面为 AI 帮我的总结,虽然问题解决了,但是很想确认的知道问题出在哪,有什么可疑的点?AI的回答是正确的吗?

源码我没深入读,如果真是这样,那不同订单 Path 参数不一样导致路径不同,每个动态路由单独缓存一个控制器实例也许真是这个问题的根源。我技术水平有限,如果有不足之处请谅解。


Webman 动态路由 + controller_reuse 下的内存增长现象,想请社区帮忙确认

我这里遇到一个现象,不确定是不是我理解得对,所以先把实测情况发出来,想请社区一起确认。

环境

  • PHP >= 8.4
  • workerman/webman-framework v2.2.1
  • workerman/workerman v5.1.10
  • 常驻进程运行
  • HTTP worker 数量 16
  • 开启了 app.controller_reuse = true

实测现象

如果路由是这种带 path 参数的形式:

Route::post('/items/{id}/notify', [SomeController::class, 'notify']);

然后持续请求很多不同的实际路径,例如:

/items/1/notify
/items/2/notify
/items/3/notify
...

会出现下面的情况:

  • 内存持续上涨
  • 停止压测后,内存也不会明显回落
  • 轻控制器上涨不明显
  • 依赖比较重的控制器上涨非常明显

我这里同样是动态路由:

  • 一个很轻的测试控制器,请求几千次后,内存只涨很少
  • 一个依赖较重的控制器,请求几千个不同 path 后,内存涨幅很大

关闭 controller_reuse 后的情况

我把 controller_reuse 关闭后,问题明显缓解了。

不是完全不涨,但只会有少量上涨,不会像之前那样一路顶高,最后把服务器内存打满。

所以我现在的临时处理方案就是:

  • 先关闭 controller_reuse

但我理想上还是想开启它,所以想先确认这个现象是不是框架层面的正常行为。

我的猜测,但不确定对不对

我看源码时的理解是:

  • webman 似乎会按 method + path 缓存 callback
  • 如果是这样,那么动态路由下,不同的实际 path 值会生成不同缓存
  • controller_reuse = true 时,这些缓存里可能已经带上了控制器实例

如果我的理解没错,那么就可能出现这种情况:

  • path 值越多,缓存的 callback 越多
  • 控制器越重,每条缓存占用越大
  • 在常驻 worker 下,内存就会被不断推高

我不确定的地方

我不确定这是不是 webman 的预期设计,也不确定我的判断是否完全准确。

另外,我本地也有应用层的 path 级缓存,可能会放大这个现象。
所以我这里不是下结论,只是把实测结果和我的理解发出来,想请大家帮忙讨论一下:

  1. 我的理解对不对?
  2. 动态路由下,callback 缓存是不是确实按实际 path,而不是按路由模板?
  3. controller_reuse = true 时,缓存项里是不是会长期持有控制器实例?
  4. 如果这是正常行为,这种场景下官方推荐的做法是什么?

如果社区确认这是我的误判,我也接受。
我主要是想把这个现象描述清楚,看看是不是还有别人遇到过类似情况。

16 1 0
1个回答

walkor 打赏

callback 最多缓存1024个,不会无限缓存,所以这个缓存本身不会导致内存无限增大。

如果关闭控制器复用,则使用$container->make('控制器');获取控制器实例,所以每次都会重新创建新的实例。
如果开启控制器复用,则使用$container->get('控制器');获取控制器实例,所以每次进程内只会有这一个控制器对应的实例,所有请求共享。

如果复用控制器,并且业务会向空制器实例的数组属性里无限存储数据时可能会产生内存泄露。

另外发下 config/container.php 内容看下你用的是否是webman自带的container。

  • walkor 2小时前

    控制器复用之前压测过,没有内存泄露。
    刚刚本地压测,也没发现内存泄露。
    如果你能复现问题,请写一个能稳定复现问题的项目,打包发到我邮箱 walkor@workerman.net
    注意,是能复现问题的,我这能本地运行起来的,要求压测请求数达到100w后,继续压测内存还是无限增长这种。
    10w以内的压测内存有增长是正常情况,一般10w以后内存继续无限增长才有问题。

🔝