使用 JMS 队列的潜在陷阱?

Posted

技术标签:

【中文标题】使用 JMS 队列的潜在陷阱?【英文标题】:Potential pitfalls in using a JMS queue? 【发布时间】:2011-01-11 02:46:57 【问题描述】:

我被要求设计和实施一个系统,用于接收来自大量设备的大量自动传感器数据。该数据将定期生成,并以 http post 中的 xml 格式发送到服务器。如果设备没有收到来自服务器的特定确认,它们将继续重新发送相同的数据。在通过事务将其插入主数据库中的多个表之前,需要对这些数据进行一些潜在的繁重处理,此外,一些数据点需要排队以重定向到其他外部 url。

我计划使用带有 servlet 的 Java 应用程序服务器(倾向于 GlassFish)来接收传入的数据。我想实现某种排队机制来临时存储数据,以便返回传感器的响应不依赖于所有中间处理。单独的独立队列也是数据重定向部分的要求。在做了一些研究之后,两个主要选项似乎是:

1) 在应用服务器上安装数据库并为各种队列使用表。队列将由 Java 应用程序处理,可以在应用服务器中运行,也可以作为自己的服务独立运行。

2) 使用数据库支持的 JMS 解决方案来实现排队。

我对 JMS 不是很熟悉,但从我读到的内容来看,在这种情况下它似乎是更好的解决方案。主要要求是传感器数据在处理之前不会丢失或从队列中丢弃,并且或多或少是按顺序处理的。我们还想让在某些时间停止处理某些队列变得容易,但仍然让它们积累数据,并且这些消息永远不会自动过期。

使用策略 1,我很清楚如何满足这些要求,但它可能不如策略 2 健壮和可扩展,而且开发起来更复杂,因为我需要编写自己的多线程代码来处理各种独立队列。我想知道为此目的使用 JMS 队列可能存在哪些潜在缺陷,因为我以前从未使用过它们。

数据完整性是一个大问题,因此我需要确保 JMS 能够保证在服务器重新启动、断电或由于某种原因队列变得非常大的情况下不会丢失数据。例如,在一段时间内完成对主数据库的事务是否可能会导致 JVM 内存不足、崩溃并丢失所有累积的数据? (这将是噩梦般的场景)。

另外,我想知道是否有任何方法可以通过应用服务器管理工​​具暂停 JMS 队列处理或轻松查看队列中的内容(我将排队一个对象,该对象将是消息 xml 加上其他一些数据,包括收到的时间戳等)我在这里阅读了一些处理相关问题的帖子,但希望得到一些直接的反馈。基本上,我想知道 JMS 不是合适的排队解决方案的实例(如果有的话),以及这是否是其中一种情况。非常感谢任何建议。

【问题讨论】:

根本不是 Java 人,但这是否意味着等待回复队列等待结果响应?如果您的客户端协议是 HTTP,这似乎会破坏交易。这不就得绑个线程了吗? 实际上我必须处理两种不同的排队场景。一个是到主数据库的队列,这将是通过 jdbc 连接池的连接。这是 servlet 将写入的内容。另一个将包含该数据的一个子集,在主队列中成功处理后将其放入这个单独的队列中。此队列的消费者将通过 http 将消息发送到另一个站点。这意味着初始 servlet 响应将被两个队列从 http post 到第三方站点的结果中分开。 【参考方案1】:

Kaleb 的回答非常雄辩地谈到了 JMS 的好处,但既然您问的是陷阱,这就是我能想到的。

并非所有 JMS 实现都是平等的。从理论上讲,您可以使用适合您需要的任何实现,但除非您准备进行一些严重的负载测试和故障条件测试,否则您无法知道特定实现在您的特定用例下不会失败。 大多数 JMS 使用事务数据存储(如关系数据库)作为后端。这意味着您不必直接写入您熟悉的任何数据存储,而是必须依赖 JMS 实现在您和存储的消息之间的额外层。 虽然交换 JMS 实现以找到完全符合您需求的实现似乎是一项简单的工作,因为同质 JMS API、故障处理的关键特性、JMS 服务器监控以及上面存在的所有其他很酷的东西如果您确实更改了实现,那么处理消息之外的消息将是一件麻烦事。

