Как бороться с таймаутами SMTP

Я отправляю массовые электронные письма на корпоративный сервер обмена, используя клиентское приложение, написанное на С#.

Может случиться, и это произошло, что тайм-аут клиентского приложения (а не сервера). Поскольку невозможно узнать, выполнил ли сервер запрос, как обрабатывать повторные попытки в этом случае?

Нет никаких идентификаторов, которые можно было бы использовать, чтобы избежать дублирования. Установка длительных тайм-аутов или даже бесконечных тайм-аутов не является хорошей политикой.

Я использую экспоненциальный алгоритм отсрочки для повторной попытки. В этом случае он должен отправить только один дубликат, потому что в следующий раз он будет ждать дольше.

Я думаю, что нет решения для пули. В любом случае, поскольку это первый проект такого рода, мне нужно проверить, есть ли у кого-то решение, которое я пропустил.

Обновление: биржа выполняет ретрансляцию. Я использую SmtpClient для отправки электронной почты. Проблема в том, что сервер может отправить сообщение 250 Ok, но получатель никогда его не получит, а затем повторит попытку. Это единственная проблема, которую я пытаюсь решить в этом посте.

В службах Rest рекомендуется использовать ошибку параллелизма. Если клиент публикует что-то и получает статус "409 - конфликт", это означает, что сообщение уже было сохранено на сервере. Но для этого должен быть ключ к сообщению, которое создается клиентом и является частью сообщения. У SMTP, похоже, нет механизма, который мог бы это предотвратить.


person MiguelSlv    schedule 13.09.2017    source источник
comment
Вы нашли разумное решение?   -  person Michael Freidgeim    schedule 05.08.2019
comment
Нет, я не могу гарантировать отсутствие дубликатов. В любом случае, похоже, что до сих пор это работало нормально, используя стратегию экспоненциального отката. Я не знаю никаких дубликатов. Установка значения тайм-аута выше сделает его менее похожим. Вот и конец пути.   -  person MiguelSlv    schedule 05.08.2019


Ответы (2)


Обычно SMTP-сервер предоставит вам информацию о том, принял он электронную почту или нет при использовании стандартного SMTP. Вот пример через телнет:

введите здесь описание изображения введите здесь описание изображения

Таким образом, вашему приложению нужно только отслеживать ответ здесь, и, если произойдет тайм-аут, вам не нужно продолжать работу со всеми электронными письмами, а нужно выбрать, где он остановился. Кстати, ваше приложение должно проверять каждый раз, когда приходит ответ, так как может быть, что отправитель или получатель не приняты...

Это упоминается в RFC 5321 (вы можете прокручивать этот документ):

Когда получатель-SMTP принимает часть почты (путем отправки сообщения «250 OK» в ответ на DATA), он берет на себя ответственность за доставку или ретрансляцию сообщения. Он должен серьезно отнестись к этой ответственности. Он НЕ ДОЛЖЕН терять сообщение по легкомысленным причинам, например, из-за того, что хост позже выходит из строя или из-за предсказуемой нехватки ресурсов. [...] Когда конец текста успешно получен и сохранен, SMTP-получатель отправляет ответ «250 OK».

Когда вы отправляете электронные письма, я думаю, что раздел 4.5.4.1. может быть важно, если вы не используете какую-то структуру, которая обрабатывает исходящие электронные письма в соответствии с RFC.

Пример. Ваш клиент создает электронное письмо, но во время отправки из тела (последняя часть передачи электронной почты) соединение разорвалось. Затем серверу не разрешено обрабатывать электронную почту. Технически он мог получить всю информацию, но RFC не позволил ему отправить ее, так как у клиента был тайм-аут, и он не завершил весь процесс.

Обновление: лучше всего использовать ссылку метод C# SmtpClient, а затем проверьте smtpStatusCode OK (= "Электронное письмо было успешно отправлено в службу SMTP"), что также можно увидеть в примерах telnet. Здесь метод больше ничего не делает (технически). Этот метод также соответствует RFC 5321, поэтому вам не нужно заново изобретать колесо отправки smtp. Если вы не получили OK, возможно, что-то произошло за это время, и вам нужно проверить результат, а затем выполнить повторную отправку (или вам нужно отказаться, если в сообщении об ошибке говорится, что адрес электронной почты недействителен или что-то подобное, чтобы избежать бесконечного цикла).

