【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

深入浅出理解 BackgrounDRb 插件

  • 2007-09-17
  • 本文字数:3313 字

    阅读完需:约 11 分钟

用 Ruby on Rails 开发各种类型的 Web 应用确实是很棒的选择,但是这些 Web 应用所在的问题领域中,你可能经常会遇到一些复杂精密的计算或者长时间运行的后台任务。但是由于你的 Web 应用被限制在 HTTP 协议的 request/response 模型下,这可能就会造成一些问题。你知道应该如何运行漫长的后台任务而不让你的 Web 服务器超时么?你又知道该如何把这些任务的进度告诉用户么?

作者写了一个叫做 BackgrounDRb 的 Rails 插件用来解决上面的问题。在 Ruby 的标准库中已经预制了 DRb(Distributed Ruby),为使用 TCP/IP 或 Unix sockets 通过网络存取 Ruby 对象提供了一个简单的 API。BackgrounDRb 提供了一个框架方便在 Rails 以外的独立线程中运行后台任务,从而摆脱了 request/response 模型。而且使用 DRb 你可以在 Rails 中使用钩子函数为用户提供任务进度或者状态更新。

BackgrounDRb 服务端程序通过发布一个 MiddleMan 对象来管理你所有的 woker 类,其中包括一个由{job_key => running_worker_object}键值对组成的 @jobs 和一个由{job_key => timestamp}键值对组成的 @timestamps 两个 hash,MiddleMan 对象是 DRb 服务器和你的 Rails 应用之间的一个接口。下面的图表简单说明了 BackgrounDRb 和 Rails 应用之间的关系。

下面是通过插件提供的 worker generator 脚本生成的一个 worker 类。

$ script/generate worker Foo<br></br> class FooWorker < BackgrounDRb::Rails<br></br> def do_work(args)<br></br> # This method is called in its own new thread when you<br></br> # call new worker. args is set to :args<br></br> end<p> end</p><br></br> 当 FooWorker 对象在 Rails 中通过 MiddleMan 初始化以后,do_work 方法会自动运行在它自己的线程中。由于 do_work 在自己的线程中运行,所以 Rails 不需要等待 do_work 完成就可以继续执行。

使用 BackgrounDRb,你经常会通过 AJAX 请求创建一个新的 worker 对象。在 View 中可以使用 periodically_call_remote 来取得任务的进度,再用你喜欢的方式展现给用户。接下来让我们补全刚才的 FooWorker 类,并告诉你如何在一个 rails controller 中创建新的 FooWorker 对象并获取它的进度。

class FooWorker < BackgrounDRb::Rails<br></br> attr_reader :progress<br></br> def do_work(args)<br></br> @progress = 0<br></br> calculate_the_meaning_of_life(args)<br></br> end<br></br> def calculate_the_meaning_of_life(args)<br></br> while @progress < 100<br></br> # calculations here<br></br> @progress += 1<br></br> end<br></br> end<br></br> end<br></br> 在 controller 中添加下面的代码:

class MyController < ApplicationController<br></br> def start_background_task<br></br> session[:job_key] =<br></br> MiddleMan.new_worker(:class => :foo_worker,<br></br> :args => "Arguments used to instantiate a new FooWorker object")<br></br> end<br></br> def get_progress<br></br> if request.xhr?<br></br> progress_percent = MiddleMan.get_worker(session[:job_key]).progress<br></br> render :update do |page|<br></br> page.call('progressPercent', 'progressbar', progress_percent)<br></br> page.redirect_to( :action => 'done') if progress_percent >= 100<br></br> end<br></br> else<br></br> redirect_to :action => 'index'<br></br> end<br></br> end<br></br> def done<br></br> render :text => "<h2>Your FooWorker task has completed</h2>"<br></br> MiddleMan.delete_worker(session[:job_key])<br></br> end<br></br> end<br></br> 再将下面的代码添加到你的 start_background_task.rhtml 视图中:

