2009-04-25 7 views
16

Sto cercando di implementare un sistema di mailing list per la mia applicazione. Attualmente sto usando il Zend_Mail_Transport_Smtp('localhost') come trasporto, collegando il mio elenco di abbonati e inviando un nuovo Zend_Mail a ciascuno di essi. Tuttavia, sto notando che il tempo necessario per completare lo script aumenta con l'aumento del numero di abbonati.Qual è l'approccio migliore per l'invio di e-mail a centinaia di destinatari da un'applicazione Zend Framework?

Sono sicuro che ci deve essere un approccio più professionale per farlo, coinvolgendo l'accodamento delle e-mail. Suppongo che l'approccio ideale sarebbe per l'utente di compilare il modulo, fare clic su Invia, e ottenere immediatamente una risposta dicendo che le e-mail vengono inviate, piuttosto che attendere che le centinaia di e-mail finiscano di inviare.

Capisco che Zend_Mail non faccia alcun tipo di accodamento della posta. Qualcuno che ha esperienza con questo, può darmi una panoramica di come questo può essere fatto? Non so nulla di cron/crontab/cronjobs, quindi se ciò comporta, per favore spiega il processo.

risposta

19

Per inviare in modo affidabile un numero elevato di e-mail tramite PHP, è necessario utilizzare un meccanismo di accodamento. Come suggerito da altri, il processo di utilizzo di una coda simile a questa:

  • loop sopra la serie di utenti, la creazione di una e-mail per ciascuno di essi ed eventualmente personalizzare il contenuto
  • Passo ogni oggetto la posta nella coda che ritarderà l'invio dell'e-mail fino a più tardi
  • In una sorta di script cron, inviare il contenuto della coda poche centinaia alla volta. Nota: è necessario modificare il numero di e-mail che si stanno inviando osservando i registri per gli errori provenienti dall'effettivo processo di invio. Se si tenta di inviare troppi, ho notato che raggiunge un punto in cui il trasporto di posta non accetterà più connessioni (sto usando qmail)

Ci sono alcune librerie là fuori è possibile utilizzare per fai questo, PEAR Mail Queue (con Mail_Mime) e SwiftMailer ti permettono entrambi di creare e accodare le email. Finora, Zend Mail prevede solo la creazione di e-mail, non l'accodamento (ne parleremo più avanti).

Ho esperienza principalmente con PEAR Mail Queue e ci sono alcuni trucchi. Se si sta tentando di accodare un numero elevato di e-mail (ad esempio, il ciclo su oltre 20.000 utenti e il tentativo di farli entrare in coda in un tempo ragionevole), l'implementazione della codifica stampabile quotata di Mail Mime è molto lenta. Puoi accelerarlo passando alla codifica base64.

Come per Zend Mail, è possibile scrivere un oggetto Zend Mail Transport che inserisce gli oggetti Zend Mail nella coda PEAR Mail. Ho fatto questo con un certo successo, ma ci vuole un po 'di gioco per farlo bene.Per fare ciò, estendi Zend Mail Transport Abstract, implementa il metodo _sendMail (che è dove farai cadere l'oggetto Zend Mail nella coda di posta) e passa l'istanza dell'oggetto di trasporto al metodo send() dell'oggetto Zend Mail o da Zend Mail :: setDefaultTransport().

La conclusione è che ci sono molti modi in cui puoi farlo, ma ci vorrà qualche ricerca e apprendimento a tuo nome. È un problema molto risolvibile, tuttavia.

0

La classe di posta di Zend sembra buona e semplice da usare, ma consente anche di inviare un testo semplice e una versione HTML dell'email, che nell'e-mail marketing è molto importante.

Se la tua familiarità con il telaio funziona, ci rimango.

cose importanti da considerare quando si inviano messaggi di posta elettronica a grandi volumi di persone sono:

  • può il vostro webserver far fronte alle richieste di immagine quando le email vengono aperte + il carico sul server di persone che visitano il tuo sito.

