0

我有一个控制台应用程序作为我的网络作业来处理我的应用程序内的通知。使用队列触发进程。应用程序使用实体框架 6 与 SQL Azure 数据库交互。被调用的 Process() 方法读取/写入数据库中的数据。

处理队列消息时出现几个错误。它们永远不会进入毒物队列,因为它们在 2-3 次后被成功重新处理。主要错误如下:

mscorlib.dll 中出现“System.StackOverflowException”类型的未处理异常

错误:System.OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。

默认批量大小为 16,因此消息是并行处理的。

我的猜测是用于并行处理消息的 Ninject 设置是错误的。因此,当它们同时处理时,会出现一些错误,最终处理成功。

我的问题是:这个设置看起来好吗?我应该使用 InThreadScope() 可能是因为我不知道并行处理也是多线程的。

这是我的应用程序的代码。

程序.cs


namespace NotificationsProcessor
{
    public class Program
    {
        private static StandardKernel _kernel;
        private static void Main(string[] args)
        {
            var module = new CustomModule();
            var kernel = new StandardKernel(module);
            _kernel = kernel;

            var config =
                new JobHostConfiguration(AzureStorageAccount.ConnectionString)
                {
                    NameResolver = new QueueNameResolver()
                };
            var host = new JobHost(config);
            //config.Queues.BatchSize = 1; //Process messages in parallel
            host.RunAndBlock();
        }

        public static void ProcessNotification([QueueTrigger("%notificationsQueueKey%")] string item)
        {
            var n = _kernel.Get<INotifications>();
            n.Process(item);
        }

        public static void ProcessPoison([QueueTrigger("%notificationsQueueKeyPoison%")] string item)
        {
            //Process poison message. 
        }
    }
}

这是 Ninject 的 CustomModule 的代码


namespace NotificationsProcessor.NinjectFiles
{
    public class CustomModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IDbContext>().To<DataContext>(); //EF datacontext
            Bind<INotifications>().To<NotificationsService>();
            Bind<IEmails>().To<EmailsService>();
            Bind<ISms>().ToSmsService>();
        }
    }
}

处理方法的代码。


    public void ProcessMessage(string message)
    {
        try
        {
            var notificationQueueMessage = JsonConvert.DeserializeObject<NotificationQueueMessage>(message);
            //Grab message and check if it has to be processed
            var notification = _context.Set().Find(notificationQueueMessage.NotificationId);

            if (notification != null)
            {
                if (notification.NotificationType == NotificationType.AppointmentReminder.ToString())
                {
                    notificationSuccess = SendAppointmentReminderEmail(notification); //Code that sends email using the SendGrid Api
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex + Environment.NewLine + message, LogSources.EmailsService);
            throw;
        }                   
    }


更新 - 添加了异常

Json 序列化程序抛出异常。这是堆栈跟踪:

错误:System.OutOfMemoryException:引发了“System.OutOfMemoryException”类型的异常。在 System.String.CtorCharCount(Char c, Int32 count) 在 Newtonsoft.Json.JsonWriter.AutoCompleteClose(JsonContainerType type) 在 Newtonsoft.Json.JsonWriter.WriteEndObject() 在 Newtonsoft.Json 的 Newtonsoft.Json.JsonTextWriter.WriteIndent()。 JsonWriter.WriteEnd(JsonContainerType type) 在 Newtonsoft.Json.JsonWriter.WriteEnd() 在 Newtonsoft.Json.JsonWriter.AutoCompleteAll() 在 Newtonsoft.Json.JsonTextWriter.Close() 在 Newtonsoft.Json.JsonWriter.System.IDisposable.Dispose( )在 Newtonsoft.Json.JsonConvert.SerializeObjectInternal(对象值,类型类型,JsonSerializer jsonSerializer)在 Newtonsoft.Json.JsonConvert.SerializeObject(对象值,类型类型,格式化格式,JsonSerializerSettings 设置)在 Core.Services.Communications.EmailsS​​ervice。
4

1 回答 1

1

由于您正在接收OutOfMemoryExceptions 和StackOverflowExceptions,我建议可能存在递归或深度嵌套的方法。如果您有异常的堆栈跟踪,那将非常有帮助,遗憾的是,StackOverflowExceptions 并非如此。但是,OutOfMemoryException有一个堆栈跟踪,因此您需要记录它并查看它的内容/将其添加到您的问题中。另外,就目前StackoverflowException而言,你也可以试试这个

范围界定

您不应该.InThreadScope()在这种情况下使用。为什么?通常使用线程池。线程被重用。这意味着,作用域对象的寿命比处理单个消息的时间长。当前您正在使用.InTransientScope()(如果您不指定其他任何内容,这是默认设置)。由于您IDbContext只在一个地方使用,没关系。如果您想在多个对象之间共享同一个实例,那么您必须使用范围或手动传递该实例。

现在,在您的情况下可能存在的问题是,您可能正在创建很多 new IDbContexts,但在使用它们后没有将它们处理掉。根据其他因素,这可能会导致垃圾收集器花费更长的时间来清理内存。请参阅我是否必须在 DbContext 上调用 dispose

但是我承认这不会解决您的问题。它可能只会加快您的应用程序。无论如何,您可以这样做:

你应该做的是:INotifications派生自IDisposable

public interface INotifications : IDisposable 
{
    (...)
}

internal class Notifications : INotifications
{
    private readonly IDbContext _context;

    public Notifications(IDbContext context)
    {
        _context = context;
    }

    (...)

    public void Dispose()
    {
        _context.Dispose();
    }
}

并更改您的调用代码以处理INotifications

    public static void ProcessNotification([QueueTrigger("%notificationsQueueKey%")] string item)
    {
        using(var n = _kernel.Get<INotifications>())
        {
           n.Process(item);
        }
    }
于 2014-10-21T05:22:43.333 回答