我们有一个使用消息驱动 bean 处理 JMS 消息的应用程序。此应用程序部署在 OC4J 应用程序服务器上。(10.1.3)
我们计划将此应用程序部署在将配置为在集群中运行的多个 OC4J 应用程序服务器上。
问题在于此集群中的 JMS 消息处理。我们必须确保在整个 OC4J 集群中一次只处理一条消息。这是必需的,因为必须按时间顺序处理消息。
您知道用于控制 OC4J 集群中的消息处理的配置参数吗?
或者你认为我们必须实现我们自己的同步代码来同步整个集群中的消息驱动bean?
我使用竞争消费者模式和租赁模式的组合,对集群中的消息进行了相当大规模的顺序处理 - 每天超过 150 万条消息。
不过,这里是最重要的 - 您一次只能处理一个跨性别的要求将使您无法实现目标。我们有相同的基本要求——消息必须按顺序处理。至少,我们认为我们做到了。然后我们顿悟了——当我们更多地思考这个问题时,我们意识到我们不需要完全排序。我们实际上只需要在每个帐户中订购。因此,我们可以通过将帐户范围分配给集群中的不同服务器来在集群中的服务器之间分配负载。然后,每个服务器负责按顺序处理给定帐户的消息。
这是第二个聪明的部分——我们使用了一个租赁模式来动态地将账户范围分配给集群中的不同服务器。如果集群中的一台服务器出现故障,另一台将抢占租约并接管第一台服务器的责任。
这对我们有用,并且该过程在生产中生活了大约 4 年,然后由于公司合并而被替换。
编辑:
我在这里更详细地解释了这个解决方案:http: //coders-log.blogspot.com/2008/12/favorite-projects-series-installment-2.html
编辑:
好的,明白了。您已经在您需要的级别上进行处理,但是由于您被部署到集群,您需要确保只有一个 MDB 实例在主动从队列中提取消息。另外,您需要最简单可行的解决方案。
我不认为你不需要放弃你现在拥有的 MDB 机制。本质上,我们在这里谈论的是对分布式锁机制的要求,而不是太花哨的词组。
所以,让我提出这个建议。在您的 MDB 注册以接收来自队列的消息时,它应该检查分布式锁,并查看它是否可以获取它。第一个获得锁的 MDB 获胜,只有它会注册接收消息。所以,现在你有了你的序列化。这个锁应该采取什么形式?有很多可能性。好吧,这个怎么样。如果您可以访问数据库,那么它的事务锁定已经提供了您需要的一些东西。创建一个单行表。行中是当前持有锁的服务器的标识符和过期时间。这是服务器的租约。每个服务器都需要有一种方法来生成其唯一标识符,例如,可能是服务器名称加上线程 ID。
如果服务器可以获得对该行的更新访问,并且租约已过期,它应该获取它。否则,它就放弃了。如果它抢到租约,它需要在不久的将来更新该行,比如五分钟左右,然后提交更新。活动服务器应在租约到期之前更新租约。我建议在剩下一半时间时更新它,因此,如果租约在五分钟后到期,则每 2-1/2 分钟更新一次。有了这个,您现在可以进行故障转移。如果活动的 MDB 死了,另一个 MDB(并且只有一个)将接管。
我认为这应该很简单。现在,您想让休眠的 MDB 偶尔检查锁以查看它是否已被释放。
因此,活动的 MDB 和休眠的 MDB 都必须定期做一些事情。您可能会让他们产生一个单独的线程来执行此操作。如果您这样做,许多应用程序引擎供应商不会高兴,但添加一个线程没什么大不了的,尤其是因为它大部分时间都在休眠。另一种选择是绑定到许多引擎提供的计时器机制,并让它定期唤醒您的 MDB 以检查租约。
哦,顺便说一句 - 确保服务器管理员使用 NTP 来保持时钟合理同步。
第一点:这是一个非常糟糕的设计,一次只能处理一条消息会严重限制性能。我假设您只是为了容错而进行集群,因为您不会获得性能改进?
您是在使用 OC4J 的默认 JMS 实现还是其他实现?
我过去使用过 IBM 的 MQ,它具有可以将队列标记为独占的功能,这意味着只有一个客户端可以连接到它。这似乎可以提供您想要的东西。
另一种方法是引入序列 ID(就像递增计数器一样简单),处理消息的客户端将检查序列 ID 是否是下一个预期值,如果不是,则放回消息。这种方法要求不同的客户端保留他们在一些集中共享的数据存储(例如数据库)中看到的最后一个有效序列 ID。
我同意 stevendick 的观点:可能是你的设计偏离了轨道。关于序列 ID 或类似方法,我建议您通过Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (by Gregor Hohpe y Bobby Woolf)深入了解消息传递架构。这是一本很棒的书,有很多有用的模式......我相信你所面临的力量和问题在那里得到了很好的描述。