Se la risposta è no o non sei sicuro, l'utilizzo del benchmark Apache dovrebbe essere in grado di aiutarti a capire se possibile. Se sei ancora insicuro, è sempre meglio inviare in batch le e-mail (che possono essere cronometrate con crontab) per distribuire il carico.

Spero che questo aiuti.

+0

Questa è una risposta confusa dal momento che se non si utilizza cron, timeout di script quando l'invio di email sarà un problema prima che il traffico è di preoccupazione. – rick

+0

Penso che Phil suggerisca di usare cron per limitare l'invio delle e-mail. Ad esempio, invia solo 100 alla volta, ogni 30 minuti, finché l'elenco non è esaurito. – grossvogel

+0

Ma sembra che suggerisca che crontab debba essere usato come soluzione per il traffico intenso? Ad ogni modo, dovremmo essere tutti così fortunati da generare troppo traffico con una campagna di marketing. Le probabilità sono, quello non è un problema. – rick

3

Dalla documentazione di PHP.net.

Nota: vale la pena notare che la funzione mail() non è adatta per grandi volumi di e-mail in un ciclo. Questa funzione apre e chiude un socket SMTP per ogni e-mail, che non è molto efficiente.
Per l'invio di grandi quantità di e-mail, consultare i pacchetti »PEAR::MailPEAR::Mail_Queue.

La classe Zend Mail è probabilmente piuttosto buona (la maggior parte delle cose di Zend è buona) Ma se volete altre opzioni. Eccoli.

+0

PERA :: La posta è lenta nella mia esperienza. PHPMailer e swiftmailer sono eccellenti. – rick

2

Si dovrebbe andare bene usando PHP su migliaia di destinatari, sebbene si eviti la posta() come altri hanno notato. Ho visto alcuni sistemi progettati per grandi quantità di posta (oltre 100.000 destinatari) a scavalcare le funzioni standard di mailing e cercare di lavorare più direttamente con il MTA. Anche allora non mi è stato chiaro che fosse necessario.

Fare posta elettronica professionale è più circa assicurandosi che la formattazione è buono (HTML e testo normale quando possibile), le persone possono cancellarsi facilmente, rimbalzi sono gestiti correttamente, il server di posta ha tutti i record DNS giusti sono a posto, e la la configurazione del server non viola le regole di alcun sistema di blacklist principale. La lingua in cui scrivi l'applicazione non è un fattore importante a poche centinaia o addirittura poche migliaia di messaggi.

18

NOTA: quando ho letto la tua domanda per la prima volta, ho pensato che dicesse centinaia di migliaia di e-mail contemporaneamente. Quando ho controllato due volte, ho notato che in realtà diceva centinaia di migliaia. Sono troppo pigro per cambiare il mio post ora, quindi ecco alcuni avvertimenti: dalla mia esperienza, probabilmente si può andare bene senza uno strumento commerciale a circa 40K. A circa 10K vorrai seguire l'elenco "minimo" per prevenire gravi dolori quando inizi a raggiungere dimensioni di listino più grandi. Raccomando comunque di implementarlo subito.

ho detto prima, ci sono due lati per l'invio di e-mail:

  1. la parte tecnica - praticamente tutti del RFC del tutto il protocollo SMTP , formati e-mail, DNS record, ecc. Questo è leggermente complicato ma risolvibile.
  2. Il lato magico - la gestione della consegna dell'email è voodoo. Otterrai frustrato, le cose si romperanno per il motivo apparente e ti verrà addebitato il per un altro lavoro che non implichi la posta elettronica.

Si consiglia di non scrivere il proprio mittente. Sono sicuro che PHP può fare un buon lavoro, ma probabilmente dovresti passare il tuo tempo altrove. I due prodotti che ho usato in passato e raccomandano sono Strongmail e PowerMTA. State attenti: hanno un prezzo elevato, ma posso quasi garantire che spenderete più a costruire la vostra soluzione a lungo termine.

