2

寻找一些想法/模式来解决我将很快开始工作的系统的设计问题。毫无疑问,我需要使用某种消息传递(可能是 MSMQ)在系统的某些区域之间进行通信。我不想重新发明轮子,但同时我想确保我使用正确的工具来完成这项工作。我一直在修改和阅读 NServiceBus,它的功能给我留下了深刻的印象——但我不确定它是否适用于我想要实现的目标。

这是系统需要做什么的(希望)非常简单和概念性的描述:

我有一个客户可以向其发送消息的服务。该服务是“Fire and Forget”——客户得到的最多可能是成功或失败(成功是收到消息)。

每条消息的处理/处理都非常重要,并且可能会占用大量系统资源。因此,只能同时处理 X 条消息,其中 X 是可配置的值(基于系统规范等)。传入的消息将存储在队列中,直到“轮到他们”处理。

对于每个客户端,消息必须按顺序处理(FIFO)。但是,某些客户端可能会连续发送许多消息(数千条或更多),例如,如果它们在一段时间内失去连接。出于这个原因,必须在客户端之间以循环方式处理消息——不允许客户端吃饱,也不允许客户端饿死。因此,系统要么必须能够查询特定客户端的队列,要么为每个客户端创建单独的队列(自动,因为客户端在编译时不为人所知)并轮流从中提取。

我目前的想法是,我真的只需要使用 vanilla MSMQ,创建一个服务来接受消息并将它们写入一个或多个队列,然后创建一个进程来从队列中读取消息并处理/处理它们。但是,使用 NServicebus 之类的东西获得的可靠性、审计、可扩展性和易于配置看起来非常吸引人。

ESB 是不是适合这项工作的工具?还有其他一些我应该研究的技术或模式吗?

更新

一些澄清。

关于“按顺序”处理消息——在单个客户端的上下文中,消息绝对需要按照接收到的顺序进行处理。解释确切的原因很复杂,但这是一个坚定的要求。我没有提到每个客户端只能同时处理一条消息。因此,即使有 10 个工作线程并且只有一个客户端有消息等待处理,一次也只会处理其中一条消息——不必担心竞争条件。

我相信这通常可以通过 vanilla MSMQ 实现——您可以在队列中拥有一个消息列表,并始终先获取最旧的消息。

我还想澄清一个循环排序的用例。在此示例中,我有两个发送消息的客户端(A 和 B),并且只有一个工作线程。所有队列都是空的。客户端 A 一夜之间失去了连接,因此在上午 8 点向服务发送了 1000 条消息。这些消息排队,工作线程获取最旧的消息并开始处理它。在处理第一条消息时,客户端 B 将消息发送到服务中,该服务排队(如前所述,可能在单独的队列中)。当客户端 A 的第一条消息完成处理后,逻辑应该检查客户端 B 是否有消息(它是客户端 B 的“轮到”),既然找到了,就下一步处理它。

如果客户端 B 在此期间没有发送消息,则工作人员将继续一次处理客户端 A 的消息,始终在处理后检查其他客户端队列是否包含等待消息,以确保没有客户端被饿死。

我仍然觉得 ESB 之间可能存在不匹配,这个问题是 ESB 旨在促进服务之间的通信;我想要实现的是消息/通信和选择性排队系统的组合。

4

2 回答 2

2

使用像 NServiceBus 这样的 ESB 似乎是解决问题的好方法。但是根据你的概念描述,有一些事情需要考虑。让我们使用 NServiceBus 作为可能的 ESB 解决方案,逐步了解您的需求:

我有一个客户可以向其发送消息的服务。该服务是“Fire and Forget”——客户得到的最多可能是成功或失败(成功是收到消息)。

这很容易通过 NServiceBus 完成。您可以从客户端 Bus.Send(Message)。如果您的客户需要答案,您可以使用 Bus.Return(ErrorCode)。您提到“成功是收到消息”。如果您使用像 NServiceBus这样的 ESB,则由消息传递平台来传递消息。因此,如果您的 Bus.Send 没有引发异常,则可以确定消息已正确发送。因此,您可能不必将成功/失败消息发送回客户端。

每条消息的处理/处理都非常重要,并且可能会占用大量系统资源。因此,只能同时处理 X 条消息,其中 X 是可配置的值(基于系统规范等)。传入的消息将存储在队列中,直到“轮到他们”处理。

使用 NServiceBus 时,可以通过设置“NumberOfWorkerThreads”选项来配置工作线程的数量。如果您的服务器有多个核心/cpus,您可以使用此设置来平衡工作负载。

对于每个客户端,消息必须按顺序处理(FIFO)。

根据您的要求,这可能会导致问题。ESB 通常不承诺按顺序处理消息,如果它们有许多线程处理消息。在 NServiceBus 的情况下,您可以将一组消息从客户端发送到总线,这些消息将按顺序处理。此外,您可以使用Sagas来解决一些有序消息传递问题。

但是,某些客户端可能会连续发送许多消息(数千条或更多),例如,如果它们在一段时间内失去连接

使用 ESB 解决方案时,您的服务器不必启动客户端即可工作。客户端仍然可以发送消息,服务器将在重新联机后立即开始处理它们。这是一个关于这个的小介绍。

出于这个原因,必须在客户端之间以循环方式处理消息——不允许客户端吃饱,也不允许客户端饿死。

这不是问题,因为您已决定使用消息 :)

因此,系统要么必须能够查询特定客户端的队列,要么为每个客户端创建单独的队列(自动,因为客户端在编译时不为人所知)并轮流从中提取。

你能扩展一下吗?我不确定你在这个上的设计。

于 2010-08-23T05:28:18.220 回答
2

所以系统要么必须能够查询特定客户端的队列,

使用游标在 MSMQ 队列中搜索来自特定客户端的消息可能效率低下且无法扩展。

或为每个客户端创建单独的队列(自动,因为客户端在编译时不为人所知)并轮流从中提取。

MSMQ 无法自动创建队列。所有消息都必须首先发送到已知队列。但是,您自己的自定义调度程序服务可以按需创建新队列并将消息副本放入其中。

[[我避免说“移动”消息,因为你不能用应用程序代码这样做;您只能阅读消息并使用原始数据创建新消息。例如,当您使用 Source Journaling 时,这种区别很重要。]]

干杯

约翰·布雷克韦尔

于 2010-08-23T10:08:50.703 回答