Rails 求一个定时任务的解决方案

tsinghan · 2013年09月11日 · 最后由 kimigao1986 回复于 2013年09月12日 · 9035 次阅读

现在有一个情景,用户可以在页面上配置监听时间,配置完成后,需要通过定时读取 api 去获取数据,然后返回给用户。我的思路是通过用户的配置时间,动态的写入定时任务中,如何实现?还是有其他的思路来解决这个问题?

没有完全理解需求,不过如果在页面上配置了监听时间,能否用 setInterval() 去定时发起 Ajax 请求到后端获取 API 数据呢?

#1 楼 @lgn21st 比如说 在页面上配置了时间频率是 30 分钟,那么我希望每各 30 分钟去跑一遍程序去调用外部的 API 往数据库塞数据,然后展现给用户。但是页面上的时间频率用户可以动态修改。

换个思路?服务器定时去 API 数据,取到之后塞数据库一份,然后推送给用户一份做展示。

用 cron 执行定时任务,每 30 分钟把设置 30 分钟的用户任务推入任务队列,不同设置的用户用不同的 cron。不同设置的用户还可以区分不同队列。

任务 worker 就是来任务就执行,注意监控队列大小,任务增长速度大于处理速度就要加线程/进程/机器。

#3 楼 @lvjian700 #4 楼 @Rei #1 楼 @lgn21st

真实场景,现在有不同的账户,类型有新浪微博,腾讯微博等,在账户里面用户可以设置一些参数,比如监听频率 (10 分钟),过滤条件 (是否过滤图片,是否是原创),当用户设置完这些条件后,我希望用设置的监听频率,加上设置的不同的过滤条件,去调用 api 然后返回给用户抓取回来的微博等内容。不知道这样说,大家更容易理解了吗。现在的问题是,可能会有不同的监听频率的账户并且带有不同的参数,这样我怎么用程序来写入到 cron 中?并且带上参数呢

这么想呢,给每种频率分别开定时任务的 worker,间隔为他的频率,然后触发之后,从数据库里取相应频率的 task,然后处理

好处是简单,你不需要给每个 task 单独设置 crontab,如果 task 数量巨大的话,你可以先取出所有的 task 然后再分发到子 worker 处理,感觉也不是很复杂

#6 楼 @jasl 有两个问题,1.如果用户只设置了几种频率的监听,给每种频率设置 worker? 2.每种账户设置的监听参数可能不同,这些参数,需要传给 api 接口, 我怎么传给定时任务?

我觉得从需求推导实现,有点复杂了。新浪微博,腾讯微博都有 API 的请求次数限制吧,你在限制以内,尽可能把用户的数据抓过来。数据在手了,然后按照用户所希望的方式显示给用户就好了。

我不太理解为什么要设置成 5 分钟或者 10 分钟或者更长时间这么复杂,难道数据同步不是越快越实时越好?

每分钟 起 cron,检查是否有任务需要执行,没有就结束了,有就执行。不过不知道是要获取实时数据,如果是实时的数据遇到 API 限制就又是个麻烦了

首先,你的定时请求去拿东西,是不是你页面 ui 里定时刷新的效果?

估计是。

就是用户选择刷新时间,存到数据库,每次用户打开界面,就去数据库拿到刷新的频率,用 js 写好定时刷新效果。

ajax 刷新去拿 api 的数据渲染,可以去你后台拿,然后你后台去 api 取返回浏览器。

以前做定时刷新 GPS 平台里的车辆实时信息,嘎嘎。

你没描述清楚的就是是否由用户打开某个网页时才有定时去抓取然后查看;还是,你后台定时去抓取,用户只一次打开浏览。

最简单的方法,数据库建立一个任务表,用户修改时间,就往这个表里面插入一条要具体在什么时间执行的记录。然后一个独立进程不停地查询这个表,看有什么记录已经超过了当前时间,执行一下,执行完以后,按用户设置的时间间隔,再插入一条。

常见的网页策略游戏都这么做,高端大气的就用个 KV store 或者消息队列。

#8 楼 @lgn21st 这个调用的 api 是由后端 java 来处理的,它负责返回给我数据,我来封装,会有延时的,不会那么频繁去调用。其实我的思路是能动态的创建定时任务,然后传入参数,去调用 api,但不知道能否实现呢

#10 楼 @badboy 不是,是希望根据设置的不同时间来执行后台定时任务,不需要前端来刷新的

gem 'rufus-scheduler'

#12 楼 @TsingHan 我觉得大家说的和你说的不冲突,他们的是存数据库里,你的是存系统里(crontab),他们的是用定时任务去轮询数据库,你的是系统自己去轮询自己的定时任务(猜测是这样),我觉得放在数据库这一层控制起来更方便一些。每分钟去问数据库,也不是每次都要调 java api,只是这一分钟有需要调的才去调,也没增加多大开销

根据不同的设置来创建定时任务,可行性不大,用户增多,定时任务会爆掉! 很是同意 @quakewang 方案,用户做个设置,增加条记录,记录包含时间间隔、下次执行时间等规则,另一进程不停查询这表中当前时间大于下次执行时间的记录,根据这条记录中规则请求 api,同时将下次执行时间设置为当前时间 + 时间间隔。

#11 楼 @quakewang #16 楼 @scys77 #17 楼 @kimigao1986 恩 这种方式应该挺好,而且维护数据库自己更容易控制,这个流程是不是可以这样,往数据库插入一条 5 分钟的记录并且设置执行时间为 5 分钟以后,进程轮询来扫描数据库的值,发现当前时间大于下次执行时间,就读取这条数据,并且按规则去调用 api,然后再将这条记录的执行时间设置成 5 分钟以后。

#15 楼 @lvjian700 #14 楼 @qichunren 恩 我先看一下 这个 gem

以前我工作的时候碰到个类似的任务,让用户绑定微博帐号,然后在个人页面显示最新微博。

后来我提议用微博嵌入挂件,改改样式,搞掂了。

#7 楼 @TsingHan 不是 比如 a 设置 1 分钟监听 b 设置 1 分钟监听 c 设置 2 分钟监听 那就是有一个全局的 1 分钟间隔的 job 一个全局的 2 分钟间隔的 job

1 分钟间隔的 job 触发后 发现 a、b 是一分钟触发,遂执行 2 分钟间隔的 job 触发后 发现 c 是他管的....执行之

这样就不涉及动态改 cron 了 如果请求数量大,那就把这些 job 当触发器用,开新的线程、进程去处理 防止阻塞

这个场景跟我想要做的有点像,我也来学习一下。 另外搭车问一下,有没有办法在 rails server 启动的时候同时把这个任务也开启,让它一直在后台跑?

发现很多问题贴,回复贴个 @hooopo 的 blog 地址就解决了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号