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,
- Procese los mensajes (es decir, envíe los correos electrónicos).
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:
if(
now() > 2pm && now() < 7pm
) {
process(10);
} else {
process(100);
}
function process(count) {
for(i=0; i<=count; i++) {
message = dequeue();
mail(message)
}
}
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:
CREATE TABLE message_queue (
id int(11) NOT NULL AUTO_INCREMENT,
timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
processed enum('0','1') NOT NULL DEFAULT '0',
message varchar(255) NOT NULL,
PRIMARY KEY (id),
KEY timestamp (timestamp),
KEY processed (processed)
)
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:
- Matrices PHP para pruebas unitarias,
- Amazon SQS en producción,
- MySQL en los entornos de desarrollo y prueba.
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é). El reason
es una constante y, por supuesto, tengo una gran tabla que asigna un lugar reason
a explicaciones más completas (Me las arreglé para enviar cerca de 500 correos electrónicos a los clientes con el críptico reason
en lugar del mensaje más completo una vez).
Otras lecturas
Normas:
Herramientas:
Lecturas interesantes: