Я написал компонент в службе Windows (C #), который отвечает за отправку иногда больших объемов электронных писем.Эти электронные письма будут отправляться получателям на многих доменах - на самом деле, на любой домен. (Да, получатели хотят получить электронное письмо. Нет, я не рассылаю спам. Да, я жалуюсь на CAN-SPAM. Да, я в курсе отправка электронной почты с кодом отстой .) Многие электронные письма являются транзакционными (генерируются в ответ на действия пользователя);некоторые из них массовые (слияние почты в основном).
Я не хочу полагаться на внешний SMTP-сервер.(Среди прочих соображений мысль о необходимости проверять почтовый ящик на предмет ненадежных сообщений и пытаться разобрать их вызывает у меня плохие чувства.)
Мой дизайн довольно прост.Как транзакционные, так и массовые сообщения генерируются и вставляются в таблицу БД.Эта таблица содержит почтовый конверт и содержимое, а также количество попыток и дату повторной попытки.
Служба запускает несколько рабочих потоков, которые одновременно получают по 20 строк и перебирают каждый из них.Используя библиотеку Simple DNS Plus , я беру MX-записи домена получателя и затем использую System.Net.Mail.SmtpClient
для синхронной отправки электронной почты.Если вызов на Send()
завершится успешно, я могу удалить электронную почту из очереди.Если это временно не удается, я могу увеличить счетчик попыток и установить соответствующую дату повторной попытки.Если это не удастся, я могу удалить из очереди и обработать ошибку.
Очевидно, что отправка тысяч тестовых писем на сотни различных реальных доменов - очень плохая идея.Однако мне определенно нужно провести стресс-тестирование моего многопоточного кода отправки.Я также не совсем уверен, что лучший способ - симулировать различные режимы сбоя SMTP.Кроме того, я хочу удостовериться, что обошёл все возможные методы борьбы со спамом (грейлисты для обозначения наиболее значимых для сетевого уровня вещей).
Даже мои небольшие трудности с тестированием усугубляются моим недавним открытиеммой провайдер блокирует соединения с портом 25 на любом сервере, кроме SMTP-сервера моего провайдера.(В производственном процессе эта вещь, конечно, будет на правильном сервере, где порт 25 не заблокирован. Это не помогает мне тестировать с моей машины разработки.)
Итак, две вещи, которые мне наиболее интересныabout:
- Как мне протестировать мой код?
- Какими различными способами может не работать
SmtpClient.Send()
?Шесть исключений перечислены;SmtpException
и SmtpFailedRecipientsException
кажутся наиболее актуальными.
Обновление: Ответ Марка Б указывает, что я в основномсоздать свой собственный SMTP-сервер.Он делает правильное замечание, что я заново изобретаю колесо, поэтому вот мое обоснование того, что вместо этого вместо «фактического» (Postfix и т. Д.) Следует использовать следующее:
Письма имеют разные приоритеты отправки (хотя это не связано с конвертом X-Priority
).Массовая электронная почта имеет низкий приоритет;Транзакционный высокий.(И любая электронная почта или группа электронных писем могут быть дополнительно настроены на произвольный приоритет.) Мне нужно иметь возможность приостановить отправку писем с более низким приоритетом, чтобы письма с более высоким приоритетом могли доставляться первыми.(Для этого рабочие потоки просто выбирают элементы с наивысшим приоритетом из очереди каждый раз, когда получают еще 20).
Если я уже отправил несколько тысяч массовых элементов на внешний SMTP-сервер, у меня естьнет никакой возможности отложить их, пока отправляемые мной элементы отправляются.Беглый поиск Google показывает, что Postfix не поддерживает приоритеты; Sendmail расставляет приоритеты для информации в конверте, которая не соответствует моим потребностям.
Мне нужно иметь возможность отображать ход процесса отправки взрыва (группа массовых писем) для моих пользователей.Если я просто передал все свои электронные письма на внешний сервер, я понятия не имею, как далеко продвигается фактическая доставка.
Я не решаюсь анализировать отказов сообщения, потому что отказов каждого MTA разные.Sendmail отличается от Exchange отличается от [...]
.Кроме того, на какой частоте я проверяю свой почтовый ящик отказов?Что делать, если само сообщение о пересылке не доставлено?
Меня не слишком беспокоит то, что на полпути провал взрыва.
Если мы говоримкатастрофический сбой (необработанное исключение, завершающее работу приложения, сбой питания и т. д.): поскольку рабочие потоки удаляют каждое сообщение из базы данных при успешной доставке, я могу знать, кто получил взрыв, а кто нет.Кроме того, когда служба сбрасывается после сбоя, она просто определяет, где она остановилась в очереди.
Если мы говорим о локальном сбое (SmtpException
, сбое DNS и т. Д.): Iпросто зарегистрируйте ошибку, увеличьте счетчик попыток электронной почты и повторите попытку позже.(Это в основном то, что требует спецификация SMTP.) После n попыток я могу окончательно пропустить сообщение (удалить его из очереди) и зарегистрировать сбой для проверки позже.Таким образом, я могу найти странные крайние случаи, которые мой код не обрабатывает - даже если мой код не на 100% совершенен в первый раз.(И давайте будем честными, этого не произойдет.)
Я надеюсь, что маршрут «катайся сам» в конечном итоге позволит мне получать электронные письма быстрее, чем если бы мне пришлосьполагаться на внешний SMTP-сервер.Мне бы пришлось беспокоиться об ограничении скорости, если бы сервер не был под моим контролем;даже если бы это было, это все еще узкое место.Многопоточная архитектура, с которой я столкнулся, означает, что я подключаюсь к нескольким удаленным серверам параллельно, уменьшая общее время, необходимое для доставки n сообщений.