使用Delayed job处理Rails中的异步任务

130 篇文章 0 订阅

转自:http://hi.baidu.com/jiazom/item/37dcc4eb6cc136324cdcaf3a

使用Delayed job处理Rails中的异步任务

    Rails cast上有关于介绍Delayed job的使用范例的:使用delayed job处理一个异步发送邮件的任务。我没仔细看。然后自己弄了起来。记录过程如下,希望给后来者提供一些帮助。

    首先介绍下应用场景:在WEB应用中,系统执行某些任务时,可能执行结果比较长,并且不需要及时把结果反馈给用户。这样我们就需要将这些处理时间长的请求剥离成异步处理,及时响应页面的请求。比如Rails cast中示例的发邮件。可能发邮件需要2到10秒钟不等。这个请求如果采用同步处理,会让用户觉得卡了一下的感觉,体验不好。如果采用异步处理,只需要记录需要处理这个任务,然后将任务记录在某个地方,再由后台程序去调度执行这些任务,这样就可以在极短时间(如果其他代码没有问题)内给用户反馈信息,这样体验就会好很多。

    我的应用场景是:一个用例在修改名称后,需要将名称同步到脚本相关的Excel中。修改Excel的过程比较长,至少会有3秒以上。这样就需要将这个修改异步出去。我的环境是ruby  + Rails 3 + Mysql。

    在github上搜索delayed job。可以得到好多个分支的delayed job插件。collectiveidea 分支的不错,可以自动建立migration和script任务。

     原理如下:在Object对象上扩展了一个delay方法。如果遇到任何需要异步处理的函数,就使用对象.delay.方法名。比如正常代码为:Mail.send_mail(@mail) ,异步处理即为:Mail.delay.send_mail(@mail)。delay函数中利用ruby的动态性,即将send_mail方法异步调用:先写入到数据库的表delayed_jobs,生成一条记录,worker进程会每隔5秒去检查一下数据库这张表的记录,如果有记录就去读取出来处理掉,然后再删除记录。有点像消息队列的处理机制。废话完毕,开干~~~

    1、在Gemfile里面配置:gem 'delayed job'。然后在工程目录下执行:sudo bundle install

    2、执行ruby script/generate delayed_job 。会创建Model和Migration。执行rake db:migration即可建立数据库表。

    3、启动worker:ruby scrpt/delayed_job。这个进程就是去定时读取数据库任务的。

    4、在需要处理的函数上都加上.delay.方法名。

    大功告成。

    有个小问题需要注意:Rails中启动应用会有不同的环境,为了让delayed_job这个worker找到正确的数据库,需要设置系统的环境变量,执行:export RAILS_ENV=online_production。这样再启动delayed job的worker进程时,就会去读取online_production对应的数据库。

      delayed_job值得提及的特性就是自动重试功能。如果一个方法抛出了异常,这个异常会被捕获,之后这个方法将会重新运行。这个过程最多重复25次,次数越多重试的时间间隔就越长。

     在分布式中,worker进程只需要启动一个即可。不需要每个机器都启动worker去读取数据库的任务去执行。在异步任务不是太多时,一个进程完全够用。并且,时间瓶颈也并非在一个进程还是在两个进程上,主要还是在处理异步任务的代码上。另外,需要说明的是,worker启动时可以指定多个实例。还有其他一些参数,运行:ruby script/delayed_job可以看到帮助。

在Java,可以使用Redis的Sorted Set(有序集合)来实现延时发布任务。下面是一个简单的示例代码: ```java import redis.clients.jedis.Jedis; import java.util.Set; public class DelayedTaskPublisher { private static final String REDIS_KEY = "delayed_tasks"; public static void main(String[] args) { // 创建Redis连接 Jedis jedis = new Jedis("localhost"); // 模拟发布4个延时任务 publishDelayedTask(jedis, "task1", 5000); publishDelayedTask(jedis, "task2", 10000); publishDelayedTask(jedis, "task3", 15000); publishDelayedTask(jedis, "task4", 20000); // 开始处理延时任务 processDelayedTasks(jedis); // 关闭Redis连接 jedis.close(); } private static void publishDelayedTask(Jedis jedis, String task, long delayMillis) { // 计算任务的过期时间 long expireTime = System.currentTimeMillis() + delayMillis; // 将任务添加到有序集合,分数为过期时间,值为任务内容 jedis.zadd(REDIS_KEY, expireTime, task); } private static void processDelayedTasks(Jedis jedis) { while (true) { // 获取当前时间戳 long currentTime = System.currentTimeMillis(); // 获取所有已过期的任务 Set<String> expiredTasks = jedis.zrangeByScore(REDIS_KEY, 0, currentTime); // 处理已过期的任务 for (String task : expiredTasks) { System.out.println("处理延时任务:" + task); // 从有序集合移除已处理任务 jedis.zrem(REDIS_KEY, task); } // 休眠一段时间后再次检查是否有延时任务到期 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 这个示例代码演示了如何使用Redis的Sorted Set来实现延时发布任务。首先,通过`publishDelayedTask`方法发布了4个延时任务,每个任务设定了不同的延时时间。然后,在`processDelayedTasks`方法,通过循环和睡眠,不断检查是否有任务已经过期,并进行处理处理完成后,将任务从有序集合移除。 请注意,在实际生产环境,可能需要进行更多的优化和异常处理。此示例仅为演示基本概念和流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值