You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 5:ActionMailer的deliver_later在生产环境无法发送邮件

解决Rails deliver_later 异步任务仅入队不执行的问题(无需外部基础设施)

首先明确:你完全可以继续用默认的:async适配器,不用引入Sidekiq+Redis这类外部工具——问题出在进程内线程池的运行条件没满足,我们一步步解决:

核心原理先理清

:async适配器的任务队列是进程内内存队列,也就是说:任务只能被「入队的那个进程」处理。比如你在控制台入队的任务,只会在控制台进程里执行;服务器请求里触发的任务,只能由服务器进程的线程池处理。


情况1:你在Rails控制台里测试deliver_later

控制台是交互模式,主线程一直卡在等待输入状态,默认不会自动触发线程池处理任务。解决方法有两个:

  1. 手动触发任务执行:入队任务后,在控制台执行这行代码:

    ActiveJob::Base.execute_all_jobs
    

    这会立即处理当前队列里的所有任务。

  2. 让控制台自动处理任务:在config/environments/development.rb里添加配置,启动一个后台线程自动轮询队列:

    if defined?(Rails::Console)
      ActiveJob::Base.queue_adapter = :async
      Thread.new do
        loop do
          ActiveJob::Base.execute_all_jobs
          sleep 1
        end
      end
    end
    

    之后每次打开控制台,都会有个后台线程每隔1秒处理队列任务。


情况2:你在服务器请求中触发deliver_later

如果是接口/页面请求里调用,但任务没执行,大概率是服务器线程数不足:

  • 默认的WEBrick是单线程的,主线程被请求阻塞后,没有空闲线程处理Async队列;
  • 即使是Puma,如果配置了threads 1,1,也会出现同样问题。

解决方法:修改config/puma.rb,增加线程数:

threads 2, 4 # 最小2个线程,最大4个

这样Puma会预留额外线程来处理ActiveJob的异步任务。

另外,如果开发环境频繁重载代码(比如Spring自动重载),可能会重置线程池,重启服务器就能解决。


情况3:确认Async适配器确实被启用

检查config/application.rb或对应环境配置文件(比如config/environments/development.rb),确保没有被意外改成其他适配器:

# 确保这行存在(默认就是:async,也可以显式声明)
config.active_job.queue_adapter = :async

测试环境额外提示

如果是在RSpec等测试框架里用deliver_later,默认Async队列不会自动执行任务,你可以用测试内置的方法触发:

it "sends payout email asynchronously" do
  expect {
    PayoutMailer.payout_success_email('test@example.com', 200000, 'test name').deliver_later
  }.to have_enqueued_job(ActionMailer::DeliveryJob)

  # 执行所有入队任务
  perform_enqueued_jobs

  # 断言邮件发送结果
  expect(ActionMailer::Base.deliveries.last.to).to include('test@example.com')
end

内容的提问来源于stack exchange,提问作者nao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:38:40