He escrito un componente en un servicio de Windows (C#) que es responsable de enviar a veces grandes volúmenes de correos electrónicos. Estos correos electrónicos se destinarán a destinatarios en muchos dominios – realmente, cualquier dominio. (Sí, los destinatarios quieren el correo electrónico. No, no estoy enviando spam. Sí, estoy en una queja con CAN-SPAM. Sí, soy consciente de sending email from code sucks.) Muchos de los correos electrónicos son transaccionales (generados en respuesta a las acciones del usuario); algunos son a granel (fusiones de correo básicamente).¿La mejor manera de probar el código de envío de correo electrónico SMTP de gran volumen?
No quiero depender de un servidor SMTP externo. (Entre otras consideraciones, la idea de tener que revisar un buzón de mensajes de rebote y tratar de analizarlos me da malos sentimientos.)
Mi diseño es bastante simple. Tanto los mensajes transaccionales como los masivos se generan e insertan en una tabla DB. Esta tabla contiene el sobre y el contenido del correo electrónico, además de un conteo de intentos y una fecha de reintento posterior.
El servicio ejecuta algunos subprocesos de trabajo que toman 20 filas a la vez y recorren cada una de ellas. Utilizando la biblioteca Simple DNS Plus, tomo el (los) registro (s) MX del dominio del destinatario y luego uso System.Net.Mail.SmtpClient
para enviar el correo electrónico sincrónicamente. Si la llamada al Send()
tiene éxito, puedo dequeue el correo electrónico. Si falla temporalmente, puedo incrementar el recuento de intentos y establecer una fecha de reintento adecuada. Si falla de manera permanente, puedo quitar la cola y manejar el error.
Obviamente, enviar miles de correos electrónicos de prueba a cientos de dominios reales diferentes es una muy mala idea. Sin embargo, definitivamente necesito poner a prueba mi código de envío multiproceso. Tampoco estoy seguro de cuál es la mejor manera de simular los distintos modos de falla de SMTP. Además, quiero asegurarme de que supere los diversos métodos de control de spam (clasificación por líneas para nombrar el nivel más relevante de la capa de red).
Incluso mis dificultades de prueba a pequeña escala se ven agravadas por mi reciente descubrimiento de mi ISP bloqueando las conexiones al puerto 25 en cualquier servidor que no sea el servidor SMTP de mi ISP. (En producción, esto será, por supuesto, en un servidor adecuado donde el puerto 25 no está bloqueado. Eso no me ayuda a probarlo desde mi máquina de desarrollo.)
Entonces, las dos cosas que más me interesan son:
- ¿Cómo debo hacer una prueba de mi código?
- ¿Cuáles son las diversas formas en que
SmtpClient.Send()
puede fallar? Seis excepciones se enumeran;SmtpException
ySmtpFailedRecipientsException
parecen ser los más relevantes.
Actualización:Marc B's answer señala que básicamente estoy creando mi propio servidor SMTP. Él hace que el punto válido que estoy reinventar la rueda, así que aquí está mi razón para no usar uno 'real' (Postfix, etc.) en lugar:
Los correos electrónicos tienen diferentes prioridades de envío (aunque esto no está relacionado con el sobre
X-Priority
). El correo masivo es de baja prioridad; transaccional es alto. (Y cualquier correo electrónico o grupo de correos electrónicos puede configurarse además para tener una prioridad arbitraria). Necesito poder suspender el envío de correos electrónicos de menor prioridad para que se puedan enviar primero los correos electrónicos de mayor prioridad. (Para lograr esto, los hilos de trabajo simplemente recogen los elementos de mayor prioridad de la cola cada vez que obtienen otros 20.)Si ya he enviado varios miles de elementos masivos a un servidor SMTP externo, no tengo forma de ponerlos en espera mientras se envían los artículos que deseo enviar. Un resumen Google search muestra que Postfix realmente no admite prioridades; Sendmail prioriza la información en el sobre, que no satisface mis necesidades.
Necesito poder mostrar el progreso del proceso de envío de una ráfaga (grupo de correos electrónicos masivos) a mis usuarios. Si simplemente entregué todos mis correos electrónicos a un servidor externo, no tengo idea de qué tan avanzado está en la entrega real.
Tengo dudas en analizar mensajes de rebote porque el mensaje de rebote de cada MTA es diferente. Sendmail es diferente de Exchange, es diferente de
[...]
. Además, ¿a qué frecuencia consulto mi bandeja de entrada de rebote? ¿Qué pasa si un mensaje de rebote no se entrega?No estoy demasiado preocupado con una explosión que falla a la mitad.
Si hablamos de falla catastrófica (aplicación que termina excepción no controlada, falla de energía, lo que sea): como los subprocesos de trabajo dequeue cada correo electrónico de la base de datos cuando se entrega correctamente, puedo saber quién recibió la explosión y quién no tiene Además, cuando el servicio se restablece después de una falla, simplemente continúa donde lo dejó en la cola.
Si hablamos de error local (a
SmtpException
, error de DNS, etc.): Solo registro la falla, incremente el contador de intentos del correo electrónico y vuelva a intentarlo más tarde. (Que es básicamente lo que pide la especificación SMTP). Después de n intentos, puedo fallar permanentemente el mensaje (dequeue) y registrar el error para examinarlo más tarde. De esta forma, puedo encontrar casos de bordes extraños que mi código no maneja – incluso si mi código no es 100% perfecto la primera vez. (Y seamos honestos, no lo será.)Espero que la ruta de transferencia propia me permita, en última instancia, recibir correos electrónicos más rápido que si tuviera que depender de un servidor SMTP externo. Tendría que preocuparme por la limitación de la velocidad si el servidor no estuviera bajo mi control; incluso si lo fuera, sigue siendo un cuello de botella. La arquitectura multiproceso con la que me he alojado significa que me estoy conectando a varios servidores remotos en paralelo, disminuyendo la cantidad total de tiempo que lleva entregar n mensajes.
Me hice cargo de esta comprensión que realmente estaba creando esencialmente mi propio servidor SMTP. Elegí la ruta Roll-my-own por las razones que agregué a mi pregunta. En el segundo párrafo de su respuesta: ¿podría ampliar las formas de realmente * hacer * esas varias cosas? – josh3736
Greylisting es fácil. Hay complementos para los diversos servidores SMTP grandes para hacerlo. Los rebotes duros son simplemente errores de tipo "no hay tal usuario en esta dirección". Los problemas de ICMP se pueden simular en el firewall, es particularmente fácil con iptables en Linux. –