我们有一个使用消息驱动bean处理JMS消息的应用程序。此应用程序部署在OC4J应用程序服务器上。 (10.1.3)
我们计划在多个OC4J应用服务器上部署此应用程序,这些服务器将配置为在群集中运行。
问题在于此群集中的JMS消息处理。我们必须确保一次只在整个OC4J集群中处理一条消息。这是必需的,因为必须按时间顺序处理消息。
您是否知道可以控制跨OC4J群集的消息处理的配置参数?
或者您认为我们必须实现自己的同步代码,以便在群集中同步消息驱动的bean吗?
答案 0 :(得分:5)
我已经使用Competing Consumers模式和Lease模式的组合,以相当大的规模对集群中的消息进行顺序处理 - 每天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来保持时钟合理同步。
答案 1 :(得分:-1)
第一点:这是一个非常糟糕的设计,你会严重限制性能,只能一次处理一条消息。我假设你只是为了容错而聚类,因为你不会得到性能改进吗?
您是否正在使用OC4J或其他的默认JMS实现?
我过去曾使用IBM的MQ,并且有一个特性,即队列可以被标记为独占,这意味着只有一个客户端可以连接到它。这似乎提供了你想要的东西。
另一种方法是引入一个序列ID(就像递增计数器一样简单),处理该消息的客户端将检查序列ID是否为下一个期望值,如果不是,则将消息放回。此方法要求不同的客户端持久保存他们在某些集中共享数据存储(例如数据库)中看到的最后一个有效序列ID。
答案 2 :(得分:-1)
我同意stevendick:可能你的设计偏离了轨道。关于序列ID或类似方法,我建议您通过企业集成模式:设计,构建和部署消息传递解决方案(Gregor Hohpe和Bobby Woolf)深入了解消息传递体系结构。这是一本很棒的书,有很多有用的模式......我确信你所面临的力量和问题在那里都有很好的描述。