在项目中有一个定时通知用户的需求。在某个固定时间段,间隔 5 分钟去通知。 因此想到了 linux 中的 cron 定时任务,ruby 中有相应的 gem,想必大家用到的也蛮多 whenever
定时任务 周期性执行 Model 的一个实例方法,里面做的操作是迭代数据表,使用 sikekiq 的 delay 方法异步执行某个发送消息的操作。这个迭代相对耗时,执行完毕大概在 30s 左右。
但是最近服务器出现内存被吃完的情况,经过定位发现有很多的 cron 进程没有被操作系统回收,大概 200 个左右(定时任务执行的次数)。
这样将近吃掉了服务器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
进程都可以被及时回收。
不明白的是为什么会阻塞?