<p> <%= periodically_call_remote(:url => {:action => </p><br></br>'get_progress'}, :frequency => 1) %> MiddleMan.new_worker 方法会随机产生一个 job_key,你可以把它存在 session 中方便存取。如果你想指定 job_key 的名字可以使用下面的方法:

# This will throw a BackgrounDRbDuplicateKeyError if the :job_key already exists.<br></br> MiddleMan.new_worker(:class => :foo_worker,<br></br> :job_key => :my_worker,<br></br> :args => "Arguments used to instantiate a new FooWorker object")<p> MiddleMan.get_worker :my_worker </p> BackgrounDRb 安装之后还会生成一个配置文件 RAILS_ROOT/config/backgroundrb.yml。里面有一个 load_rails 配置选项,如果设置为 true 你就可以在 worker class 中使用你的 ActiveRecord 对象了,在 BackgrounDRb 服务启动的时候会自动根据 database.yml 中的设置去访问数据库。

这个插件还可以用于缓存类似 ActiveRecord object 这类大对象或者需要大量计算的对象,你也可以把渲染后的 View 对象或者大的查询进行缓存,事实上你可以缓存任何文本和任何可以被序列华的对象。下面是一个使用缓存的例子:

# Fill the cache<br></br> @posts = Post.find(:all, :include => :comments)<br></br> MiddleMan.cache_as(:post_cache, @posts)<br></br> # OR<br></br> @posts = MiddleMan.cache_as :post_cache do<br></br> Post.find(:all, :include => :comments)<br></br> end<p> # Retrieve the cache</p><br></br> @posts = MiddleMan.cache_get(:post_cache)<br></br> # OR<br></br> @posts = MiddleMan.cache_get(:post_cache) { Post.find(:all, :include => :comments) } MiddleMan.cache_get 接受一个可选的 block,如果缓存中的:post_cache 是空的,block 中的计算结果就会被放到 cache 中并赋给 @post。 如果你没有提供 block 而且缓存是空的则返回 nil。

在现在的实现中,你要自己负责对缓存过期,删除 worker 对象。有两种方法,一种是直接调用 MiddleMan.delete_worker(:job_key) 或者 MiddleMan.delete_cache(:cache_key),也可以将一个时间对象传给 MiddleMan.gc! ,删除所有在 timestamp 之前的 jobs(文章开始提到 MiddleMan 包括 @jobs 和 @timestamps 两个 hash)。下面的脚本可以删除 30 分钟以前的 jobs,你可以把它放在 cron 中执行:

#!/usr/bin/env ruby<br></br> require "drb"<br></br> DRb.start_service<br></br> MiddleMan = DRbObject.new(nil, "druby://localhost:22222")<br></br> MiddleMan.gc!(Time.now - 60*30) 在最新的特性中会有一个定时机制加入到 BackgrounDRb 中,这将允许你定时的运行你自己的任务和垃圾回收,或者在你创建一个新的 job 或 cache 的时候就定义一个存活时间的参数。

插件中还包含了一些命令行脚本用于启动 / 停止 BackgrounDRb,在 OS X、Linux 或者 BSD 上面可以使用 rake:

$ rake backgroundrb:start<br></br>$ rake backgroundrb:stop 在 Windows 上当你运行 BackgrounDRb 服务的时候要始终打开那个启动服务的命令行窗口(希望后面的版本可以有所改进)。所以在 Windows 上启动 BackgrounDRb 服务你要先打开一个命令行窗口,然后运行下面的命令:

> ruby script\backgroundrb\start<br></br> # ctrl-break to stop现在你可能会问这东西在现实中究竟可以用在什么地方?在下面的列表中作者告诉了你,他正在用 BackgrounDRb 做什么:

  • 下载并缓存 RSS,这样可以做一个 RSS 聚合器。
  • 使用 watir 驱动浏览器在后来访问网站并收集信息,做自动的屏幕抓取。
  • Xen VPS 的自动创建和系统管理任务。
  • 后台为 Hyper Estraier 和 erret 创建索引。
  • 连接 Rails 和 IRC 机器人。

