我有两个使用Rebus的控制台应用程序。它们都引用了定义消息(命令和事件)的程序集。 控制台应用" A"发送命令并侦听事件以进行日志记录(例如:发送CreateTCommand并侦听TCreatedEvent)。控制台应用" B" (实际上是一个ASP.NET核心应用程序)监听命令并处理它们(例如:一个传奇由CreateTCommand启动,聚合被创建并引发一个TCreatedEvent)。在app" B"的过程中的另一个DLL中有一个TCreatedEvent的处理程序。
所以我有app" A"发送的创建命令以及创建活动的两个处理程序,一个在app" A"和应用程序中的一个" B"。
问题:当我从app" A"发送命令时第一次,app" B"引发创建的事件,该事件在同一进程中触发处理程序。处理程序 在app" A"没有触发。 来自app" A"的进一步命令总是由应用程序中的传奇处理" B"但是创建的事件在该过程中永远不会再次触及处理程序,但是由app" A" !!!处理 有时(我无法理解如何重现)来自app" A"不是应用程序中的传奇处理" B" (我在MSMQ中的错误队列中找到了带有异常&#34的命令;带有Id的消息无法分派给任何处理程序")。 有时(很少)两个处理程序都被击中。但我无法一致地重现这种行为......
我对此的感觉(对Rebus一无所知,这对我来说很新):
Rebus的配置代码在2个应用程序中是相同的,如下所示:
const string inputQueueAddress = "myappqueue";
var mongoClient = new MongoClient("mongodb://localhost:27017");
var mongoDatabase = mongoClient.GetDatabase("MyAppRebusPersistence");
var config = Rebus.Config.Configure.With(new NetCoreServiceCollectionContainerAdapter(services))
.Logging(l => l.Trace())
.Routing(r => r.TypeBased()
.MapAssemblyOf<AlertsCommandStackAssemblyMarker>(inputQueueAddress)
.MapAssemblyOf<AlertsQueryStackAssemblyMarker>(inputQueueAddress)
)
.Subscriptions(s => s.StoreInMongoDb(mongoDatabase, "subscriptions"))
.Sagas(s => s.StoreInMongoDb(mongoDatabase))
.Timeouts(t => t.StoreInMongoDb(mongoDatabase, "timeouts"))
.Transport(t => t.UseMsmq(inputQueueAddress));
var bus = config.Start();
bus.Subscribe<AlertDefinitionCreatedEvent>();
bus.Subscribe<AlertStateAddedEvent>();
bus.Subscribe<AlertConfigurationForEhrDefinitionAddedEvent>();
services.AddSingleton(bus);
services.AutoRegisterHandlersFromThisAssembly();
我希望有人可以提供帮助,这让我疯狂......
p.s。:当传递isCentralized时,问题也存在:true到subscription.StoreInMongoDb()。
编辑1:我添加了控制台日志记录,您可以看到这种奇怪的行为: https://postimg.org/image/czz5lchp9/
第一个命令发送成功。它由saga处理,事件触发了控制台app#34; A&#34;。 Rebus说第二个命令没有发送给任何处理程序,但它实际上是由saga处理的(我在调试中遵循了代码)并且事件由app中的处理程序处理&#34; B&#34;而不是&#34; A&#34; ...为什么? ;(
编辑2:我正在调试Rebus的源代码。我注意到在ThreadPoolWorker.cs中,方法是TryAsyncReceive
async void TryAsyncReceive(CancellationToken token, IDisposable parallelOperation)
{
try
{
using (parallelOperation)
using (var context = new DefaultTransactionContext())
{
var transportMessage = await ReceiveTransportMessage(token, context);
if (transportMessage == null)
{
context.Dispose();
// no need for another thread to rush in and discover that there is no message
//parallelOperation.Dispose();
_backoffStrategy.WaitNoMessage();
return;
}
_backoffStrategy.Reset();
await ProcessMessage(context, transportMessage);
}
}
在应用程序&#34; B&#34;发布TCreatedEvent之后,在应用程序&#34; A&#34;代码到达 等待ProcessMessage(context,transportMessage) transportMessage是实际事件。在应用程序&#34; B&#34;的过程中未达到此行代码。好像消息的第一个接收者将其从MSMQ的队列中删除。正如我所说,我对Rebus和总线一般都很陌生,但如果这种行为符合设计,我会感到非常困惑......多个进程中的多个总线如何监听同一个队列? / p>
答案 0 :(得分:2)
您永远不应该有两个总线实例接收来自同一队列的消息,除非它们是同一端点的多个实例。
当两个进程使用相同的输入队列时,它们将相互接收消息。如果您正在实现competing consumers pattern,使用它在工作进程集群之间均匀分配工作,但是您无法在多个不同的端点之间共享输入队列,那么这绝对可以。
我的猜测是,如果让每个总线实例使用自己的输入队列,一切看起来都会更加可预测;)
现在你告诉我这些处理程序需要住在主应用程序中
不,我不是:)我告诉你,如果让两个不同的应用程序抢夺对方的消息,你将得到不可预测的结果。
虽然所有处理程序都在同一个总线实例中(因此可以通过来自同一队列的消息调用),但最常见的情况是,您将以符合您希望的方式拆分应用程序进化系统。
通过这种方式,您可以一次更新一个应用程序,避免大“停止世界” - 更新。
您可以通过启动多个端点来执行此操作,每个端点使用自己的队列 - 然后在端点之间使用ROUTE消息进行通信。
考虑要将命令发送到命令处理器的场景。命令处理器是一个Rebus端点,它从HH:mm
队列中获取消息。
在发件人的最后,您将配置一个“端点映射”(您可以在the routing section on the Rebus wiki中阅读更多相关信息,如下所示:
command_processor
这将使发件人能够简单地进入
Configure.With(...)
.Transport(t => t.UseMsmq("sender"))
.Routing(r => {
r.TypeBased()
.Map<TheCommand>("command_processor");
})
.Start();
然后总线将知道将命令消息发送到哪个队列。
我希望这更清楚:)
请注意,这是point-to-point messaging的一个非常简单的情况,其中一个端点发送一条消息,该消息应由一个其他端点使用。 Rebus可以为您提供其他几种模式,例如: request/reply和publish/subscribe。