新手问题 cron 进程在任务执行结束后不被操作系统回收

mumuxizzz · 2018年07月27日 · 最后由 mumuxizzz 回复于 2018年07月28日 · 2864 次阅读

问题描述

在项目中有一个定时通知用户的需求。在某个固定时间段,间隔 5 分钟去通知。 因此想到了 linux 中的 cron 定时任务,ruby 中有相应的 gem,想必大家用到的也蛮多 whenever

定时任务 周期性执行 Model 的一个实例方法,里面做的操作是迭代数据表,使用 sikekiq 的 delay 方法异步执行某个发送消息的操作。这个迭代相对耗时,执行完毕大概在 30s 左右。

但是最近服务器出现内存被吃完的情况,经过定位发现有很多的 cron 进程没有被操作系统回收,大概 200 个左右(定时任务执行的次数)。

很多个cron进程

这样将近吃掉了服务器3 个多 G的物理内存。

临时的解决方案

批量杀进程

思考

top 命令查看,发现没有僵尸进程

本地重现,也没有问题。进程也会被操作系统回收。

补充

whenever 配置文件中指定了输出文件

set :output, "#{Rails.root}/log/cron.log"

但是本地测试的在cron.log文件中看到了Running via Spring preloader in process xxx(pid)

服务器上就看不到,但是确实有很多的 cron 进程。😰

麻烦各位大佬帮忙给看看是什么原因😯

更新

现在定位到原因了,因为有一个和 ruby 有关的僵尸进程。

ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
root     13788  2455  0 Jul26 ?        00:00:19 [ruby] <defunct>

# 产生僵尸进程的元凶(父进程)
ps -ef | grep 2455
root      2455     1  0 May07 ?        00:00:14 spring server | okaoyan-news | started 1986 hours ago

spring的 ReadMe 中有一句话介绍到:

Spring makes extensive use of Process.fork, so won't be able to provide a speed up on platforms which don't support forking (Windows, JRuby).

通过 fork 创建子进程。

rails runner rails generate rails console rails rake 都会用到 spring preloader

因为这个僵尸进程,所有的 cron 中周期性的rails runner任务都被阻塞了。所以就一直占着系统资源。

疑惑

按道理讲,当产生了很多的僵尸进程后,会占用大量的进程号导致系统不可用。现在只有一个僵尸进程。但是杀掉僵尸进程的父进程后。一切正常了。 cron以及rails runner进程都可以被及时回收。

不明白的是为什么会阻塞?

为什么是 rails runner?不用 rake 么

建议用 timeout 包裹该类方法,然后看下输出日志,排查问题,whenever 就是利用 crontab,不太可能存在问题,另外这种周期性利用 sidekiq 的任务,可以用 sidekiq-cron。

IChou 回复

嗯,rake 也可以,比 rails runner 更合适。但是问题应在不在这里。我在 stackoverflow 发现我遇到的问题,但是还没有人回答。

会不会 WxCommon.notify_user_sign 没有设置 timeout 卡死了。

Rei 回复

很有可能,@dfzy5566 也这么建议。我先捕获一下Timeout::Error异常。明天观察一下日志。多谢 Rei 大佬。🌴

dfzy5566 回复

感谢,我试试看。

mumuxizzz 关闭了讨论。 07月30日 10:19
需要 登录 后方可回复, 如果你还没有账号请 注册新账号