2010-07-26 11 views
6

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:

  1. ¿Cómo debo hacer una prueba de mi código?
  2. ¿Cuáles son las diversas formas en que SmtpClient.Send() puede fallar? Seis excepciones se enumeran; SmtpException y SmtpFailedRecipientsException 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:

  1. 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.

  2. 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.

  3. 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?

  4. 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á.)

  5. 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.

Respuesta

2

Supongamos que tiene dos servidores disponibles. Uno será el emisor, el otro será el receptor. Puede configurar DNS (o incluso solo archivos de host) en ambos con una larga serie de dominios falsos. En lo que respecta a los dos servidores, esos dominios son perfectamente válidos ya que los servidores DNS locales tienen autoridad para ellos, pero son completamente inválidos en lo que respecta al resto de la red. Solo asegúrese de que la resolución compruebe el archivo de hosts antes de DNS.

Una vez hecho esto, puede hacer que el servidor de envío envíe el servidor de recepción al contenido de su corazón, al igual que el receptor hace varias cosas para probar las reacciones de su código. Lista gris, retrasos TCP, rebotes, ICMP inalcanzables, saltos ICMP excedidos, etc.

Por supuesto, dado que tiene que probar todas estas condiciones, básicamente está creando su propio servidor SMTP, entonces ¿por qué no usar un uno real para comenzar? Supongo que el esfuerzo requerido para hacer un análisis básico de los mensajes de rebote será mucho menor que tener que crear fragmentos de código para manejar todos los modos de falla que postfix/sendmail/exim/etc ... ya manejan perfectamente bien en su propio.

Y esto es especialmente cierto cuando considera que su código de envío debe ser perfecto desde el primer momento. Si una explosión de correo electrónico falla parcialmente y solo la mitad de la lista de destinatarios recibe el mensaje, estás en un agujero mucho más grande que si rebotasen unos pocos cientos o miles de mensajes. O peor aún, falla de múltiples maneras diferentes (algunos servidores son inalcanzables, algunos greylisting por exceso de tráfico, etc.). Mientras que los rebotes se acomodarán felizmente en la cola entrante hasta que los procese manualmente, o repare su analizador de rebote para manejarlos.

+0

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

+0

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. –

0

Después de searching around, terminé disparando Papercut en varias máquinas adicionales que tenía por ahí. Luego llené mi base de datos con las direcciones de prueba *@[test-machine-*.local].

Si bien esto funcionó bastante bien, probé con 25 hilos de envío y parecía que estaba abrumando las cuatro computadoras que ejecutaban Papercut. Varios cientos de intentos de envío experimentaron fallas de conexión TCP; esos mensajes fueron correctamente puestos en cola para ser enviados más tarde (y finalmente llegaron). Sin embargo, de los 25,000 correos electrónicos de prueba, alrededor de 500 simplemente desaparecieron al – al sumar los archivos * .eml en la carpeta de Papercut en cada máquina de prueba solo se obtuvieron ~ 24,500.

Ahora me pregunto si los correos electrónicos que faltan son debido a un problema en mi código, o si Papercut ha eliminado los mensajes que informó en SMTP como 250 OK.

Cuestiones relacionadas