2009-04-25 15 views
16

Estoy tratando de implementar un sistema de listas de correo para mi aplicación. Actualmente estoy usando Zend_Mail_Transport_Smtp('localhost') como mi transporte, recorriendo mi lista de suscriptores y enviando un nuevo Zend_Mail a cada uno. Sin embargo, me doy cuenta de que el tiempo que lleva completar el script aumenta a medida que aumenta el número de suscriptores.¿Cuál es el mejor enfoque para enviar correos electrónicos a cientos de destinatarios desde una aplicación de Zend Framework?

Estoy seguro de que debe haber un enfoque más profesional para hacer esto, que implica la puesta en cola de correos electrónicos. Supongo que el enfoque ideal sería que el usuario complete el formulario, haga clic en enviar e inmediatamente obtenga una respuesta que diga que se están enviando los correos electrónicos, en lugar de esperar que los cientos de correos electrónicos terminen de enviarse.

Entiendo que Zend_Mail no hace ningún tipo de cola de correo. ¿Podría alguien que tenga experiencia con esto, darme una visión general de cómo se puede hacer esto? No sé nada de cron/crontab/cronjobs, así que si eso implica, explica el proceso.

Respuesta

19

Para enviar de manera confiable una gran cantidad de correos electrónicos usando PHP, debe utilizar un mecanismo de cola. Según lo sugerido por otros, el proceso de usar una cola es como la siguiente:

  • bucle sobre el conjunto de los usuarios, creando un correo electrónico para cada uno y, posiblemente, la personalización de los contenidos
  • paso de cada correo objeto a la cola lo que retrasará el envío del correo electrónico hasta más tarde
  • En algún tipo de script cron, envíe el contenido de la cola unos cientos a la vez. Nota: deseará ajustar la cantidad de correos electrónicos que está enviando al observar los registros en busca de errores provenientes del proceso de envío real. Si intenta enviar demasiados, me he dado cuenta de que llegue a un punto en el transporte de correo ya no aceptará conexiones (estoy usando qmail)

Hay algunas bibliotecas por ahí que puede utilizar para haz esto, PEAR Mail Queue (con Mail_Mime) y SwiftMailer te permiten crear y poner en cola correos electrónicos. Hasta ahora, Zend Mail solo proporciona la creación de correos electrónicos, sin cola (más sobre esto más adelante).

Tengo experiencia principalmente con PEAR Mail Queue y hay algunos inconvenientes. Si está tratando de poner en cola una gran cantidad de correos electrónicos (por ejemplo, hacer un bucle de más de 20,000 usuarios e intentar colocarlos en la cola en un tiempo razonable), la implementación de la codificación imprimible entre comillas de Mail Mime es muy lenta. Puede acelerar esto cambiando a codificación base64.

En cuanto a Zend Mail, puede escribir un objeto Zend Mail Transport que coloca sus objetos de Zend Mail en PEAR Mail Queue. Lo he hecho con cierto éxito, pero se necesita un poco de juego para hacerlo bien.Para ello, extienda Zend Mail Transport Abstract, implemente el método _sendMail (que es donde colocará su objeto Zend Mail en Mail Queue) y pase la instancia de su objeto de transporte al método send() de su objeto Zend Mail o por Zend Mail :: setDefaultTransport().

En pocas palabras, hay muchas maneras de hacerlo, pero llevará algo de investigación y aprendizaje en su nombre. Sin embargo, es un problema muy solucionable.

0

La clase Zend Mail se ve bien y es simple de usar, también le permite enviar un texto sin formato y una versión HTML del correo electrónico, que en el marketing por correo electrónico es muy importante.

Si está familiarizado con el marco, me quedaré con él.

cosas importantes a tener en cuenta al enviar correos electrónicos a los grandes volúmenes de personas son:

  • Puede su servidor web frente a las solicitudes de imágenes cuando se abren los correos electrónicos + la carga en el servidor de personas que visitan su sitio.

