Pull to refresh

Фоновая обработка видео в Ruby on Rails

Ruby
Допустим у вас есть сайт, на котором пользователи загружают видео, и это видео надо конвертировать в flv формат.
Делать это непосредственно после загрузки, в текущем рабочем потоке не хорошо, т.к. потоков этих ограниченное число, и при большой нагрузке сайт будет недоступен.
Будет гораздо лучше, если обработкой этих видео-файлов займется демон.

Логика работы демона может быть следующей — демон выбирает необработанный объект из БД (например, используя флаг state, и сортируя по дате), после конвертирует соответствующий видео файл и обновляет запись в бд, выставляя статус в «обработано» (или «ошибка обработки»).
Для написания собственного демона можно воспользоваться библиотекой daemons (как ни странно). Демон будет состоять из 2-х частей: скрипт для управления фоновым процессом, и скрипт с логикой демона. В нашем случае это будут файлы scripts/video_converter_control и lib/video_converter.

scripts/video_converter_control:
require 'rubygems'
require 'daemons'

options = {:app_name => "video_converter",
:ARGV => ARGV,
:dir_mode => :script,
:dir => '../tmp/pids',
:log_output => false,
:multiple => false,
:ontop => false,
:mode => :load,
:backtrace => false,
:monitor => false
# daemons предлагает возможность мониторить состояние процесса (опция monitor), но я предпочитаю использовать monit
}

Daemons.run(File.join(File.dirname(__FILE__), '../lib/video_converter.rb'), options)


lib/video_converter:
require File.dirname(__FILE__) + '/../config/boot'
require RAILS_ROOT + "/config/environment"

SLEEP_TIME = 1

logger = RAILS_DEFAULT_LOGGER

loop do
video = Video.find(:first, :conditions => {:state => Video::PENDING_STATE}, :order => "id ASC")
success = video.process
if success.empty?
video.update_attribute(:state, Video::CONVERTED_STATE)
else
logger.info "VideoConverter: video #{video.id} converting failure"
video.update_attribute(:state, Video::FAILURE_STATE)
end
sleep(SLEEP_TIME)
end


Работать с демоном можно следующим образом:
estarter@ny $ ./script/video_converter_control status
video_converter: no instances running
estarter@ny $ ./script/video_converter_control start
estarter@ny $ ./script/video_converter_control status
video_converter: running [pid 5957]
estarter@ny $ ./script/video_converter_control stop
estarter@ny $ ./script/video_converter_control status
video_converter: no instances running


Теперь нарастим «мощь» конвертера, т.к. можно и нужно обрабатывать несколько видео-файлов одновременно. Самый простой вариант — запускать несколько демонов, однако при этом встает проблема синхронизации, т.к. нельзя допустить, что бы несколько процессов одновременно обрабатывали один файл. Это можно решить например «блокируя» запись в бд, выставляя соответствующий флаг (Video::PROCESSING_STATE).
Но если обработка происходит только на одном сервере, можно поступить умнее — делать потоке в основном демоне.
Для этих целей подходит плагин spawn, который позволяет просто создавать и работать с потоками. Теперь демон будет выбирать за раз несколько объектов, и обрабатывать их в отдельных потоках.

lib/video_converter:
require File.dirname(__FILE__) + '/../config/boot'
require RAILS_ROOT + "/config/environment"
include Spawn

THREAD_COUNT = 5 # максимальное количество одновременно обрабатываемых объектов, максимальное количество потоков
SLEEP_TIME = 1

logger = RAILS_DEFAULT_LOGGER
spawn_ids = []

loop do
videos = Video.find(:all, :conditions => {:state => Video::PENDING_STATE}, :order => "id ASC", :limit => THREAD_COUNT)
videos.each_with_index do |video, idx|
logger.info "VideoConverter: convert #{videos.size} videos"
spawn_ids[idx] = spawn do
success = video.process
if success.empty?
video.update_attribute(:state, Video::CONVERTED_STATE)
else
logger.info "VideoConverter: video #{video.id} converting failure"
video.update_attribute(:state, Video::FAILURE_STATE)
end
end
end
wait(spawn_ids)
sleep(SLEEP_TIME)
end


Данных подход можно использовать для целого ряда ресурсоемких задач.

Оригинал — в моем блоге.
Tags:railsrorruby on railsrubyфоновые процессыдемоны
Hubs: Ruby
Total votes 16: ↑15 and ↓1+14
Views2.4K

Popular right now