Запуск нескольких фоновых параллельных заданий с помощью Rails

В моем приложении Ruby on Rails мне нужно параллельно выполнять 50 фоновых заданий. Каждое задание создает TCP-соединение с другим сервером, обрабатывает некоторые данные и обновляет активный объект записи.

Я знаю разные решения для выполнения этой задачи, но любое из них параллельно. Например, delayed_job (DJ) мог бы стать отличным решением, если бы он мог выполнять все задания параллельно.

Любые идеи? Спасибо.


person fjyaniez    schedule 22.10.2009    source источник
comment
Другое решение здесь: stackoverflow.com/questions/16551466/   -  person Subhas    schedule 16.06.2013
comment
Интересно, спасибо @RDX   -  person fjyaniez    schedule 17.06.2013


Ответы (3)


Некоторые мысли...

  • Тот факт, что вам нужно прочитать 50 сайтов и, естественно, нужна параллельная работа, не означает, что вам нужно 50 процессов или потоков. Вам нужно сбалансировать замедление и накладные расходы. Как насчет того, чтобы 10 или 20 процессов читали несколько сайтов каждый?

  • В зависимости от того, какой Ruby вы используете, будьте осторожны с зелеными нитями, вы можете не получить желаемого параллельного результата.

  • Возможно, вы захотите структурировать его как обратный inetd на стороне клиента и использовать connect_nonblock и IO.select для получения желаемых параллельных соединений, заставив все серверы отвечать параллельно. Вам не нужна параллельная обработка результатов, вам просто нужно встать в очередь на всех серверах параллельно, потому что именно там действительно есть задержка.

Итак, что-то вроде этого из библиотеки сокетов... расширьте ее для нескольких незавершенных соединений...

require 'socket'
include Socket::Constants
socket = Socket.new(AF_INET, SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(80, 'www.google.com')
begin
  socket.connect_nonblock(sockaddr)
  rescue Errno::EINPROGRESS
  IO.select(nil, [socket])
  begin
    socket.connect_nonblock(sockaddr)
    rescue Errno::EISCONN
  end
end
socket.write("GET / HTTP/1.0\r\n\r\n")
# here perhaps insert IO.select. You may not need multiple threads OR multiple
# processes with this technique, but if you do insert them here
results = socket.read
person DigitalRoss    schedule 22.10.2009
comment
IO.select может быть полезен в этом случае, я попробую. Спасибо. - person fjyaniez; 23.10.2009

На самом деле можно запустить несколько воркеров delayed_job.

Из http://github.com/collectiveidea/delayed_job:

# Runs two workers in separate processes.
$ RAILS_ENV=production script/delayed_job -n 2 start
$ RAILS_ENV=production script/delayed_job stop

Итак, теоретически вы можете просто выполнить:

$ RAILS_ENV=production script/delayed_job -n 50 start

Это создаст 50 процессов, однако я не уверен, будет ли это рекомендовано в зависимости от ресурсов системы, в которой вы это используете.


Альтернативным вариантом может быть использование тредов. Просто создайте новый поток для каждого из ваших заданий.

Одна вещь, которую следует учитывать при использовании этого метода, заключается в том, что ActiveRecord не является потокобезопасным. Вы можете сделать его потокобезопасным, используя следующую настройку:

ActiveRecord::Base.allow_concurrency = true
person Olly    schedule 22.10.2009
comment
Вы также можете запускать воркеры delayed_job на нескольких машинах. Я сомневаюсь, что вы получите большую выгоду от запуска большего количества рабочих процессов на одной машине, чем у вас есть ядер ЦП, но вы можете распределить нагрузку, запустив ее на нескольких компьютерах. Если вам нужно запустить 50 одновременно, я думаю, вам нужно будет распределить работу. - person Luke Francl; 22.10.2009
comment
Я получу выгоду от запуска нескольких рабочих процессов на одной машине, потому что большинство рабочих процессов будут заблокированы вводом-выводом. - person fjyaniez; 23.10.2009

Поскольку вы работаете с рельсами, я бы посоветовал вам использовать для этого delayed_job, а не разбиваться на потоки или вилки. Причина в том, что работа с тайм-аутами и прочим, когда браузер ждет, может быть настоящей болью. Есть два подхода, которые вы можете использовать с DJ

Первый - создать 50+ рабочих. В зависимости от вашей среды это может быть довольно тяжелым решением для памяти, но оно отлично работает. Затем, когда вам нужно запустить свою работу, просто убедитесь, что вы создали 50 уникальных заданий. Если памяти слишком много и вы хотите сделать что-то таким образом, создайте отдельную урезанную среду специально для ваших воркеров.

Второй способ — создать одно задание, использующее Curl::Multi для выполнения ваших 50 одновременных TCP-запросов. Подробнее об этом можно узнать здесь: http://curl-multi.rubyforge.org/ Таким образом, у вас может быть один фоновый процессор, выполняющий все ваши TCP-запросы параллельно.

person PatrickTulskie    schedule 27.10.2009