作者在后续版本中还计划加入创建新进程的能力,以便能处理需要 Ruby 解释器实例的更大的任务。在 Windows 上希望可以作为 service 运行,希望熟悉 Windows service 的人能提供一些帮助,任何建议和补丁都非常欢迎。

查看英文原文: Introduction to BackgrounDRb - - - - - -

作者简介:苏锐,Ruby on Rails 开发者,关注各种 Web 开发技术,Mac 爱好者。他的博客为: http://www.surui.net 。参与 InfoQ 中文站内容建设,请邮件至 china-editorial@infoq.com

2007-09-17 07:211970

评论

发布
暂无评论
发现更多内容

Github标星5.3K,YGC问题排查,又让我涨姿势了

JVM调优资料

Java 程序员 面试 后端

Java入门你值得拥有!同一个Spring-AOP的坑

JVM调优资料

Java 程序员 面试 后端

Java工作资料!Java开发基础知识学习总结之(上

Java 程序员 面试 后端

Java开发面试准备,【备战秋招冲击大厂

策划Java工程师

Java 程序员 面试 后端

2021必看!热榜!基于jsp

JVM调优资料

Java 程序员 面试 后端

IBM大面积辞退40岁+的员工,Java泛型详解

JVM调优资料

Java 程序员 面试 后端

2021程序员进阶宝典!Java程序员:

JVM调优资料

Java 程序员 面试 后端

2021Java面试总结!再见笨重的ELK

Geek_f90455

Java 程序员 面试 后端

2021Java面试笔试总结!Flutter中的widget

Geek_f90455

Java 程序员 面试 后端

Java开发热门前沿知识!Java集合中的基本数据结构

策划Java工程师

Java 程序员 面试 后端

2021Java进阶新篇章,狂刷1个月Java面试题

Geek_f90455

Java 程序员 面试 后端

GitHub标星8k!你以为在做的是微服务?不

JVM调优资料

Java 程序员 面试 后端

Java入门视频教程!什么是JVM?

Java 程序员 面试 后端

Java小技巧:Oracle存储过程常用技巧

Java 程序员 面试 后端

Java开发视频教程!MySQL8

策划Java工程师

Java 程序员 面试 后端

iOS开发:解决App进入后台,倒计时(定时器)不能正常计时的问题

三掌柜

8月日更 8月

2021年您应该知道的技术之一!MySQL最全整理

Geek_f90455

Java 程序员 面试 后端

Java小程序开发实例!docker容器启动后修改或添加端口

Java 程序员 面试 后端

Java开发必须掌握!Java虚拟机(JVM

策划Java工程师

Java 程序员 面试 后端

Java开发指南!Redis高频面试笔记:基础

策划Java工程师

Java 程序员 面试 后端

Java开发经验谈:动手造轮子:实现一个简单的-AOP-框架

策划Java工程师

Java 程序员 面试 后端

Java开发面试问题,Java中高级核心知识全面解析(2)

策划Java工程师

Java 程序员 面试 后端

2021Java大厂高频面试题:Redis面试题及答案整理

Geek_f90455

Java 程序员 面试 后端

Java开发入门教程!你技术这么好,总要改变点什么把

Java 程序员 面试 后端

Java开发实战!不会吧

策划Java工程师

Java 程序员 面试 后端

2021非科班生的Java面试之路,set集合

JVM调优资料

Java 程序员 面试 后端

Java基础入门教程!Java垃圾回收机制小结以及优化建议

Java 程序员 面试 后端

Java并发原理解析!我们来捋一捋JAVA的异常

Java 程序员 面试 后端

Java开发6年了,你确定你真的理解_双亲委派_了吗?

Java 程序员 面试 后端

Druid 独立服务器方式部署文档

HoneyMoose

路边的小店

箭上有毒

8月日更

深入浅出理解BackgrounDRb插件_Ruby_Ezra Zygmuntowicz_InfoQ精选文章