person BastianW    schedule 13.09.2017
comment
Я не понимаю. Если тайм-аут клиента, это означает, что клиент отказывается от ожидания ответа сервера, поэтому он не может знать, каков был результат запроса, потому что он не будет слушать. Например: если связь была прервана, сервер продолжит работу до конца, но клиент не получит ответа. Другой пример: сервер был слишком занят, поэтому он обработал запрос через 31 секунду, но клиент сдался через 30 секунд. - person MiguelSlv; 13.09.2017
comment
Это довольно просто: в те дни каждый SMTP-сервер должен следовать RFC 5321 обратите внимание, что вы можете прокручивать этот URL-адрес Ваше приложение должно соответствовать RFC 5321, иначе вы будете отправлять несколько электронных писем. Это означает, что если обработка была прервана посередине, сервер не сможет продолжить работу с электронной почтой, она просто не будет работать и будет противоречить RFC 5321. Это означает, что если ваше приложение получит 250 OK, как указано в RFC, тогда электронная почта принимается удаленным сервером. Если нет, его необходимо отправить повторно. - person BastianW; 13.09.2017
comment
Я не уверен, что мы на одной странице. Сервер обмена Microsoft настроен на ретрансляцию сообщений для меня, поэтому RFC 5321 должен быть обеспечен этим сервером. Нужно ли каким-либо образом соблюдать RFC 5321? Я отправляю одно сообщение за раз, проверяя результат перед продвижением. В случае ошибки я повторяю попытку 5 раз, и сначала это была моя проблема. Если у меня есть тайм-ауты, как действовать дальше. - person MiguelSlv; 14.09.2017
comment
Вы действуете как SMTP-клиент и, следовательно, должны соответствовать RFC 5321, а также являетесь отправителем. Почему вы не использовали SmtpClient метод из C#, который может обрабатывать тайм-ауты и исключения? Затем вы сможете обнаружить проблему и повторить попытку. Затем вы можете проверить 250 OK в SMTPStatusCode здесь, о котором я упоминал ранее. - person BastianW; 14.09.2017
comment
я использую SmtpClient и обрабатываю исключения. Суть в том, что сервер может отправить сообщение OK, но клиент его не получил по какой-то другой причине. В службах Rest рекомендуемый подход заключается в использовании ошибки параллелизма. Если клиент публикует что-то и получает 409 - статус конфликта, это означает, что сообщение уже было сохранено на сервере. Но для этого должен быть ключ к сообщению, который создается клиентом и является частью сообщения. Итак, я до сих пор не понимаю, как правильно обрабатывать тайм-аут. - person MiguelSlv; 14.09.2017
comment
в основном это похоже на то, как если бы два почтовых сервера разговаривали друг с другом, а в середине возникла проблема с сетью. Затем необходимо продолжить повторную отправку. К сожалению, SMTP довольно стар и не может сравниться с остальными сервисами. Однако, если вы предпочитаете отдых, почему бы не использовать Exchange Webservices EWS для отправки электронных писем? - person BastianW; 14.09.2017
comment
Это может быть вариант. Я сказал в начале, это случилось. Если вы опубликуете еще один ответ с этим предложением, я приму его. Я тоже уточню свой вопрос. Большое спасибо. - person MiguelSlv; 14.09.2017
comment
Давайте продолжим обсуждение в чате. - person BastianW; 14.09.2017

Другим возможным вариантом может быть использование Веб-службы Exchange. Вы также можете использовать их для отправки электронных писем. Пример из здесь:

// Create an email message and identify the Exchange service.
EmailMessage message = new EmailMessage(service);

// Add properties to the email message.
message.Subject = "Interesting";
message.Body = "The merger is finalized.";
message.ToRecipients.Add("[email protected]");

// Send the email message and save a copy.
message.SendAndSaveCopy();

Они также дают вам ответ:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="14" 
               MinorVersion="0" 
               MajorBuildNumber="639" 
               MinorBuildNumber="20" 
               Version="Exchange2010" 
               xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" 
               xmlns="http://schemas.microsoft.com/exchange/services/2006/types" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xm=""lns:xsd="http://www.w3.org/2001/XMLSchema" />
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:CreateItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" 
               xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:CreateItemResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:Items />
        </m:CreateItemResponseMessage>
      </m:ResponseMessages>
    </m:CreateItemResponse>
  </s:Body>
</s:Envelope>
person BastianW    schedule 14.09.2017