一次OR timer使用不当导致的内存问题记录

好久没更新博客了, 前一阵踩了一个openresty中使用timer的坑,特此记录一下。

0x0 背景

团队6月份在规划华北机房的事情,运维同事在华北部署了一套resync-broker 和 resync-agent, broker挂在了华东的server下面, 恰好华北机房内跑broker的机器只有8G内存,在运行一段时间后,容器直接OOM了。

0x1 现象

我们查看了华北环境的broker内存占用监控可以看到OOM前内存就在持续上涨,OOM后由于机制原因,K8S还会把服务拉起来,内存仍然是一个持续上涨的过程。

image

这现象有提醒我们看了一下华东集群里两个broker内存情况,内存其实也是在上涨的,只是机器的配置高,没有出现重启而已。

image
image

0x2 问题分析

这里要说一下resync-broker做的工作,resync这套服务是基于Openresty开发的,用来将华东环境产生的识别/语言资源定制文件同步到各个服务节点的。

broker服务内部使用了队列,broker从华东server同步过来文件,然后将文件分发给机房内的agent节点。在具体的实现时,服务启动时会创建两类定时任务

  • 第一类是producer timer,其实只有一个,它负责从华东server拉取文件同步的列表,然后塞入队列中。
  • 第二类是consumer timer, 每个worker有一个,它负责消费任务队列的任务元信息,然后去server上把文件下载到机房内。

两类timer都是循环,在循环内会频繁地向server发送http请求,初步怀疑是http请求创建的资源无法回收,导致内存持续上涨。通过搜索,从openresty的issues里春哥的回复也得到了印证。

在openresty中,timer是模拟的http请求,我们知道openresty中资源会在请求结束后由gc回收,在循环中处理业务逻辑申请的资源同样的要等到timer退出后才能回收,但是当timer中我们写成了死循环后这部分的资源gc是一直无法回收的。

由于agent也同样使用了这套机制,果然查看了监控,服务的内存占用也是在缓慢上涨,基本可以确定是这个问题了。

image

0x3 修改验证

我们修改了timer运行的逻辑,从之前用不退出的timer修改成运行24小时后,主动退出并重新创建timer, 大致的伪代码逻辑如下

1
2
3
4
5
6
7
8
9
10
local function loop()
while start_time + restart_interval > ngx.time() do
long_running_producer_bussiness()
end
end

local function produce(premature, loop)
loop()
ngx.timer.at(0, produce, loop)
end

修改后的版本上线后,观察监控,物理内存持续上涨问题应该已经解了。
image

然而新的坑又来了,仍然和内存有关,具体分析请看下一篇。

您的支持将鼓励我继续创作!