Question

We have a "smallish" NServiceBus application(s) that use a dozen EF mapped tables and RabbitMQ as communication medium. With NServiceBus.Host.Exe startup of the application takes ~26s (debug and release versions both, with or without debugger attached).

After adding EndpointConfigurationType application setting the load time dropped by 2s.

So I've been researching this and about 8-10s is spent with EF in the first query and it's various generation routines. EF loading performance could also be enhanced by NGen:ing the libraries.

However after NGen:ing up the libraries and starting NServiceBus.Host.exe, Native images are loaded by default appdomain, but also by an additional appdomain (which uses IL dlls), so it looks like it uses LoadFrom to load up the dependencies.

Is there a way around this? We'd like to use NSB.Host.exe for it's windows service features (that we're not interested in reimplementing). Also the other "IWantTo..." features are nice since we have already several (16?) endpoints using those.

edit: http://blogs.msdn.com/b/abhinaba/archive/2014/02/18/net-ngen-explicit-loads-and-load-context-promotion.aspx All my dlls are in same directory as NServiceBus.Host.exe, so based on that, Fusion should've loaded the native dlls as well.

edit2: This is "minimal" repro that doesn't have all the b&w of nservicebus.host.exe, but it seems to work in debug situation. It starts in under 2s versus 26s of Nservicebus.host.exe

using NServiceBus;
using BABAR.Configuration;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace BABAR.NGENHelper
{
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Starting: {0}", DateTime.Now.ToLongTimeString());
        StartNSB();
        Console.WriteLine("NSB started: {0}", DateTime.Now.ToLongTimeString());
    }

    private static void StartNSB()
    {
        // this ends up loading EF, in this case unnoticeable
        NServiceBus.Unicast.Transport.TransportConnectionString.Override(
            GetRabbitMQConnectionString);

        NServiceBus.SetLoggingLibrary.Log4Net(() => log4net.Config.XmlConfigurator.Configure());

        var semibus = Configure.With(GetNSBAssemblies())
                .DefaultBuilder()
                .DefineEndpointName("NGENHelper")
                .UseTransport<NServiceBus.RabbitMQ>()
                .PurgeOnStartup(false)
                .UnicastBus()
                .ImpersonateSender(false)
                .RunHandlersUnderIncomingPrincipal(false)
                .CustomConfigurationSource(new DefaultNServiceBusConfigurationSource())
                .MessageForwardingInCaseOfFault()
                .DisableTimeoutManager();

        var bus = semibus.CreateBus()
            .Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>()
                            .Install());
    }
    public static string GetRabbitMQConnectionString()
    {
        var nc = new Access().GetNode<NodeConfiguration>();
        return nc.RabbitConnectionString;
    }
    internal static IEnumerable<Assembly> GetNSBAssemblies()
    {
        return new[] { 
            typeof(NServiceBus.RabbitMQ).Assembly, // IConfigureTransport for NSB
            typeof(BABAR.Bootstrapper).Assembly, 
        };
    }
}
}
Was it helpful?

Solution

I think just go with self hosting https://github.com/SimonCropp/NServiceBus.SelfHost#self-host

see sample code for a self host below

The "service features" of the NSB host can be substituted by calls to sc.exe https://github.com/SimonCropp/NServiceBus.SelfHost#install--uninstall

class ProgramService : ServiceBase
{
    IStartableBus bus;

    static void Main()
    {
        using (var service = new ProgramService())
        {
            // so we can run interactive from Visual Studio or as a service
            if (Environment.UserInteractive)
            {
                service.OnStart(null);
                Console.WriteLine("\r\nPress any key to stop program\r\n");
                Console.Read();
                service.OnStop();
            }
            else
            {
                Run(service);
            }
        }
    }

    protected override void OnStart(string[] args)
    {
        Configure.GetEndpointNameAction = () => "SelfHostSample";
        bus = Configure.With()
            .DefaultBuilder()
            .UnicastBus()
            .CreateBus();
        bus.Start(Startup);
    }

    static void Startup()
    {
        //Only create queues when a user is debugging
        if (Environment.UserInteractive && Debugger.IsAttached)
        {
            Configure.Instance.ForInstallationOn<Windows>().Install();
        }
    }

    protected override void OnStop()
    {
        if (bus != null)
        {
            bus.Shutdown();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top