Si la respuesta es no o no está seguro, el uso de la referencia de apache debería ser capaz de ayudarlo a resolver si es posible. Si todavía no está seguro, siempre es mejor enviar por lotes correos electrónicos (que pueden sincronizarse con crontab) para repartir la carga.

Espero que esto ayude.

+0

Esta es una respuesta confusa ya que si no el uso de cron, escritura tiempos de espera al envío de correos electrónicos será un problema antes que el tráfico es motivo de preocupación. – rick

+0

Creo que Phil sugiere que use cron para estrangular el envío de los correos electrónicos. Por ejemplo, solo envíe 100 a la vez, cada 30 minutos, hasta que se agote la lista. – grossvogel

+0

¿Pero parece que está sugiriendo que crontab debería usarse como una solución para el alto tráfico? De todos modos, todos deberíamos ser tan afortunados como para generar demasiado tráfico con una campaña de marketing. Es probable que eso no sea un problema. – rick

3

De la documentación PHP.net.

Nota: Vale la pena señalar que la función mail() no es adecuada para grandes volúmenes de correo electrónico en un bucle. Esta función abre y cierra un socket SMTP para cada correo electrónico, que no es muy eficiente.
Para el envío de grandes cantidades de correo electrónico, consulte los paquetes »PEAR::MailPEAR::Mail_Queue.

La clase de correo de Zend es probablemente bastante buena (la mayoría de las cosas de Zend son buenas) Pero si quieres otras opciones. Aquí están.

+0

PEAR :: El correo es lento en mi experiencia. PHPMailer y swiftmailer son excelentes. – rick

2

Debería estar bien usando PHP en los miles de destinatarios, aunque evite el correo() como otros han notado. He visto algunos sistemas diseñados para grandes cantidades de correo (más de 100.000 destinatarios) comenzar a pasar por alto las funciones de correo estándar e intentar trabajar más directamente con el MTA. Incluso entonces no ha sido claro para mí que se requiera.

Haciendo profesional de correo electrónico es más sobre cómo hacer que el formato es bueno (HTML y texto plano siempre que sea posible), la gente puede darse de baja fácilmente, se manejan rebotes correctamente, el servidor de correo tiene todos los registros DNS correctas están en su lugar, y el la configuración del servidor no infringe las reglas de ningún sistema importante de listas negras. El idioma en el que escribe la aplicación no es un factor importante en unos cientos o incluso miles de mensajes.

18

NOTA: cuando leí por primera vez su pregunta, pensé que decía cientos de miles de correos electrónicos a la vez. Cuando lo comprobé dos veces, noté que en realidad decía cientos o miles. Soy demasiado perezoso para cambiar mi publicación ahora, así que he aquí algunas advertencias: según mi experiencia, probablemente pueda funcionar bien sin una herramienta comercial de aproximadamente 40K. Con aproximadamente 10K, querrá seguir la lista "mínima" para evitar dolores mayores cuando comience a alcanzar tamaños de lista más grandes. Sin embargo, recomiendo implementarlo todo de inmediato.

Lo he dicho antes, hay dos lados en el envío de correo electrónico:

  1. el aspecto técnico - básicamente todo del RFC de todo el protocolo SMTP , formatos de correo electrónico, DNS registros, etc. Esto es ligeramente complicado pero solucionable.
  2. El lado mágico - entrega de correo electrónico gestión es vudú. Obtendrá frustrado, las cosas se romperán sin razón aparente y usted considerará salir para otro trabajo que no involucre el correo electrónico.

Le recomiendo que no escriba su propio remitente masivo. Estoy seguro de que PHP puede hacer un buen trabajo, pero probablemente deberías pasar tu tiempo en otro lado. Los dos productos que he usado en el pasado y recomiendo son Strongmail y PowerMTA. Ten en cuenta que tienen un alto precio, pero casi puedo garantizarte que gastarás más en construir tu propia solución a largo plazo.

Una de las áreas que se encargará de escribir la suya en PHP es la de estrangulamiento/alquitrán. Los servidores de correo comenzarán a agregarse en reposo (30) después de que haya enviado algunos mensajes para desacelerarlo y evitar que envíe spam.

