Estoy diseñando una aplicación web y me pregunto cómo diseñar la arquitectura para administrar el envío de correos electrónicos automatizados.
Actualmente tengo esta característica integrada en mi aplicación web y los correos electrónicos se envían en función de las aportaciones / interacciones del usuario (como la creación de un nuevo usuario). El problema es que conectarse directamente a un servidor de correo tarda un par de segundos. Escalando mi aplicación, este será un cuello de botella significativo en el futuro.
¿Cuál es la mejor manera de administrar el envío de una gran cantidad de correos electrónicos automatizados dentro de la arquitectura de mi sistema?
No habrá una gran cantidad de correos electrónicos enviados (2000 por día como máximo). Los correos electrónicos no necesitan enviarse de inmediato, hasta 10 minutos de retraso está bien.
Actualización: la cola de mensajes se ha dado como respuesta, pero ¿cómo se diseñaría? ¿Se manejará esto en la aplicación y se procesará durante un período de silencio, o necesito crear una nueva 'aplicación de correo' o servicio web para administrar la cola?
Respuestas:
El enfoque común, como ya mencionó Ozz , es una cola de mensajes . Desde una perspectiva de diseño, una cola de mensajes es esencialmente una cola FIFO , que es un tipo de datos bastante fundamental:
Lo que hace que una cola de mensajes sea especial es que, si bien su aplicación es responsable de la cola, un proceso diferente sería responsable de la cola. En la jerga de colas, su aplicación es el remitente de los mensajes, y el proceso de eliminación de colas es el receptor. La ventaja obvia es que todo el proceso es asíncrono, el receptor funciona independientemente del remitente, siempre que haya mensajes para procesar. La desventaja obvia es que necesita un componente adicional, el remitente, para que todo funcione.
Dado que su arquitectura ahora se basa en dos componentes que intercambian mensajes, puede usar el término elegante comunicación entre procesos .
¿Cómo afecta la introducción de una cola al diseño de su aplicación?
Ciertas acciones en su aplicación generan correos electrónicos. Introducir una cola de mensajes significaría que esas acciones ahora deberían enviar mensajes a la cola (y nada más). Esos mensajes deben contener la cantidad mínima absoluta de información necesaria para construir los correos electrónicos cuando su receptor los procese.
Formato y contenido de los mensajes.
El formato y el contenido de sus mensajes depende completamente de usted, pero debe tener en cuenta que cuanto más pequeños, mejor. Su cola debe ser tan rápida para escribir y procesar como sea posible, arrojar una gran cantidad de datos probablemente creará un cuello de botella.
Además, varios servicios de colas basados en la nube tienen restricciones en el tamaño de los mensajes y pueden dividir mensajes más grandes. No lo notará, los mensajes divididos se servirán como uno solo cuando los solicite, pero se le cobrarán varios mensajes (suponiendo, por supuesto, que esté utilizando un servicio que requiere una tarifa).
Diseño del receptor.
Como estamos hablando de una aplicación web, un enfoque común para su receptor sería un simple script cron. Se ejecutará cada
x
minuto (o segundos) y:n
Cantidad emergente de mensajes de la cola,Tenga en cuenta que estoy diciendo pop en lugar de get o fetch, eso se debe a que su receptor no solo obtiene los elementos de la cola, sino que también los borra (es decir, los elimina de la cola o los marca como procesados). Cómo sucederá exactamente eso depende de su implementación de la cola de mensajes y de las necesidades específicas de su aplicación.
Por supuesto, lo que estoy describiendo es esencialmente una operación por lotes , la forma más simple de procesar una cola. Dependiendo de sus necesidades, es posible que desee procesar los mensajes de una manera más complicada (eso también requeriría una cola más complicada).
Tráfico
Su receptor podría tener en cuenta el tráfico y ajustar la cantidad de mensajes que procesa en función del tráfico en el momento en que se ejecuta. Un enfoque simplista sería predecir sus horas de alto tráfico en función de los datos de tráfico anteriores y suponiendo que utilizó un script cron que se ejecuta cada
x
minuto, podría hacer algo como esto:Un enfoque muy ingenuo y sucio, pero funciona. Si no lo hace, bueno, el otro enfoque sería encontrar el tráfico actual de su servidor en cada iteración y ajustar la cantidad de elementos del proceso en consecuencia. Sin embargo, no micro optimice si no es absolutamente necesario, ya que estaría perdiendo el tiempo.
Almacenamiento en cola
Si su aplicación ya usa una base de datos, entonces una sola tabla en ella sería la solución más simple:
Realmente no es más complicado que eso. Por supuesto, puede hacerlo tan complicado como lo necesite, puede, por ejemplo, agregar un campo de prioridad (lo que significaría que esto ya no es una cola FIFO, pero si realmente lo necesita, ¿a quién le importa?). También podría simplificarlo, omitiendo el campo procesado (pero luego tendría que eliminar las filas después de procesarlas).
Una tabla de base de datos sería ideal para 2000 mensajes por día, pero probablemente no escalaría bien para millones de mensajes por día. Hay un millón de factores a considerar, todo en su infraestructura juega un papel en la escalabilidad general de su aplicación.
En cualquier caso, suponiendo que ya haya identificado la cola basada en la base de datos como un cuello de botella, el siguiente paso sería buscar un servicio basado en la nube. Amazon SQS es el único servicio que utilicé e hizo lo que promete. Estoy seguro de que hay bastantes servicios similares por ahí.
Las colas basadas en memoria también son algo a considerar, especialmente para las colas de corta duración. memcached es excelente como almacenamiento de cola de mensajes.
Sea cual sea el almacenamiento en el que decida construir su cola, sea inteligente y abstraiga. Ni su remitente ni su receptor deben estar vinculados a un almacenamiento específico, de lo contrario, cambiar a un almacenamiento diferente en un momento posterior sería un PITA completo.
Enfoque de la vida real
He creado una cola de mensajes para correos electrónicos que es muy similar a lo que está haciendo. Estaba en un proyecto PHP y lo he construido alrededor de Zend Queue , un componente de Zend Framework que ofrece varios adaptadores para diferentes almacenamientos. Mis almacenamientos donde:
Mis mensajes eran tan simples como pueden ser, mi aplicación creó pequeños arreglos con la información esencial (
[user_id, reason]
). El almacén de mensajes era una versión serializada de esa matriz (primero era el formato de serialización interno de PHP, luego JSON, no recuerdo por qué cambié). Elreason
es una constante y, por supuesto, tengo una gran tabla que asigna un lugarreason
a explicaciones más completas (Me las arreglé para enviar cerca de 500 correos electrónicos a los clientes con el crípticoreason
en lugar del mensaje más completo una vez).Otras lecturas
Normas:
Herramientas:
Lecturas interesantes:
fuente
Necesita algún tipo de sistema de colas.
Una manera simple podría ser escribir en una tabla de base de datos y tener otras filas de proceso de aplicación externas en esta tabla, pero hay muchas otras tecnologías de colas que podría usar.
Podría tener una importancia en los correos electrónicos para que ciertos se activen casi de inmediato (por ejemplo, restablecimiento de contraseña), y los de menor importancia podrían agruparse para enviarse más tarde.
fuente
Además de la cola, lo segundo que debe considerar es enviar correos electrónicos a través de servicios especializados: MailChimp, por ejemplo (no estoy afiliado a este servicio). De lo contrario, muchos de los servicios de correo, como gmail, pronto enviarán sus cartas a una carpeta de correo no deseado.
fuente
He modelado el sistema de mi cola en diferentes 2 tablas como;
Hay 1-1 relación entre estas tablas.
Tabla de mensajes para almacenar el contenido del mensaje. El contenido real (Para, CC, CCO, Asunto, Cuerpo, etc.) se serializa en el campo Gráfico en formato XML. Otra información Desde, Hasta solo se usa para informar problemas sin deserializar el gráfico. La separación de esta tabla permite dividir el contenido de la tabla en un almacenamiento de disco diferente. Una vez que esté listo para enviar un mensaje, debe leer toda la información, por lo tanto, no hay nada de malo en serializar todo el contenido en una columna con índice de clave principal.
Tabla MessageState para almacenar el estado del contenido del mensaje con información adicional basada en la fecha. La separación de esta tabla permite un mecanismo de acceso rápido con índices adicionales en el almacenamiento rápido de E / S. Otras columnas ya se explican por sí mismas.
Podría usar un grupo de subprocesos separado que escanee estas tablas. Si la aplicación y el grupo viven en la misma máquina, puede usar una clase EventWaitHandle para indicarle al grupo desde la aplicación que hay algo insertado en estas tablas; de lo contrario, es mejor escanear periódicamente con un tiempo de espera.
fuente