如何让Delayed Job自动入队?Rails中Azure媒体服务视频更新任务
嘿,我刚好做过类似的Rails+Azure媒体服务集成,针对你用Delayed Job处理视频状态持续更新的需求,给你几个实用的方案,从易实现到最优解都有:
方案1:任务自调度(最适配Delayed Job的常规玩法)
核心思路就是让任务自己检查视频状态,如果还在处理中,就重新调度自己,直到拿到最终的可用/失败状态。给你补全下你的Job代码:
class UpdateAzureVideosJob < ApplicationJob queue_as :azure_media_service def perform # 只捞处于处理中的视频,别瞎折腾已完成的 pending_videos = AzureVideo.where(status: :processing) pending_videos.each do |video| # 调用Azure API拿当前处理状态 azure_status = fetch_azure_video_status(video.azure_asset_id) case azure_status when "Available" video.update!(status: :available, processed_url: get_azure_processed_url(video)) when "Failed" video.update!(status: :failed, error_message: get_azure_error_details(video)) else # 还在处理?10分钟后再跑一次这个任务(间隔根据你的平均处理时长调) self.class.set(wait: 10.minutes).perform_later # 批量处理的话,只调度一次就够,别每个视频都发任务 break end end end private def fetch_azure_video_status(asset_id) # 这里写调用Azure媒体服务API的逻辑,不管是用官方gem还是自己写HTTP请求都行 end def get_azure_processed_url(video) # 从Azure拿处理后的视频流地址 end def get_azure_error_details(video) # 拿失败原因 end end
为啥这个好用?
- 完全贴合Delayed Job的工作逻辑,不用额外搞进程管理
- 可以灵活调整轮询间隔,避免频繁怼Azure API被限流
- 批量处理待更新视频,减少任务数量,效率更高
方案2:单视频独立任务+重试机制
如果你的视频处理时长差异很大,不想批量处理拖慢进度,可以给每个视频单独开任务,利用Delayed Job的重试特性:
class UpdateAzureVideoJob < ApplicationJob queue_as :azure_media_service # 最多重试24次(每10分钟一次就是4小时,够大部分视频处理完了) retry_on VideoProcessingInProgressError, attempts: 24, wait: 10.minutes def perform(video_id) video = AzureVideo.find(video_id) # 已经完成的直接溜,别做无用功 return if video.status.in?([:available, :failed]) azure_status = fetch_azure_video_status(video.azure_asset_id) case azure_status when "Available" video.update!(status: :available, processed_url: get_azure_processed_url(video)) when "Failed" video.update!(status: :failed, error_message: get_azure_error_details(video)) else # 抛个自定义异常触发重试 raise VideoProcessingInProgressError, "Video #{video_id} still being processed" end end class VideoProcessingInProgressError < StandardError; end end
适用场景:
- 每个视频的处理时长差异大,批量处理可能导致快完成的视频被慢的拖后腿
- 想精细化控制单个视频的重试次数和间隔
方案3:Azure Webhook(最优解,彻底告别轮询)
其实Azure媒体服务支持状态变更Webhook,视频处理完(成功/失败)时,Azure会主动发请求到你的Rails接口,完全不用你主动轮询,效率拉满。
实现步骤:
- 先在Azure媒体服务后台配置Webhook,指向你的Rails接口(比如
POST /azure_webhooks/video_status) - 在Rails里写个控制器处理这个Webhook请求:
class AzureWebhooksController < ApplicationController skip_before_action :verify_authenticity_token # 划重点:一定要验证Azure的请求签名!别啥请求都接,防止被伪造 def video_status payload = JSON.parse(request.body.read) asset_id = payload["assetId"] status = payload["status"] video = AzureVideo.find_by(azure_asset_id: asset_id) if video case status when "Completed" video.update!(status: :available, processed_url: payload["outputUrl"]) when "Failed" video.update!(status: :failed, error_message: payload["errorDetails"]) end end head :ok end end
- 之后你只需要在视频上传到Azure后,跑一次初始状态检查任务,剩下的就等Azure主动通知就行。
为啥这是最优解?
- 完全不用轮询,省资源还没延迟
- 状态变了立刻响应,用户体验更好
- 减少Delayed Job的任务量,降低系统负载
生产环境必注意的坑
- 队列监控:整个
delayed_job_web这类工具盯着队列,别让任务丢了都不知道 - 错误处理:不管用啥方案,都要处理Azure API调用失败的情况(比如网络波动),别让任务无限重试
- 幂等性:确保任务重复执行不会搞乱数据(比如别把已标记为可用的视频再更一遍)
- 队列隔离:把Azure相关的任务放到单独的
azure_media_service队列,别和其他业务任务抢资源
内容的提问来源于stack exchange,提问作者bassment
相关产品推荐
相关产品推荐

