【问题标题】:Exception when sending thousands of mails through Mandrill API通过 Mandrill API 发送数千封邮件时出现异常
【发布时间】:2015-10-06 06:35:45
【问题描述】:

应用程序通过 Entity Framework 使用表 MailingLista 进行查询,其中包含需要通过 Mail 发送的 300,000 条记录

每条记录都必须通过 API Mandrill 发送。 Mandrill 是类似于 Amazon SES 的邮件服务器

Mandrill 的响应应该会更新 MailingLista 表中的状态。此更新是通过 EF 中的存储过程完成的

我将 PLINQ 都尝试为 Parallel 并且在这两种情况下都有相同的异常

代码并行:

      public void ProcessMailingParallel()
      {
         Entities.Configuration.AutoDetectChangesEnabled = false;
         Entities.Configuration.ProxyCreationEnabled = false;
         Entities.Database.CommandTimeout = 10000;
         MandrillApi _MandrillApi = new MandrillApi(AppConfig.MailAPIKey);
         try
         {
            _Logger.Debug("Start ProcessMailing()");
            Parallel.ForEach(GetvMailingListaReadyToSend(10000).AsParallel(),
               new ParallelOptions { MaxDegreeOfParallelism = 100 },
               _MailingLista => ProcessMail(_MailingLista));
         }
         catch (Exception _Exception)
         {
            _Logger.ErrorFormat("Excepcion: {0}", _Exception);
         }
         _Logger.Debug("End ProcessMailing()");
      }


  public void ProcessMail(vMailingListaReadyToSend vMailingLista)
  {
     EmailResult _EmailResult = null;
     try
     {
        //Create Mail
        EmailMessage _EmailMessage = new EmailMessage()
        {
           to = new EmailAddress(vMailingLista.MailTo),
           from_email = vMailingLista.MailFrom ?? AppConfig.MailFrom,
           from_name = vMailingLista.MailFromName ?? AppConfig.MailFromName,
           subject = vMailingLista.Subject,
           html = vMailingLista.Body
        };

        //Mandrill Delay is about 3-5 seconds
        _EmailResult = _MandrillApi.SendMessage(_EmailMessage);

        if (_EmailResult.IsNull())
           throw new Exception("result");

        // Update using Stored 
        UpdateMailingLista(vMailingLista.MailingListaId, DateTime.Now, 
            true, _EmailResult.Status, _EmailResult.RejectReason);
     }
     catch (Exception _Exception)
     {
        _Logger.ErrorFormat("Exception:{0}",vMailingLista.MailingListaId);
     }
  }

500 次更新后收到以下异常 ExecuteReader 需要一个打开且可用的连接。连接的当前状态是打开的

异常时连接打开

【问题讨论】:

  • 异常是否恰好在 10 秒后出现? Entities.Database.CommandTimeout = 10000。如果是这样,要么增加超时时间,要么在开始发送电子邮件之前获得结果;在轮询结果的同一循环中发送电子邮件使您的光标始终保持打开状态。
  • 我更改了 CommandTimeout 并没有任何改变,我找到的解决方案是使用 (MailingEntities MailingEntities context = new ()) { context.SPUpdateMailingLista () ...... }
  • 错误是由 ExecuteReader 引起的,你在哪里从数据库中读取内容?我没有看到那个代码。

标签: c# entity-framework task-parallel-library mandrill .net-4.5.2


【解决方案1】:

最终代码

Main.cs

   protected override void OnStart(string[] args)
    {
             _LogHelper.Init();
             _LogHelper.AddFileLogging...
             _LogHelper.AddSMTPLogging...
             _Logger.DebugFormat("Start....
             _Timer = new Timer(AppConfig.Interval);
             _Timer.AutoReset =   true;
             _Timer.Elapsed += new ElapsedEventHandler(this._Timer_Tick);
             _Timer.Start();
    }

   private void _Timer_Tick(object sender, ElapsedEventArgs e)
    {
       _Timer.Stop();
       try
       {
          using (MailingService _MailingServices = new MailingService(1000000))
          {
             _MailingServices.ProcessMailingPLinQ(); //Parallel
             //_MailingServices.ProcessMailingParallel(); //PLINQ
          }
       }
       catch (Exception _Exception)
       {
          _Logger.ErrorFormat("Exception: {0}", _Exception.GetExceptionAll());
       }
       finally
       {
          _Timer.Start();
       }
    }



Services.cs

      public void ProcessMailingPLinQ()
      {
         Entities.Configuration.AutoDetectChangesEnabled = false;
         Entities.Configuration.ProxyCreationEnabled = false;
         MandrillApi _MandrillApi = new MandrillApi(AppConfig.MailAPIKey);
         try
         {
            _Logger.Debug("Start ProcessMailing()");
            GetvMailingListaReadyToSend(5000)
                          .AsParallel()
                          .Select(M => ProcessMail(M)).ToArray();
            //Query paginated
         }
         catch (Exception _Exception)
         {
            _Logger.ErrorFormat(....);
         }
         _Logger.Debug("End ProcessMailing()");
      }

      public void ProcessMailingParallel()
      {
         Entities.Configuration.AutoDetectChangesEnabled = false;
         Entities.Configuration.ProxyCreationEnabled = false;
         MandrillApi _MandrillApi = new MandrillApi(AppConfig.MailAPIKey);
         try
         {
            _Logger.Debug("Start ProcessMailing()");
            Parallel.ForEach(GetvMailingListaReadyToSend(5000).AsParallel(),
               new ParallelOptions { MaxDegreeOfParallelism = 100 },
               _MailingLista => ProcessMail(_MailingLista));
            //Query paginated
         }
         catch (Exception _Exception)
         {
            _Logger.ErrorFormat(....);
         }
         _Logger.Debug("End ProcessMailing()");
      }

public EmailResult ProcessMail(vMailingListaReadyToSend vMailingLista)
{
   EmailResult _EmailResult = null;
   try
   {
      //Create 
      EmailMessage _EmailMessage = new EmailMessage()
      {
         .....
      };

      //Sending
      _EmailResult = _MandrillApi.SendMessage(_EmailMessage).FirstOrDefault();

      //Update
      using (MailingEntities _MailingEntitiesUpdate = new MailingEntities())
      {
         _MailingEntitiesUpdate.SPUpdateMailingLista(Id, DateTime.Now, false);
      }
   }
   catch (Exception _Exception)
   {
      _Logger.ErrorFormat(...);
   }
   return _EmailResult;
}


EntityFramework.cs
IEnumerable<vMailingListaReadyToSend> GetvMailingListaReadyToSend(int vItems)
{
         return Entities.vMailingListaReadyToSend.AsNoTracking().Take(vItems);
}

欢迎所有改进!

【讨论】:

    猜你喜欢
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 2016-07-04
    • 1970-01-01
    • 2018-10-17
    • 1970-01-01
    • 2019-04-23
    相关资源
    最近更新 更多