Отправив множество уникальных писем одновременно


У меня есть asp.net основной приложение, которое отправляет электронные письма со ссылкой. Когда они нажимают на ссылку, они могут начать принимать онлайн тест без регистрации. Ссылка содержит номер, который проверяется контролером, который затем возвращает их испытания.

Я войти в Мои приложения и создать проект. Вот я вхожу в список контактов электронной почты в текстовое поле через запятую. В админке я поставил эту строку в TextBox в список вот так:

string emailList;
List<string> list = emailList.Split(',').ToList();
//removes whitespace in list:
for (int i = 0; i < list.Count(); i++)
{
 list[i] = string.Join("", list[i].Split(default(string[]), 
                       StringSplitOptions.RemoveEmptyEntries));
}

Единственный способ я вижу, как отправить много уникальных писем будет перебрать этот список и затем использовать обычный метод отправка электронных писем отключена в mimekit:

string message = "Press this link to take the test";
int number = 0;

foreach(var email in emailList)
{
  //gets a unique number stored in a table for each person:
  number = GetNumber(email);      
  message = $"http://www.mywebsite.com/Test/{number}";
  await _emailSender.SendEmailAsync(email, "Subject", message);
}

Теперь emailSender использует обычный метод mimekit, чтобы отправить одно сообщение для каждого в списке.

Это занимает много времени для большой список. Есть ли другие способы сделать это, или я могу более эффективно писать это? Какие могут быть узкие места моего решения? Любая обратная связь будет большим, из меня плохой асинхронного кода безопасности . Я подозреваю, что я использую слишком много асинхронных когда я должна была вернуться только задача, но я не уверен, как это сделать. Мне тоже интересно, как я могу проверить это в более широком масштабе, как я не имею большой emaillist использовать.

Вот код mimekit для отправки электронной почты:

public async Task SendEmailAsync(string email, string subject, string message)
{
  var melding = new MimeMessage();
  melding.From.Add(new MailboxAddress("Frank", _options.Option1));
  melding.To.Add(new MailboxAddress(email));
  melding.Subject = subject;

  melding.Body = new TextPart("plain")
  {
     Text = message
  };
  Try
  {
     await SendMessage(melding);
  }
  catch(Exception ex)
  {
     //log error
     throw new ApplicationException($"something went wrong:'{ex.Message}'.");
  }
}
private async Task SendMessage(MimeMessage message)
{
   using (var client = new SmtpClient())
   {
      await client.ConnectAsync("smtp.WebSiteLive.net", 587, SecureSocketOptions.None);
      client.AuthenticationMechanisms.Remove("XOAUTH2"); // Must be removed for Gmail SMTP
      await client.AuthenticateAsync(_options.Option1, _options.Option2);
      await client.SendAsync(message);
      await client.DisconnectAsync(true);
    }            
}

любая помощь значительно apprechiated!!



1949
1
задан 12 апреля 2018 в 08:04 Источник Поделиться
Комментарии
3 ответа

Заполнения письма пачками и пакетная обработка пакетное вместо отправки электронной почты по одному, это обеспечивает лучшую масштабируемость, а также иметь больше контроля на использование числа потоков {отмены[если требуется]}.

   For example:
Number of emails to send: 103
BatchSize :5 (Make it configurable)
We have in total : 20 batches of size 5 and 1 batch of size 3

Шаги следующим образом

    a. Process first batch.
b. Wait till batch completes execution
c. Take next batch for process
d. Repeat the process till all the batches.

У меня есть два подхода для достижения этой цели, исходя из потребностей вы можете использовать любой из подходов


  1. Использовать async/await и task => создать отдельную задачу для отправки каждого сообщения в каждом пакете, который использует несколько потоков.

    Пакетный процесс 1 => 5 пользователей, создать 5 задачи и выполнять
    => Дождаться завершения всех задач
    Пакетная обработка 2 => 5 пользователей, создать 5 задачи и выполнять
    => Дождаться завершения всех задач
    ...


  2. Используйте только асинхронный/ждут => который будем использовать почти такой же поток для обработки.

