Asp.net mvc 基于内容的Rebus多队列
设置:使用SimpleInjector在asp.net mvc项目中重新创建 我需要创建两个处理程序来接收消息,每个处理程序都来自一个特定的队列。通过遵循我在上面的发现,我创建了类似的代码 在类库中,我有一个实现SimpleInjectorAsp.net mvc 基于内容的Rebus多队列,asp.net-mvc,simple-injector,azure-servicebus-queues,rebus,Asp.net Mvc,Simple Injector,Azure Servicebus Queues,Rebus,设置:使用SimpleInjector在asp.net mvc项目中重新创建 我需要创建两个处理程序来接收消息,每个处理程序都来自一个特定的队列。通过遵循我在上面的发现,我创建了类似的代码 在类库中,我有一个实现SimpleInjectorIPackage的类,其代码如下: public void RegisterServices( Container container ) { container.Register<IHandleMessages<MyMessage>
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(…)
代码块,现在,当我执行\u bus.Send(message)
时,我在消费者过程中遇到另一个错误,它说
处理ID为fef3acca-97f4-4495-b09d-96e6c9f66c4d的消息时出现未处理的异常1:SimpleInjector.ActivationException:找不到类型IEnumerable的注册。但是,IHandleMessages有一个注册;您是想调用GetInstance()还是依赖IHandleMessages?或者您的意思是使用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()
2017-04-13 10:21:03805[77]警告Rebus.Retry.ErrorTracking.InMemErrorTracker-
在SimpleInjector.Container.ThrowMissingInstanceProducerException(类型serviceType)
在SimpleInjector.Container.GetInstanceErrorOttoType[TService]()
在SimpleInjector.Container.GetInstance[TService]()
在SimpleInjector.Container.GetAllInstances[TService]()
在Rebus.SimpleInjector.SimpleInjectorContainerAdapter.d_u3`1.MoveNext()中
异常状态为:“类型IBus已注册”。根据堆栈跟踪,第二次添加IBU是在SimpleInjectorContainerAdapter
内。你必须知道它是什么时候第一次注册的。这很容易做到;刚刚注册了一个虚拟的IBus
作为创建容器后的第一个注册,并查看它爆炸的堆栈跟踪。我通常建议每个容器实例只保留一个IBus
,因为总线本身可以被视为“应用程序”,这恰好与IoC容器是一个可以在应用程序生命周期内“承载”应用程序的对象这一事实非常吻合
Rebus没有提供一致的容器抽象,因为我同意Mark Seemann的观点,那是一个注定要失败的项目。事实上,正如Rebus所使用的那样,Rebus用于提供处理程序的自动注册,但结果证明这是有问题的
相反,Rebus鼓励您提供一个“容器适配器”(IContainerAdapter的实现),其职责是:
- 查找处理程序
- 提供以正确方式注册
IBU
和IMessageContext
的方法
如果为Autofac、Castle Windsor、SimpleInjector等提供了现成的容器适配器,则不需要提供容器适配器–Configure.With(…)
rant只接受“handler activator”(实现IHandlerActivator
),因此,如果您只想使用IoC容器查找处理程序并自行注册IBU
,也可以通过实现IHandlerActivator
并在容器中查找处理程序来实现
TL;DR:Rebus方法是将IoC容器的一个实例视为一个单独的应用程序,因此在其中只注册一个IBU
是有意义的
如果您希望在一个进程中承载多个应用程序(甚至是具有不同消息SLA的应用程序的多个实例),可以创建多个容器实例。您引用了我的SO答案(问题),因此我将与您分享我是如何实现它的。
正如您将看到的,使用特定的接口,我将命令与事件分开
然后,就在队列的消费部分,我进行了这种注册:
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;
}
公共静态无效寄存器()
{
var assemblies=AppDomain.CurrentDomain.GetAssemblys()
.Where(i=>i.FullName.StartsWith(“MySolutionPrefix”))
;
var container=新容器();
// http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perexecutioncontextscope
container.Options.DefaultScopedLifestyle=新的ExecutionContextScopeLifestyle();
var serviceType=typeof(IHandleMessages).Name;
//这是获取特定处理程序的扩展方法
IEnumerable commandHandlers=assemblies.GetHandlers(serviceType,typeof(ICommand));
Register(typeof(IHandleMessages),commandHandlers.Concat(新列表{typeof(historizeCommandHandler)});
//请注意,只有命令处理程序
RegisterCollection(typeof(IHandleMessages),commandHandler);
var bus=Configure.With(新的SimpleInjectorContainerAdapter(容器))
//…日志记录、传输(我在mongoDb上创建了自己的传输)、路由、sagas等等
.选项(o=>
{
//这只是我的个人交通登记
o、 ConfigureDecouplingDatabase(db,settings.TopicBasedMessageQueueName);
//这是更复杂的,因为我想自动复制的消息
//每个处理程序都有一个单独的主题
o、 启用HandlerDecoupling(设置.DecouplingHandlersRegistration);
})
.Start();
container.Verify();
//这是必要的,因为否则它不会向任何人发送已发布的消息
GetHandledSubTypes(serviceType,typeof(IVersionedEvent))
托利斯先生()
.ForEach(i=>bus.Subscribe(i.Wait());
//只需注意事件处理程序
IEnumerable eventHandlers=程序集
.GetHandlers(serviceType,typeof(IVersionedEvent))
。除(命令处理程序)
//.Exception(新列表{typeof(TempHandlerLogDecorator)})
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;
}