Rails 5:ActionMailer的deliver_later在生产环境无法发送邮件
解决Rails
deliver_later 异步任务仅入队不执行的问题(无需外部基础设施) 首先明确:你完全可以继续用默认的:async适配器,不用引入Sidekiq+Redis这类外部工具——问题出在进程内线程池的运行条件没满足,我们一步步解决:
核心原理先理清
:async适配器的任务队列是进程内内存队列,也就是说:任务只能被「入队的那个进程」处理。比如你在控制台入队的任务,只会在控制台进程里执行;服务器请求里触发的任务,只能由服务器进程的线程池处理。
情况1:你在Rails控制台里测试deliver_later
控制台是交互模式,主线程一直卡在等待输入状态,默认不会自动触发线程池处理任务。解决方法有两个:
手动触发任务执行:入队任务后,在控制台执行这行代码:
ActiveJob::Base.execute_all_jobs这会立即处理当前队列里的所有任务。
让控制台自动处理任务:在
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
相关产品推荐
相关产品推荐