Рассмотрим класс электронной почты, как показано ниже, и _batchSize

    public class EmailClass
{
public string Email { get; set; }
public string Message { get; set; }
public string Subject { get; set; }
}
private int _batchSize = 5;
List<EmailClass> _emailCollection = new List<EmailClass>();

Когда пользователь нажимает на кнопку Отправить

   private async void button_Click(object sender, RoutedEventArgs e)
{
for (int ii = 0; ii < 22; ii++)
{
_emailCollection.Add(new EmailClass()
{
Email = "Email_" + ii + "@gmail.com",
Message = "http://google.com",
Subject = "Subject_" + ii
});
}

await SendBulkMail();
}

Разделить целую кучу писем, на куски _batchSize, обрабатывать каждый кусок по одному.

    private async Task SendBulkMail()
{
int remainingBatches = _emailCollection.Count % _batchSize;
int numberOfBatches = (_emailCollection.Count - remainingBatches) / _batchSize;
Debug.WriteLine(string.Format("TotalCount:{0} NumberOfBatches:{1}", _emailCollection.Count, numberOfBatches));
for (int ii = 0; ii < numberOfBatches; ii++)
{
Debug.WriteLine(DateTime.Now.ToString() + " CurrentChunk : " + ii);
await SendBatchMail(0, ii, _batchSize);
}

if (remainingBatches != 0)
{
await SendBatchMail(_emailCollection.Count - remainingBatches, 0, _emailCollection.Count);
}
}

Решение 1: Создать задачу для каждого почтового при обработке каждого блока

    private async Task SendBatchMail(int initalIndex, int batchIndex, int size)
{
List<Task> chunkTasks = new List<Task>();
chunkTasks.Clear();
for (int jj = initalIndex; jj < size; jj++)
{
var index = _batchSize * batchIndex + jj;
chunkTasks.Add(Task.Factory.StartNew(() => {
SendEmailAsync(_emailCollection[index].Email, _emailCollection[index].Subject, _emailCollection[index].Message);
}));
}
await Task.Run(() =>
{
Task.WaitAll(chunkTasks.ToArray());
});
}

public void SendEmailAsync(string email, string subject, string message)
{
Debug.WriteLine("\t" + DateTime.Now.ToString() + " " + Thread.CurrentThread.ManagedThreadId + " Sending mail : " + email);
try
{
SendMessage(email + subject + message);
}
catch (Exception ex)
{
}
}

private void SendMessage(string message)
{
Debug.WriteLine("\t\t" + DateTime.Now.ToString() + " " +Thread.CurrentThread.ManagedThreadId + " Message sending : => " + message);
int sleepTime = new Random().Next(5) * 1000;
Thread.Sleep(sleepTime);
Debug.WriteLine("\t\t" + DateTime.Now.ToString() + " " +Thread.CurrentThread.ManagedThreadId + " Message sent : => " + message);
}

Решение 1 выходные данные : отдельные нити[может или не может] используется для отправки каждого сообщения в каждом пакете

    TotalCount:22 NumberOfBatches:4