也就是说,我认为您自己写数据库而不是使用 JMS 会很疯狂。首先,ActiveMQ 是在许多企业环境中使用的古老的 JMS 服务器。关于第二点,事实是你最终只是自己编写了那个额外的层来实现消息传递,而且你的代码不会有成千上万的眼睛(或一组付费开发人员的唯一工作)响应客户并确保 JMS 实施是可靠的)。第三点,后端数据存储也是如此。使用 JMS,从长远来看,您会为自己省去麻烦。

【讨论】:

谢谢杰里科。是的,我在想尝试自己解决一个以前已经解决过很多次的问题可能是愚蠢的。我只是想对整个场景进行完整性检查,然后再投入大量精力使用基于 JMS 的解决方案,但最终结果是错误的方法。我有一些编写自定义大容量 JMeter 测试的经验,在这里应该派上用场。【参考方案2】:

如果您想走 JMS 路线,独立的兼容 JMS 的消息代理(与您的应用服务器分开)将是一个不错的选择。消息代理的范围从免费的开源(如http://activemq.apache.org/ 的 ActiveMQ 或https://mq.dev.java.net/ 的 OpenMQ)到大型商业解决方案(http://www-01.ibm.com/software/integration/wmq/ 的 IBM 的 WebSphere MQ 是最大的之一)。

消息代理提供有保证的传递(前提是服务器已启动并正在侦听),并且您可以做很多事情来确保系统是故障安全的,包括集成的备份代理服务器和即时电源备份。如果您的应用服务器没有接收消息,代理队列最终可能会用完空间,但您可以分配巨大的队列深度(100 GB),并在消息未得到处理且队列到达时让服务器发送警报一定比例。

然后,您的 Java 应用程序将完全在另一台服务器上运行,并连接到代理并尽快从队列中提取消息。如果应用服务器因任何其他原因崩溃或停止接收消息,代理只会将所有消息保留在该队列中,直到应用服务器再次开始接收它们。

【讨论】:

不要吹毛求疵(我自己也是 JMS 的忠实粉丝),但这些都不是真正的“陷阱”。 感谢卡莱布的建议。我将开始尝试使用 JMS。我最初可能会使用 GlassFish 的内置功能,然后在适应了之后再考虑设置单独的消息代理。只是为了安心,我可能还会将前几天的消息记录到应用服务器上的数据库中,以防万一。你有什么好的链接或书籍可以推荐,可以更详细地描述这些东西吗? @Kaleb JMS 是否提供像 FIFO 这样的消息的排序保证?【参考方案3】:

您将希望在您的实现中实现一个有害消息队列 - 这是在经过一定次数的重试后无法处理的消息将到达的地方。

您可能需要编写一些代码来检查该队列中的消息,并在修复导致它们失败的任何问题后将它们重新发送到适当的目的地。

如果消息处理的顺序很重要,那么最终进入有害队列的消息可能意味着所有处理都将停止,直到该消息得到纠正。

就容错而言,您可以让消费服务的多个实例订阅同一个队列或主题,从而即使一个或多个实例出现故障也能继续处理。

最后,有一个看门狗进程 ping 消息队列中的各个消费者,如果没有响应,让它发送一条消息,导致新实例启动。通过这种方式,您的消息处理环境可以在某种程度上实现自我调节。

【讨论】:

以上是关于使用 JMS 队列的潜在陷阱?的主要内容,如果未能解决你的问题,请参考以下文章

消息队列ActiveMQ的使用详解

使用Spring集成组件关联2个JMS队列之间的消息

读取与从 JMS 队列复制消息..使用 logstash 到 ES

一条 JMS 消息复制到两个队列

JMS 主题与队列 - 意图

如何使用 Artemis JMS 管理 API 创建持久队列