A volte mi sono trovato in difficoltà a gestire parecchi invii di email dalla mia applicazione in quanto era difficile recuperare il valore massimo di invii simultanei dal fornitore di hosting, c'è chi risponde infinito, chi risponde 1 al minuto e chi non risponde.
Per avere una certa flessibilità l'ideale sarebbe quello di avere una sorta di buffer nel quale vengono accodate le email da inviare ed ogni X secondi viene estratta una email ed inviata.
In questo modo non si fa arrabbiare il fornitore di hosting e si inviano email singole agli utenti in modo automatico.
Come costruire un buffer per accodare le email che dovranno essere inviate
Prima cosa generiamo un Mailer
rails g mailer Notify
ed aggiungiamo il metodo notification per inviare una email ad un generico utente
file: /app/mailers/notify.rb
class Notify < ActionMailer::Base default :from => "" def notification(to, from, subject, msg) @msg = msg mail(:from => from, :to => to, :subject => subject) end end
creiamo il relativo template
file: /app/views/notify/notification.html.erb
Title <%= @msg %>
ora generiamo un modello che rappresenta il buffer contenent le email
rails g model queued_email mailer:string mailer_method:string priority:integer args:text
aggiungiamo 2 metodi all'interno del modello
- send_email: estrae una email dal buffer e si occupa dell'invio usando uno dei metodi del Mailer
- add: aggiunge una mail alla coda
In questo caso viene estratta una sola email per volta, nulla vieta di alzare questo valore in funzione del vostro SMTP. Viene estratta la prima email inserita, immaginatela come una coda FIFO.
file: /app/models/queued_email.rb
class QueuedEmail < ActiveRecord::Base serialize :args def self.send_email find(:all, :order=> "priority desc, created_at asc", :limit=>1).each do |mail| mailer_class = mail.mailer.constantize mailer_method = ("deliver_" + mail.mailer_method).to_sym mailer_class.send(mailer_method, *mail.args) mail.destroy end true end def self.add(mailer, method, args, priority) QueuedEmail.create(:mailer=>mailer.to_s, :mailer_method=>method.to_s, :args => args, :priority=> priority) end end
La direttiva serialize :args ci permette di serializzare l'attributo args, vediamolo come un oggetto che contiene i valori per l'invio della email e che verrà deserializzato al momento della chiamata al metodo del Mailer.
Per inserire una email nel buffer possiamo procedere in questo modo:
args = [MAIL_ADMIN, MAIL_FROM, "oggetto","il tuo messaggio qui"] QueuedEmail.add("Notify","notification", args, 0)
creiamo un controller che verrà richiamato ogni X secondi e produrrà l'invio della email
rails g controller senders index
file: /app/controllers/senders_controller.rb
class SendersController < ApplicationController def index QueuedEmail.send_email end end
chiamando via browser questo url verrà estratta la prima email inserita nel buffer ed inviata
http://localhost:3000/senders
possiamo pianificare un job nel crontab per chiamare ciclicamente l'url sopra indicato!
*/5 * * * * wget http://localhost:3000/senders
Se il nostro fornitore di hosting non ci permette di editare il crontab che fare?
Vi mostrerò dei workaround nei prossimi post.