我是消息传递架构的新手,所以我可能会采用错误的方式。但我想通过解决一个小问题在我的团队中慢慢介绍NServiceBus。
议程中的任命有州。两个用户可能在相同的应用程序中查看相同日程中的相同约会。他们通过中央服务器上的远程会话启动此应用程序。因此,如果用户1更新约会的状态,我希望用户2看到新的状态'实时'。
如果要模拟这个或做出概念验证,我做了一个新的控制台应用程序。通过NuGet我得到了NServiceBus和NServiceBus.Host,因为我从文档中了解到我需要两者。我知道在生产代码中不建议将所有内容放在同一个程序集中,但发布者和订阅者很可能最终会在同一个程序集中... ...
在类Program方法Main中我编写了以下代码:
BusConfiguration configuration = new BusConfiguration();
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
configuration.TimeToWaitBeforeTriggeringCriticalErrorOnTimeoutOutages(new TimeSpan(1, 0, 0));
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
using (IStartableBus bus = Bus.Create(configuration))
{
bus.Start();
Console.WriteLine("Press key");
Console.ReadKey();
bus.Publish<Events.AppointmentStateChanged>(a =>
{
a.AppointmentID = 1;
a.NewState = "New state";
});
Console.WriteLine("Event published.");
Console.ReadKey();
}
在类EndPointConfig方法中定制我添加:
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
AppointmentStateChanged是Events文件夹中的一个简单类,如下所示:
public class AppointmentStateChanged: IEvent {
public int AppointmentID { get; set; }
public string NewState { get; set; }
}
AppointmentStateChangedHandler是事件处理程序:
public class AppointmentStateChangedHandler : IHandleMessages<Events.AppointmentStateChanged> {
public void Handle(Events.AppointmentStateChanged message) {
Console.WriteLine("AppointmentID: {0}, changed to state: {1}",
message.AppointmentID,
message.NewState);
}
}
如果我启动一个控制台应用程序一切正常。我看到处理程序处理事件。但是,如果我尝试启动第二个控制台应用程序,它会崩溃:System.Messaging.MessageQueueException(请求的操作的超时已过期)。因此,我必须做错事,让我猜测我不会在更高层次上理解某些事情。有人能指出我正确的方向吗?
更新 Everthing位于命名空间AgendaUpdates中,但AgendaUpdates.Events命名空间中的事件类除外。
更新2 采取的步骤:
- &GT;我通过使用原始和复制的解决方案启动2个视觉工作室来测试它。然后在IDE中启动两个控制台应用程序。
答案 0 :(得分:3)
我不确定为什么你会得到特定的异常,但我可以解释为什么你要做的事情失败了。问题是在同一个应用程序中没有发布者和订阅者(这是可能的并且可能有用);问题是你在同一台机器上运行同一个应用程序的两个实例。
NServiceBus依赖于排队技术(在您的情况下为MSMQ),并且为了使一切正常工作,每个应用程序都需要拥有自己的唯一队列。当您启动两个相同的实例时,两者都试图共享同一个队列。
有一些事情你可以修改以使你的场景工作,并更好地了解排队的工作原理:
无论您采用哪种方式,都需要调整MessageEndpointMappings(在使用者/订阅者身上)以反映主机/发布者队列所在的位置(消息类型的“所有者”):
http://docs.particular.net/nservicebus/messaging/message-owner#configuring-endpoint-mapping
根据您的更新进行修改
我知道这是一个测试设置/概念验证,但考虑这两个部署(相同代码)与发布者/主机和订阅者/客户端仍然有用。因此,让我们将原始主机称为客户端并复制客户端。我假设您不希望每个订阅另一个(至少对于此基本测试)。
此外,请确保在计算机上以管理员身份运行这两个IDE。我不确定这是不是在干扰。
在副本中,我将App.Config中的MessageEndpointMappings更改为“AgendaUpdates2”,我得到了MSMQ异常:“队列不存在或者您没有足够的权限来执行操作”
由于副本是客户端,因此您希望将其映射指向主机。所以这应该是“AgendaUpdates”(省略“2”)。
在副本中,我将这行代码添加到EndPointConfig:configuration.EndpointName(“AgendaUpdates2”);我得到了MSMQ异常:“队列不存在或者您没有足够的权限来执行操作”
在副本中,我将这行代码添加到Program类的Main方法中:configuration.EndpointName(“AgendaUpdates2”);按键
后再次获得原始异常我之前没有注意到这一点,但您不需要两次配置端点。我相信你的EndPointConfig没有被调用,因为它仅在通过NSB主机可执行文件进行托管时使用。您可以删除此课程。
这听起来很合理,但请记住,如果是订户,您的副本不应该发布,所以在启动后不要按任何键(只按原始键)。
答案 1 :(得分:1)
如果您希望发布者也是消息的接收者,您希望在配置中指定它。
this article清楚地解释了这一点,您的问题的解决方案完全在文章的最后。