Normalmente, estos remitentes masivos comerciales ejecutan el protocolo SMTP para hacer cola. Continuarías usando Zend_Mail, pero codifícalo para conectarte a tu servidor. Pondrá en cola el correo casi tan rápido como lo pueda enviar, y luego usará su propio motor para enviar el correo a sus destinos.

En una lista de 100K, tendrá que emplear las mejores prácticas de correo electrónico. Como mínimo, se necesita:

  • registros SPF, DKIM, posiblemente, así
  • varias direcciones IP para segmentar el tráfico más - tienen 3 IP, una dirección de la calidad de su confianza, uno para las direcciones IP de riesgo medio y uno para direcciones IP de alto riesgo. Este diseño ayuda a minimizar el riesgo de recibir correo a sus mejores clientes.
  • DNS inversa adecuada para el envío de direcciones IP
  • Use los bucles de retroalimentación de AOL, Hotmail, Yahoo y otros para el procesamiento de las quejas de spam
  • Darse de baja y rebote de gestión - asegúrese de que está poda de estas direcciones
  • Tener abrir/hacer clic en el seguimiento también es importante: si sus clientes en la lista A no abren sus correos electrónicos, debe degradarlos a la lista B, y así sucesivamente. Esto es importante porque los ISP convertirán las cuentas inactivas en un honeypot. Hotmail es famoso por esto.

Por último, si realmente quiere enviar un correo electrónico, querrá algunas otras herramientas como Return Path.

+0

Ya sabes tus cosas. Desafortunadamente, tu respuesta es abrumadora. ¿Cómo utilizaría Strongmail o PowerMTA con Zend Mail y cubriría todos los puntos que mencionó? – rick

+0

Puede vincular su aplicación con Strongmail, cuando digo aplicación me refiero al almacenamiento de datos, esto significa que no necesita implementar ninguna línea de código, simplemente comparte sus datos de usuario con el servidor de Strongmail y envía los millones de correos electrónicos:) – vaske

2

Implementé un envío masivo de correo en PHP donde cada correo electrónico se personalizó para un individuo. No fue difícil y no llevó demasiado tiempo. Usé swiftmailer y cron. Zend Mail podría estar bien también. Empecé con la cola de correo PEAR, pero poner en cola los correos electrónicos fue demasiado lento.

El proceso de puesta en cola los correos electrónicos fueron así:

  1. crear la plantilla de correo electrónico y añadir marcadores de posición (o usar un motor de plantillas) para las áreas donde será sustituido contenido único.
  2. En un bucle, sustituya los marcadores de posición con cualquier contenido único, inserte el contenido del correo electrónico resultante, asunto, direcciones, id del lote y, opcionalmente, un valor de prioridad en una tabla de base de datos.

Utilicé un trabajo de cron para enviar lotes de correos electrónicos. El intervalo de tiempo de cron y el número de correos electrónicos enviados por lote fueron importantes ya que estaba en un host compartido con límites. La secuencia de comandos que fue llamada por el trabajo cron solo fue accesible por cron. El script lee x cantidad de correos electrónicos de la tabla ordenados por id. De lote y, opcionalmente, prioridad. Si un correo electrónico se envió con éxito, se eliminó de la cola de la base de datos. Si no se pudo enviar un correo electrónico, permaneció en la cola y se incrementó un contador para ese registro. Si un contador superaba un número establecido, el correo electrónico se eliminó de la cola.

+0

Un par de consejos útiles para acelerar las colas con PEAR_Mail es hacer que * _seq sea una tabla de MEMORIA en Mysql. También lo envolví en una transacción por cada 100 entradas –

0

He desarrollado un sistema de gestión de boletines con Swiftmailer y es muy fácil de implementar. Es compatible con SMTP, cifrado, archivos adjuntos, envío por lotes, ...

3

Utilice Zend_Queue para colocar los correos electrónicos en la cola para un procesamiento de fondo asilrono. Necesitará un trabajo cron para procesar la cola en segundo plano.

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); 

} 

Entonces para el trabajo de cron, un guión como

<?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 
Cuestiones relacionadas