根据内容重新启动多个队列

时间:2017-04-13 06:46:14

标签: asp.net-mvc simple-injector azure-servicebus-queues rebus

设置:使用SimpleInjector在asp.net mvc项目中重新开始。

我需要创建两个处理程序来接收来自特定队列的消息。按照我在SO answer上发现的内容,我创建了类似的代码。

在类库中,我有一个实现SimpleInjector IPackage的类,其代码如下:

public void RegisterServices( Container container ) {
    container.Register<IHandleMessages<MyMessage>, MyMessageHandler>( Lifestyle.Scoped );
    IContainerAdapter adapter = new SimpleInjectorContainerAdapter( container );
    Configure.With( adapter )
             .Transport( t => t.UseAzureServiceBus( connectionString, "A_QUEUE_NAME", AzureServiceBusMode.Standard ) )
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "A_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();

    Configure.With(adapter
             .Transport(t => t.UseAzureServiceBus(connectionString, "B_QUEUE_NAME")
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "B_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();
}

然而,当调试器进入第二个Configure.With(...)调用时,我终止时出现错误说:

  

IBus类型已经注册。如果您打算解析IBus实现的集合,请使用RegisterCollection重载。更多信息:https://simpleinjector.org/coll1。如果您打算使用此新注册替换现有注册,则可以通过将Container.Options.AllowOverridingRegistrations设置为true来允许覆盖当前注册。更多信息:https://simpleinjector.org/ovrrd

堆栈追踪:

[InvalidOperationException: Type IBus has already been registered. If your intention is to resolve a collection of IBus implementations, use the RegisterCollection overloads. More info: https://simpleinjector.org/coll1. If your intention is to replace the existing registration with this new registration, you can allow overriding the current registration by setting Container.Options.AllowOverridingRegistrations to true. More info: https://simpleinjector.org/ovrrd.]
   SimpleInjector.Internals.NonGenericRegistrationEntry.ThrowWhenTypeAlreadyRegistered(InstanceProducer producer) +102
   SimpleInjector.Internals.NonGenericRegistrationEntry.Add(InstanceProducer producer) +59
   SimpleInjector.Container.AddInstanceProducer(InstanceProducer producer) +105
   SimpleInjector.Container.AddRegistrationInternal(Type serviceType, Registration registration) +69
   SimpleInjector.Container.AddRegistration(Type serviceType, Registration registration) +131
   SimpleInjector.Container.RegisterSingleton(TService instance) +183
   Rebus.SimpleInjector.SimpleInjectorContainerAdapter.SetBus(IBus bus) +55
   Rebus.Config.RebusConfigurer.Start() +2356
   MyModule.RegisterServices(Container container) +497
   SimpleInjector.PackageExtensions.RegisterPackages(Container container, IEnumerable`1 assemblies) +50
   Myproject.SimpleInjectorInitializer.InitializeContainer(Container container) +35
   Myproject.SimpleInjectorInitializer.Initialize() +68
   Myproject.Startup.Configuration(IAppBuilder app) +28

修改

然后我删除了第二个Configure.With( ... )代码块,现在当我执行_bus.Send( message )时,我在使用者流程中遇到另一个错误

  

处理ID为fef3acca-97f4-4495-b09d-96e6c9f66c4d的消息时未处理的异常1:SimpleInjector.ActivationException:没有注册类型IEnumerable&lt; IHandleMessages&lt; MyMessage&gt;&gt;可以找到。但是,有一个注册IHandleMessages&lt; MyMessage&gt ;;你的意思是调用GetInstance&lt; IHandleMessages&lt; MyMessage&gt;&gt;()或依赖于IHandleMessages&lt; MyMessage&gt;?或者您的意思是使用RegisterCollection注册类型集合?

堆栈追踪:

2017-04-13 10:21:03,805 [77] WARN  Rebus.Retry.ErrorTracking.InMemErrorTracker - 
   at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type serviceType)
   at SimpleInjector.Container.GetInstanceForRootType[TService]()
   at SimpleInjector.Container.GetInstance[TService]()
   at SimpleInjector.Container.GetAllInstances[TService]()
   at Rebus.SimpleInjector.SimpleInjectorContainerAdapter.<GetHandlers>d__3`1.MoveNext()

3 个答案:

答案 0 :(得分:4)

我通常建议每个容器实例只保留一个IBus,因为总线可以考虑&#34;应用程序&#34;就其本身而言,恰好与IoC容器是一个可以“托管”#34;申请一生的持续时间。

Rebus没有提供Conforming Container抽象,因为我同意Mark Seemann认为这是一个注定要失败的项目。实际上,正如the wiki page mentions一样,Rebus曾经提供过处理程序的自动注册,但结果证明是有问题的。

相反,Rebus鼓励您提供一个容器适配器&#34; (IContainerAdapter)的实施,其职责是:

  • 查找处理程序
  • 提供了一种以正确的方式注册IBusIMessageContext
  • 的方法

其中容器适配器是为Autofac,Castle Windsor,SimpleInjector等提供的。但是,不需要提供容器适配器 - Configure.With(...) rant很高兴只接收一个&#34;处理程序活化剂&#34; (IHandlerActivator的实现),所以如果您只想使用IoC容器查找处理程序并自己注册IBus,那么您也可以通过实现IHandlerActivator并查找你的容器中的处理程序。

TL; DR :Rebus方法是将IoC容器的实例视为单独的应用程序,因此只在其中注册一个IBus是有意义的。

完全可以在一个进程中新建多个容器实例,以便托管多个应用程序(甚至是具有不同消息SLA的应用程序的多个实例)。

答案 1 :(得分:2)

例外情况:“类型IBus已经注册”。根据您的堆栈跟踪,第二次添加IBus是在SimpleInjectorContainerAdapter内。你必须先找出它第一次注册的时间。这很容易做到;刚刚在创建IBus之后注册了一个虚拟Container作为第一次注册,并查看它在哪里爆炸的堆栈跟踪。

答案 2 :(得分:2)

你引用了我的答案(问题),所以我会与你分享我是如何实现它的。 正如您将看到的,使用特定接口,我将命令与事件分开。

然后,就在队列的消费部分,我做了这种注册:

    public static void Register()
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies()
            .Where(i => i.FullName.StartsWith("MySolutionPrefix"))
            ;

        var container = new Container();

        // http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perexecutioncontextscope
        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();


        var serviceType = typeof(IHandleMessages<>).Name;
        // this is extension method to get specific handlers
        IEnumerable<Type> commandHandlers = assemblies.GetHandlers(serviceType, typeof(ICommand));
        container.Register(typeof(IHandleMessages<>), commandHandlers.Concat(new List<Type> { typeof(HistorizeCommandHanlder) }));

        // NOTE Just command Handlers
        container.RegisterCollection(typeof(IHandleMessages<>), commandHandlers);


        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            //.... logging, transport (I created my own transport on mongoDb), routing, sagas and so on
            .Options(o =>
            {
                // This simply my personal transport as Register<IOneWayClientTransport> 
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                // this is more complicated because i want that automatically the message is copied on
                // a separate topic for each handler
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        // It is necessary because otherwise it sends published messages to no-one 
        commandHandlers.GetHandledSubTypes(serviceType, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Subscribe(i).Wait());

        // NOTE just events handlers
        IEnumerable<Type> eventHandlers = assemblies
            .GetHandlers(serviceType, typeof(IVersionedEvent))
            .Except(commandHandlers)
            //.Except(new List<Type> { typeof(TempHandlerLogDecorator) })
            ;
        foreach (var handler in eventHandlers)
            ConfigureServiceBus(mongoDbConnectionProvider, db, handler.FullName, new[] { handler });
    }

    private static IBus ConfigureServiceBus(MongoDbConnectionProvider mongoDbConnectionProvider, IMongoDatabase db, string inputQueueName, IEnumerable<Type> handlers)
    {
        var container = new Container();

        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();

        container.RegisterCollection(typeof(IHandleMessages<>), handlers);

        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            .... logging, Subscriptions
            // this is a consumer only for inputQueueName
            .Transport(t => t.UseMongoDb(db, settings.TopicBasedMessageQueueName, inputQueueName))
            .Options(o =>
            {
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        handlers.GetHandledSubTypes(typeof(IHandleMessages<>).Name, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Advanced.Topics.Subscribe(i.GetDecoupledTopic(settings.DecouplingHandlersRegistration)).Wait());

        return bus;
    }

我仍在使用Rebus 3.0.1和SimpleInjector 3.2.3。