6.3 异步任务及邮件发送

    1. ActiveJob
    2. sidekiq
    3. ActionMailer

    在 Rails 4.2 之前,经常使用 Delayed Job,,Sidekiq 这三种异步服务,处理后端任务,比如邮件发送,报表计算,用户动态等等。

    这些任务具备一些特点:

    • 执行时间长,比如为所有关注我的用户创建好友动态。
    • 可以和前段操作分开执行,比如用户注册后,直接进入界面,而后端任务在稍后把欢迎邮件发出。
    • 调用其他应用的 api

    异步任务可以解决这些问题,但是三种常用的异步任务有各自的方法调用,Rails 4 中使用 来编写统一的操作代码,这样即便后端服务更换,也不用更改业务逻辑代码了。

    Sidekiq 使用 redis 储存任务,并且一个进程可以等于20个 Resque 或 DelayedJob 进程(官网上的说法)。

    redis 的安装非常简单,下载,进入 src 目录:

    这样一个命令就可以启动 redis 服务了。在生产环境下,可以针对文件位置等配置,可以增加一个 redis.conf 文件,启动时选择:

    这里我做了两个修改:

    1. dir ./db/redis/ [1]
    2. logfile ./log/redis.log [2]
    3. # requirepass foobar

    [1] 在我们的 db 下建立 redis 目录,放置 redis 数据库文件
    [2] redis 日志放入项目日志目录中
    [3] 我们在开发环境下去掉密码校验

    1. gem 'sidekiq'
    2. gem 'sinatra', :require => nil

    通常我们需要 sidekiq 的管理界面,来查看当前的任务队列情况,sinatra 可以单独启动这个管理服务, 我们修改一下 routes:

    config/routes.rb

    我们再增加一个 sidekiq 的配置文件 config/sidekiq.yml,然后运行它:

    1. sidekiq -C config/sidekiq.yml
    2. ss
    3. sss sss ss
    4. s sss s ssss sss ____ _ _ _ _
    5. s sssss ssss / ___|(_) __| | ___| | _(_) __ _
    6. s sss \___ \| |/ _` |/ _ \ |/ / |/ _` |
    7. s sssss s ___) | | (_| | __/ <| | (_| |
    8. ss s s |____/|_|\__,_|\___|_|\_\_|\__, |
    9. s s s |_|
    10. s s
    11. sss

    这样便启动了 sidekiq 服务,我们用它来完成异步任务。在用 Rails 使用 sidekiq 前,需要在 config/application.rb 声明一下:

    1. config.active_job.queue_adapter = :sidekiq

    我们用 generate 来创建一个任务类:

    1. rails generate job order_create

    OrderCreateJob 用来处理订单创建时,需要额外完成的一些操作,比如,向这个订单的用户发送一封 “订单已确认” 的邮件。我们使用 after_create 这个回调,来触发这个异步任务。

    1. class OrderCreateJob < ActiveJob::Base
    2. queue_as :default
    3. def perform(order)
    4. ...
    5. end
    6. end

    ActionMailer 是一个邮件发送 gem,它使用了 ActionController 类和 发送邮件,邮件可以使用视图文件,也可以是 txt 邮件。它也可以接收邮件,具体可参考手册 或 。

    我们来创建一个处理订单邮件发送的控制器:

    1. rails generate mailer OrderMailer
    2. create app/mailers/application_mailer.rb
    3. invoke erb
    4. create app/views/order_mailer
    5. create app/views/layouts/mailer.text.erb
    6. create app/views/layouts/mailer.html.erb
    7. invoke rspec
    8. create spec/mailers/order_mailer_spec.rb
    9. create spec/mailers/previews/order_mailer_preview.rb
    1. def confirm_email(order)
    2. @user = order.user
    3. @order = order
    4. mail(to: @user.email, subject: "您的订单 #{@order.number} 已经确认")
    5. end
    6. end

    ActionMailer 为我们创建了邮件模板和 html、text 两种格式的邮件,我们分别制作相同的内容,具体请参照 第六章代码。如果同时存在 html 和 text 视图,ActionMailer 会采用 Multipart 形式将他们发送出去。

    进入终端来测试邮件:

    deliver_later 是将邮件发送任务队列,deliver_now 是将邮件立刻发送。区别在于,deliver_later 不会阻塞当前进程,比如我们页面中会立刻进入下一个页面,而 deliver_now 会等待邮件发送完成,才会进行下一步。

    更 ActionMailer 的介绍请查看 。

    回到 OrderCreateJob, 我们把邮件发送加入到 perform 方法中

    1. class OrderCreateJob < ActiveJob::Base
    2. queue_as :default
    3. def perform(order)
    4. OrderMailer.confirm_email(order).deliver_now
    5. end

    因为我们已经使用异步任务,所以直接使用 deliver_now 发送邮件了。

    更多 ActionMailer 的配置,在 这里 有详细的介绍。

    sidekiq 可以完成其他异步的业务逻辑,比如确认订单后的积分计算,向关注我的好友发送动态等。因为我们在 routes 中增加了 sidekiq 的管理界面地址,所以访问 可以查看当前任务执行情况。