4/12/2018 11:07:02 PM CurrentChunk : 0
4/12/2018 11:07:02 PM 13 Sending mail : Email_0@gmail.com
4/12/2018 11:07:02 PM 15 Sending mail : Email_1@gmail.com
4/12/2018 11:07:02 PM 15 Message sending : => Email_1@gmail.comSubject_1http://google.com
4/12/2018 11:07:02 PM 11 Sending mail : Email_2@gmail.com
4/12/2018 11:07:02 PM 11 Message sending : => Email_2@gmail.comSubject_2http://google.com
4/12/2018 11:07:02 PM 13 Message sending : => Email_0@gmail.comSubject_0http://google.com
4/12/2018 11:07:02 PM 14 Sending mail : Email_3@gmail.com
4/12/2018 11:07:02 PM 14 Message sending : => Email_3@gmail.comSubject_3http://google.com
4/12/2018 11:07:02 PM 14 Message sent : => Email_3@gmail.comSubject_3http://google.com
4/12/2018 11:07:02 PM 14 Sending mail : Email_4@gmail.com
4/12/2018 11:07:02 PM 14 Message sending : => Email_4@gmail.comSubject_4http://google.com
4/12/2018 11:07:02 PM 14 Message sent : => Email_4@gmail.comSubject_4http://google.com
4/12/2018 11:07:05 PM 15 Message sent : => Email_1@gmail.comSubject_1http://google.com
4/12/2018 11:07:05 PM 11 Message sent : => Email_2@gmail.comSubject_2http://google.com
4/12/2018 11:07:05 PM 13 Message sent : => Email_0@gmail.comSubject_0http://google.com
4/12/2018 11:07:05 PM CurrentChunk : 1
4/12/2018 11:07:05 PM 16 Sending mail : Email_5@gmail.com
4/12/2018 11:07:05 PM 14 Sending mail : Email_7@gmail.com
4/12/2018 11:07:05 PM 13 Sending mail : Email_6@gmail.com
4/12/2018 11:07:06 PM 15 Sending mail : Email_8@gmail.com
4/12/2018 11:07:06 PM 15 Message sending : => Email_8@gmail.comSubject_8http://google.com
4/12/2018 11:07:06 PM 11 Sending mail : Email_9@gmail.com
4/12/2018 11:07:06 PM 11 Message sending : => Email_9@gmail.comSubject_9http://google.com
4/12/2018 11:07:06 PM 14 Message sending : => Email_7@gmail.comSubject_7http://google.com
4/12/2018 11:07:06 PM 13 Message sending : => Email_6@gmail.comSubject_6http://google.com
4/12/2018 11:07:06 PM 15 Message sent : => Email_8@gmail.comSubject_8http://google.com
4/12/2018 11:07:06 PM 16 Message sending : => Email_5@gmail.comSubject_5http://google.com
4/12/2018 11:07:06 PM 16 Message sent : => Email_5@gmail.comSubject_5http://google.com
4/12/2018 11:07:10 PM 11 Message sent : => Email_9@gmail.comSubject_9http://google.com
4/12/2018 11:07:10 PM 14 Message sent : => Email_7@gmail.comSubject_7http://google.com
4/12/2018 11:07:10 PM 13 Message sent : => Email_6@gmail.comSubject_6http://google.com

Решение 2 : Использовать только ключевые слова async/await для

    private async Task SendBatchMail(int initalIndex, int batchIndex, int size)
{
List<Task> chunkTasks = new List<Task>();
chunkTasks.Clear();
for (int jj = initalIndex; jj < size; jj++)
{
var index = _batchSize * batchIndex + jj;
chunkTasks.Add(SendEmailAsync(_emailCollection[index].Email, _emailCollection[index].Subject, _emailCollection[index].Message));
}
await Task.Run(() =>
{
Task.WaitAll(chunkTasks.ToArray());
});
}

public async Task SendEmailAsync(string email, string subject, string message)
{
Debug.WriteLine("\t" + DateTime.Now.ToString() + " " + Thread.CurrentThread.ManagedThreadId + " Sending mail : " + email);
try
{
await SendMessage(email + subject + message);
}
catch (Exception ex)
{
}
}

private async Task SendMessage(string message)
{
Debug.WriteLine("\t\t" + DateTime.Now.ToString() + " " +Thread.CurrentThread.ManagedThreadId + " Message sending : => " + message);
await Task.Run(() =>
{
int sleepTime = new Random().Next(5) * 1000;
Thread.Sleep(sleepTime);
});
Debug.WriteLine("\t\t" + DateTime.Now.ToString() + " " +Thread.CurrentThread.ManagedThreadId + " Message sent : => " + message);
}

Решение 2 выходных (почти один поток используется для обработки всех пакетов)

    4/12/2018 11:01:05 PM CurrentChunk : 0