Un'area che ti verrà inchiodata con la scrittura in PHP è la limitazione/tar pitting. I server di posta inizieranno ad aggiungere in sleep (30) dopo che hai inviato alcuni messaggi per rallentare e impedire lo spamming.

In genere, questi mittenti commerciali mittenti eseguono il protocollo SMTP per l'accodamento. Continueresti a usare Zend_Mail, ma dovrai codificarlo per connettersi al tuo server. Metterà in coda la posta il più velocemente possibile, quindi usa il suo motore per inviare la posta alle loro destinazioni.

In un elenco di 100 KB, è necessario utilizzare le best practice di posta elettronica. Come minimo, è necessario:

  • record SPF, possibilmente DKIM così
  • IP multipli per segmentare il traffico oltre - hanno 3 IP, uno per l'indirizzo di qualità di fiducia, uno per gli indirizzi IP di medio rischio e uno per gli indirizzi IP ad alto rischio. Questo design aiuta a ridurre al minimo il rischio di inviare posta ai tuoi migliori clienti.
  • corretta reverse DNS per l'invio di indirizzi IP
  • Utilizzare i cicli di feedback da AOL, Hotmail, Yahoo e altri per l'elaborazione di segnalazioni di spam
  • Cancellati e rimbalzare gestione - assicurarsi che si sta potatura questi indirizzi
  • Having Anche il tracciamento di apertura/clic è importante - se i clienti della lista A non stanno aprendo le e-mail, è necessario degradarle nell'elenco B e così via. Questo è importante perché gli ISP trasformeranno gli account inattivi in ​​un honeypot. Hotmail è famoso per questo.

Infine, se sei seriamente intenzionato a inviare email, ti serviranno altri strumenti come Return Path.

+0

Conosci le tue cose. Sfortunatamente, la tua risposta è travolgente. Come si usa Strongmail o PowerMTA con Zend Mail e si coprono tutti i punti elenco elencati? – rick

+0

È possibile legare l'applicazione a Strongmail, quando dico l'applicazione che intendo per l'archiviazione dei dati, significa che non è necessario implementare alcuna riga di codice, basta condividere i dettagli dell'utente con il server Strongmail e inviare milioni di email: – vaske

2

Ho implementato un mailer in blocco in php in cui ogni messaggio di posta elettronica è stato personalizzato per un individuo. Non è stato difficile e non ha impiegato troppo tempo. Ho usato swiftmailer e cron. Zend Mail potrebbe anche essere ok. Ho iniziato con la coda di posta PEAR, ma l'accodamento delle e-mail era troppo lento.

Il processo di accodamento messaggi di posta elettronica è andato in questo modo:

  1. creare il modello e-mail e aggiungere segnaposto (o usare un motore di template) per le zone dove contenuti unici saranno sostituiti.
  2. In un ciclo, sostituire i segnaposto con qualsiasi contenuto univoco, inserire il contenuto e-mail, l'oggetto, gli indirizzi, l'ID batch e facoltativamente un valore di priorità in una tabella di database.

Ho utilizzato un cron job per inviare batch di email. L'intervallo cron di cron e il numero di email inviate per batch erano importanti poiché ero su un host condiviso con limiti. Lo script chiamato dal cron job era accessibile solo da cron. Lo script legge il numero x di e-mail dalla tabella ordinata per ID batch e, facoltativamente, priorità. Se un'email è stata inviata correttamente, è stata eliminata dalla coda del database. Se non è stato possibile inviare un'e-mail, questa è rimasta in coda e un contatore è stato incrementato per quel record. Se un contatore era su un numero impostato, l'e-mail è stata cancellata dalla coda.

+0

Un paio di suggerimenti utili per accelerare l'accodamento con PEAR_Mail è rendere * _seq una tabella MEMORY in Mysql. L'ho anche avvolto in una transazione per 100 voci –

0

Ho sviluppato un sistema di gestione delle newsletter con Swiftmailer ed è molto semplice da implementare. Supporta SMTP, crittografia, allegati, invio batch, ...

3

