1

我们刚刚开始构建我们的 JMS 架构并具有以下基本设置:

  1. 玻璃鱼 v2.1
  2. MDB 通过 TopicConnectionFactory 监听一个主题(都在本地服务器上)

现在,当有新消息到达时,MDB 会生成一个工作线程,即使我们已经按顺序传递消息,我们也需要一种同步机制,以便线程在并发处理请求之前检查特定条件。

这些线程有没有办法共享数据?或者是否有任何其他机制(除了数据库表/行锁)可以用于同步?

提前致谢。


澄清一下,我不是在创建自己的线程。正如每个人都正确指出的那样,容器为我做到了。让我用一个例子来帮助解释我的困境。

-消息 A 到达 t=0,它“创建”数据 id 1

-消息 B 到达 t=0.1,它“更新”数据 id 1

现在假设容器产生 2 个工作人员来处理 A 和 B,并且“创建”数据比更新它需要更多的时间,更新将更早处理并且没有效果。

为了更清楚,

-在处理消息 B 时,我会在 t=1 时查找数据 id 1(没有找到它,因此没有做任何事情就完成了)。

- 在 t=2 处理消息 A 时将创建数据 id 1。

4

6 回答 6

6

学究警告!我是那种阅读实际技术规范的人。

阅读EJB 规范版本 3.0,第 21.1.2 节(编程限制)不允许在代码中使用线程。这是语言和基本原理...

企业 bean 不得尝试管理线程。企业 bean 不得尝试启动、停止、挂起或恢复线程,或者更改线程的优先级或名称。企业 bean 不得尝试管理线程组。

这些函数是为 EJB 容器保留的。允许企业 bean 管理线程会降低容器正确管理运行时环境的能力。

所以如果你照你说的做,EJB 警察会在半夜敲你的门,把你带走。或者你的应用程序可能会出现故障,当你抱怨时供应商会笑。或者根本不会发生任何不好的事情。

但是,正如 duffymo 所说,为什么要这样做?如果您想要大量线程提供的可伸缩性,您可以为您的 MDB 配置它吗?EJB 的重点是为您处理类似的事情。

于 2009-05-07T02:21:47.070 回答
4

我不明白为什么 MDB 必须产生一个工作线程。JMS 中有一个与消息侦听器关联的线程池。那是应该做这项工作的线程。

EJB 规范说没有在你的 bean 中产生线程。容器处理线程。这也包括 MDB。

侦听器应该正在处理它从队列中取出的消息。它需要的数据应该在消息中。有什么需要分享的?

我认为您的方法与推荐的 EJB 实践背道而驰。

于 2009-05-07T01:22:36.307 回答
3

真正的问题是,应用程序服务器通常会自己产生提到的工人。虽然 JMS 保证消息的使用顺序与至少在一个生产者中产生的顺序相同,但 MDB 规范明确指出不保留该顺序(因为提到的工人)。请参阅JSR-000220 Enterprise JavaBeans 3.0 Final Release (ejbcore)的第 5.4.11 节。

没有可移植且 100% 可靠的方法来规避这一点。由于工人的性质,引入了无法控制的种族条件。

幸运的是,尽管是专有且不兼容的,大多数应用程序服务器都有方法来配置工作人员的数量。

对于 JBoss 和 ActiveMQ 这有效:

@PoolClass(value = org.jboss.ejb3.StrictMaxPool.class, maxSize = 1)
@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "maxSessions", propertyValue = "1"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "TEST.FOO")
})
@ResourceAdapter("activemq-ra.rar")
public class NewMessageBean implements MessageListener { ... }

在这种情况下,“maxSessions”是工人的数量。它可能与其他 JMS 提供程序不同,但这应该指向正确的方向。

于 2010-05-05T12:46:45.840 回答
2

如上所述,JMS 框架处理调度问题,例如调度线程。您在这方面所做的任何事情不仅会低于默认行为,而且可能会严重限制 JMS 的功能。

更复杂的 JMS 处理程序设计为跨多个节点(= 服务器)工作,因此任何共享内存解决方案都会将您限制为单个节点上的单个 JVM,这将是一个遗憾,因为 JMS 的最大优势是可伸缩性。

一个可能的 JMSy 解决方案是有一个“cookie”队列将一个虚拟“cookie”消息同步活动。当您的进程执行有争议的活动时,它会“等待”来自“cookie”队列的单个消息,当有争议的工作完成时,它会将 cookie 放回队列中。JMS 的魔力将处理几乎所有的阻塞、等待和错误恢复。

于 2009-05-07T02:35:59.733 回答
0

在每个人都已经回答之后,看起来这个问题的性质完全改变了。我将为后代提供一个非常迟的回应;这假设有某种类型的消息 ID 可用于订购。您说“我们按顺序发送消息”,但您没有确切说明这是如何实现的。

...由于您已经澄清您没有开始自己的线程,因此您基本上在数据@ ID = 1 的“创建”与其随后的“更新”之间存在竞争条件。我假设您在创建和/或更新数据时锁定了 @ ID=1 的数据。所以,有两种可能:

  1. “创建数据”消息首先到达:(1)锁 ID=1,(2)创建数据。(3) 释放锁。(4) 应用待处理的“更新”。
  2. “更新”在“创建”之前到达:(1) 锁定 ID=1,(2) 创建丢失的数据(即,执行“更新插入”:即使数据不存在也插入数据)。(3) 释放锁。(4) 忽略待处理的“create data”消息(“create”消息的序列号低于“update”消息的序列号
    • 如果允许消息发送者同时发送更新和插入(没有请求/响应),那么它们确实应该具有某种序列号。如果是这样,当“插入”到达时,它的序列号将小于该行中的当前值,因此可以忽略它。或者,如果“create”消息类型与“update”消息类型不同,则如果数据已经存在,则始终可以忽略“creates”。

我认为您的问题是如何同步数据。基本上,线程共享对象并创建互斥锁(互斥)以允许单个线程访问数据,从而导致另一个线程阻塞。这可以简单地通过 Java 的低级同步工具(“synchronized”关键字)或帮助实现这一点的内置类(http://java.sun.com/docs/books/tutorial/essential/concurrency/highlevel.html)。

于 2009-12-15T07:58:23.363 回答
0

正如一些人所提到的,您不应该在 MDB(或任何其他类型的 EJB 或 servlet)中创建自己的线程。

许多 EJB 容器实际上不允许您创建和运行线程。不过,有一种安全的方法可以做到这一点,即使用 commonj 规范中的 WorkManager,尽管在这种特殊情况下我认为没有理由这样做,因为 MDB 已经在其自己的“工作线程”中运行。

请参阅此处有关生成线程的信息,以获取有关为什么不应该在 Java EE 服务器中生成线程以及如何在需要时安全地执行此操作的更多信息。

于 2009-05-07T13:25:44.913 回答