2012-01-02 3 views
1

Я хочу, чтобы дать своим пользователям возможность послать им ежедневную сводку своих статистических данных аккаунта в конкретных (пользователь дал) время ....Rails 3.1/грабли - datespecific задачи без очередей

Позволяет сказать следующую модель :

class DailySummery << ActiveRecord::Base 
    # attributes: 
    # send_at 
    # => 10:00 (hour) 
    # last_sent_at 
    # => Time of the last sent summary 
end 

Есть ли передовая практика, как отправить резюме по электронной почте на определенное время?

В настоящее время у меня есть бесконечная задача рейка, которая проверяет, постоянно ли электронные письма доступны для отправки, и я хотел бы поместить генерация dailysummary и отправить в эту команду rake.

У меня была мысль, что я мог бы решить эту проблему с следующий псевдокод:

while true 
    User.all.each do |u| 
    u.generate_and_deliver_dailysummery if u.last_sent_at < Time.now - 24.hours 
    end 
    sleep 60 
end 

Но я не уверен, если это имеет некоторые скрытые подводные камни ...

Примечание: Я не хочу использовать очереди, такие как resq или redis или что-то в этом роде!

EDIT: Добавлен сон (есть это уже в моем сценарии)

EDIT: Это критическое время обслуживание (уведомление о торговом ставке), поэтому она должна быть как можно быстрее. Вот почему я не хочу использовать систему на основе очереди или работы. И я использую Monit для управления этой граблиной задачей, которая работает очень хорошо.

+1

Это именно то, что делают фоновые очереди заданий. Они создают длительный процесс (отложенное задание использует задачу рейка, точно так же, как вы предлагаете), которая периодически запускает задачу. – tee

+0

Это критически важный сервис (уведомление о ставках торговли), поэтому оно должно быть как можно быстрее. Вот почему я не хочу использовать систему на основе очереди или работы. – Lichtamberg

ответ

1

Я вижу две возможности для выполнения задачи в определенное время.

Фоновый процесс/рабочий/...

Это то, что вы уже сделали. Я реорганизовал ваш пример, потому что было две плохие вещи.

  1. Проверить условия непосредственно из базы данных, это более эффективно, чем загрузка потенциальных бесполезных данных
  2. пользователей Загрузка по партии. Представьте, что ваша база данных содержит миллионы пользователей ... Я почти уверен, что вы будете счастливы, но не Rails ... совсем нет. :)

Помимо вашего кода, я вижу другую проблему. Как вы собираетесь управлять этим фоновым заданием на своем производственном сервере? Если вы не хотите использовать Resque или что-то еще, вам следует подумать об этом по-другому. Существует Monit и God которые являются мониторами процессов.

while true 
    # Check the condition from your database 
    users = User.where(['last_sent_at < ? OR created_at IS NULL', 24.hours.ago]) 
    # Load by batch of 1000 
    users.find_each(:batch_size => 1000) do |u| 
    u.generate_and_deliver_dailysummery 
    end 
    sleep 60 
end 

Cron работы/Плановое задание/...

Вторая возможность заключается в том, чтобы запланировать задачу рекурсивно, например, каждый час или полчаса. Исправьте меня, если я ошибаюсь, но ваши пользователи действительно должны запланировать доставку в 10:39? Я думаю, что позволяют им выбрать час достаточно.

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

Существует хороший камень для управления задачей cron с синтаксисом ruby. Дополнительная информация здесь: Whenever

+0

Это критически важный сервис (уведомление о ставках торговли), поэтому он должен быть как можно быстрее. – Lichtamberg

+0

Thats the background Почему я не хочу использовать очередь – Lichtamberg

+0

И я использую Monit для управления этой граблиной задачей, которая работает очень хорошо. – Lichtamberg

2

Существует только два основных способа, с помощью которых можно выполнить отложенное выполнение. Вы запускаете скрипт, когда пользователь на вашем сайте попадает на страницу, что является неэффективным и не совсем точным. Или используйте какой-то фоновый процесс, будь то задание cron или resque/delayed job/etc.