Utilizzare Zend_Queue per posizionare le e-mail nella coda per l'elaborazione in background asincrona. Avrai bisogno di un cron job per elaborare la coda in background.

protected function _enqueueEmail(WikiEmailArticle $email) 
{ 
    static $intialized = false; 

    if (!$initialized) { 

     $this->_initializeMailQueue("wikiappwork_queue"); 
     $initialized = true; 
    } 

    $this->_mailQueue->send(serialize($email)); 
} 
protected function _initializeMailQueue() 
{ 
    /* See: 1.) http://framework.zend.com/manual/en/zend.queue.adapters.html and 
    *  2.) Zend/Queue/Adapter/Db/mysql.sql. 
    */ 

$ini = Zend_Controller_Front::getInstance()->getParam('bootstrap') 
              ->getOptions(); 

    $queueAdapterOptions = array('driverOptions' => array(
    'host' => $ini['resources']['multidb']['zqueue']['host'], 
    'username' => $ini['resources']['multidb']['zqueue']['username'], 
    'password' => $ini['resources']['multidb']['zqueue']['password'], 
    'dbname' => $ini['resources']['multidb']['zqueue']['dbname'], 
    'type' => $ini['resources']['multidb']['zqueue']['adapter']), 
    'name' => $ini['resources']['multidb']['zqueue']['queueName']); 

    $this->_mailQueue = new Zend_Queue('Db', $queueAdapterOptions); 

} 

Poi per il processo di cron, uno script come

<?php 
use \Wiki\Email\WikiEmailArticle; 

// Change this define to correspond to the location of the wikiapp.work/libary 
define('APPLICATION_PATH', '/home/kurt/public_html/wikiapp.work/application'); 

set_include_path(implode(PATH_SEPARATOR, array(
    APPLICATION_PATH . '/../library', 
    get_include_path(), 
))); 

// autoloader (uses closure) for loading both WikiXXX classes and Zend_ classes. 
spl_autoload_register(function ($className) { 

    // Zend classes need underscore converted to PATH_SEPARATOR 
    if (strpos($className, 'Zend_') === 0) { 

     $className = str_replace('_', '/', $className); 
    } 

    $file = str_replace('\\', '/', $className . '.php'); 

    // search include path for the file. 
    $include_dirs = explode(PATH_SEPARATOR, get_include_path()); 

    foreach($include_dirs as $dir) { 

    $full_file = $dir . '/'. $file; 

    if (file_exists($full_file)) { 

     require_once $full_file; 
     return true; 
    } 
    } 

    return false; 
}); 

// Load and parese ini file, grabing sections we need. 
$ini = new Zend_Config_Ini(APPLICATION_PATH . 
          '/configs/application.ini', 'production'); 

$queue_config = $ini->resources->multidb->zqueue; 

$smtp_config = $ini->email->smtp; 

$queueAdapterOptions = array('driverOptions' => array(
             'host'  => $queue_config->host, 
        'username' => $queue_config->username, 
        'password' => $queue_config->password, 
        'dbname' => $queue_config->dbname, 
        'type'  => $queue_config->adapter), 
       'name' => $queue_config->queuename); 

$queue = new Zend_Queue('Db', $queueAdapterOptions); 


$smtp = new Zend_Mail_Transport_Smtp($smtp_config->server, array(
       'auth'  => $smtp_config->auth, 
     'username' => $smtp_config->username, 
     'password' => $smtp_config->password, 
     'port'  => $smtp_config->port, 
     'ssl'  => $smtp_config->ssl 
     )); 

Zend_Mail::setDefaultTransport($smtp); 

$messages = $queue->receive(10); 

foreach($messages as $message) { 

     // new WikiEmailArticle.  
    $email = unserialize($message->body); 

     try { 

      $email->send(); 

     } catch(Zend_Mail_Exception $e) { 

       // Log the error? 
       $msg = $e->getMessage(); 
       $str = $e->__toString(); 
       $trace = preg_replace('/(\d\d?\.)/', '\1\r', $str); 
     } // end try 

$queue->deleteMessage($message); 

} // end foreach