4/12/2018 11:01:05 PM 10 Sending mail : Email_0@gmail.com
4/12/2018 11:01:05 PM 10 Message sending : => Email_0@gmail.comSubject_0http://google.com
4/12/2018 11:01:05 PM 10 Sending mail : Email_1@gmail.com
4/12/2018 11:01:05 PM 10 Message sending : => Email_1@gmail.comSubject_1http://google.com
4/12/2018 11:01:05 PM 10 Sending mail : Email_2@gmail.com
4/12/2018 11:01:05 PM 10 Message sending : => Email_2@gmail.comSubject_2http://google.com
4/12/2018 11:01:05 PM 10 Sending mail : Email_3@gmail.com
4/12/2018 11:01:05 PM 10 Message sending : => Email_3@gmail.comSubject_3http://google.com
4/12/2018 11:01:05 PM 10 Sending mail : Email_4@gmail.com
4/12/2018 11:01:05 PM 10 Message sending : => Email_4@gmail.comSubject_4http://google.com
4/12/2018 11:01:05 PM 10 Message sent : => Email_2@gmail.comSubject_2http://google.com
4/12/2018 11:01:05 PM 10 Message sent : => Email_3@gmail.comSubject_3http://google.com
4/12/2018 11:01:05 PM 10 Message sent : => Email_4@gmail.comSubject_4http://google.com
4/12/2018 11:01:08 PM 10 Message sent : => Email_0@gmail.comSubject_0http://google.com
4/12/2018 11:01:08 PM 10 Message sent : => Email_1@gmail.comSubject_1http://google.com
4/12/2018 11:01:08 PM CurrentChunk : 1
4/12/2018 11:01:08 PM 10 Sending mail : Email_5@gmail.com
4/12/2018 11:01:08 PM 10 Message sending : => Email_5@gmail.comSubject_5http://google.com
4/12/2018 11:01:08 PM 10 Sending mail : Email_6@gmail.com
4/12/2018 11:01:08 PM 10 Message sending : => Email_6@gmail.comSubject_6http://google.com
4/12/2018 11:01:08 PM 10 Sending mail : Email_7@gmail.com
4/12/2018 11:01:08 PM 10 Message sending : => Email_7@gmail.comSubject_7http://google.com
4/12/2018 11:01:08 PM 10 Sending mail : Email_8@gmail.com
4/12/2018 11:01:08 PM 10 Message sending : => Email_8@gmail.comSubject_8http://google.com
4/12/2018 11:01:08 PM 10 Sending mail : Email_9@gmail.com
4/12/2018 11:01:08 PM 10 Message sending : => Email_9@gmail.comSubject_9http://google.com
4/12/2018 11:01:09 PM 10 Message sent : => Email_9@gmail.comSubject_9http://google.com
4/12/2018 11:01:10 PM 10 Message sent : => Email_7@gmail.comSubject_7http://google.com
4/12/2018 11:01:10 PM 10 Message sent : => Email_8@gmail.comSubject_8http://google.com
4/12/2018 11:01:11 PM 10 Message sent : => Email_5@gmail.comSubject_5http://google.com
4/12/2018 11:01:12 PM 10 Message sent : => Email_6@gmail.comSubject_6http://google.com

0
ответ дан 12 апреля 2018 в 06:04 Источник Поделиться


   using (var client = new SmtpClient())
{
await client.ConnectAsync("smtp.WebSiteLive.net", 587, SecureSocketOptions.None);
client.AuthenticationMechanisms.Remove("XOAUTH2"); // Must be removed for Gmail SMTP
await client.AuthenticateAsync(_options.Option1, _options.Option2);
...
await client.DisconnectAsync(true);
}

Посмотрите сколько работы вы делаете за сообщение. Если вы повторно использовать клиент, вы экономите установки и демонтажа время каждого сообщения после первого.

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

1
ответ дан 12 апреля 2018 в 11:04 Источник Поделиться


string emailList;
List<string> list = emailList.Split(',').ToList();
//removes whitespace in list:
for (int i = 0; i < list.Count(); i++)
{
list[i] = string.Join("", list[i].Split(default(string[]),
StringSplitOptions.RemoveEmptyEntries));
}

Это очень творческий способ удаления пробелов. Но можно просто использовать Trim:

emailList.Split(',').Select(x => x.Trim()).ToList();



string emailList;
List<string> list = emailList.Split(',').ToList();

Этот термин, однако, не так изобретателен больше. Имя emailString как emailList но использовать list вместо emails для получения списка писем. Ровно наоборот.

1
ответ дан 12 апреля 2018 в 12:04 Источник Поделиться