В то время как ваш метод, имеющий пробег процесс повеса вечно будет работать нормально, это неэффективно, потому что вы итерация пользователей 24/7, как только он закончится, что-то вроде:

while true 
    User.where("last_sent_at <= ? OR last_sent_at = ?", 24.hours.ago, nil).each do |u| 
      u.generate_and_deliver_dailysummery 
    end 

    sleep 3600 
end 

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

0

Вы можете сделать это, вам также нужно будет проверить время, в которое вы хотите отправить. Так, начиная с псевдокодом и добавив к нему:

while true 
    User.all.each do |u| 
    if u.last_sent_at < Time.now - 24.hours && Time.now.hour >= u.send_at 
     u.generate_and_deliver_dailysummery 
     # the next 2 lines are only needed if "generate_and_deliver_dailysummery" doesn't sent last_sent_at already 
     u.last_sent_at = Time.now 
     u.save 
    end 
    end 

    sleep 900 
end 

Я также добавил sleep, так что вы не напрасно забиваете свою базу данных. Вы также можете взглянуть на ограничение этого цикла только тем набором пользователей, которого вам нужно отправить. Запрос, аналогичный тому, что предлагает Захари, будет намного более эффективным, чем то, что у вас есть.

2

Выполнение задачи периодически является задачей cron. Всякий раз, когда gem (https://github.com/javan/whenever) упрощает настройку определений cron для вашего приложения.

Поскольку ваше приложение масштабируется, вы можете обнаружить, что задача рейка занимает слишком много времени для запуска и что очередь полезна в верхней части графика cron.Вы можете использовать cron для управления, когда поставки запланированы, но фактически они выполняются рабочим пулом.

+0

Как я уже говорил, я не хочу использовать очереди, такие как resq или redis или что-то в этом роде! Бесконечная задача rake запрашивает веб-сервис для времени дыры (ставки обмена), поэтому очередь на основе задания не подходит. – Lichtamberg

+0

Concur, делая доставку почты асинхронной, стоит только установить resque/redis. Мы используем resque_mailer/resque_scheduler для доставки писем через определенные разработчиком интервалы. Это очень удобно. Я знаю, что вы не хотите его использовать, но поскольку мы видели прирост производительности и увеличенный процент доставки, я чувствую, что это стоит упомянуть. Ура! – Tass

+1

@ Lichtamberg с использованием cron (через каждый драгоценный камень) * не * очередь и не требует redis и т. Д. Это утилита unix для запуска задач с определенными интервалами. Ответ на бетаматт - лучшее решение, на мой взгляд. – dwhalen

0

Если вы не хотите использовать очередь - считают отсроченной работу (вроде бедной очереди мужика) - это работать в качестве задачи граблей аналогично тому, что вы делаете

он хранит все задачи в таблице заданий, как правило, при добавлении задачи она очередей ему работать как можно быстрее, однако вы можете изменить это, чтобы задержать его до определенного времени

вы можете превратить ваш DailySummary класс DailySummaryJob и после завершения он может повторно очередь нового экземпляра себе на ближайшие дни запустить

+0

Как я уже говорил, я не хочу использовать очереди! Бесконечная задача rake запрашивает веб-сервис для времени дыры (ставки обмена), поэтому очередь на основе задания не подходит. – Lichtamberg

+1

, если вы не хотите использовать очереди, несмотря на все советы, предоставленные вам разными сторонами, было бы полезно объяснить, почему. В противном случае вы создаете впечатление упрямства без причины. –

+0

Это критически важный сервис (уведомление о ставках торговли), поэтому оно должно быть как можно быстрее. Вот почему я не хочу использовать систему на основе очереди или работы. – Lichtamberg

0

Как обновить атрибут last_sent_at?

если вы используете

last_sent_at += 24.hours 

и инициализируется last_sent_at = Time.now.at_beginning_of_day + send_at

будет все ок.

не использовать last_sent_at = Time.now. это потому, что может быть некоторая задержка, когда работа действительно выполнена, это сделает атрибут last_sent_at все более «отложенным».

Смежные вопросы