好久没更新博客了, 前一阵踩了一个openresty中使用timer的坑,特此记录一下。
0x0 背景
团队6月份在规划华北机房的事情,运维同事在华北部署了一套resync-broker 和 resync-agent, broker挂在了华东的server下面, 恰好华北机房内跑broker的机器只有8G内存,在运行一段时间后,容器直接OOM了。
0x1 现象
我们查看了华北环境的broker内存占用监控可以看到OOM前内存就在持续上涨,OOM后由于机制原因,K8S还会把服务拉起来,内存仍然是一个持续上涨的过程。
这现象有提醒我们看了一下华东集群里两个broker内存情况,内存其实也是在上涨的,只是机器的配置高,没有出现重启而已。
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也同样使用了这套机制,果然查看了监控,服务的内存占用也是在缓慢上涨,基本可以确定是这个问题了。
0x3 修改验证
我们修改了timer运行的逻辑,从之前用不退出的timer修改成运行24小时后,主动退出并重新创建timer, 大致的伪代码逻辑如下
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
修改后的版本上线后,观察监控,物理内存持续上涨问题应该已经解了。
然而新的坑又来了,仍然和内存有关,具体分析请看下一篇。