3

我目前在 JBoss AS7 中遇到间歇性异常,不幸的是,我还无法重现。

我们目前正在运行两个应用程序,它们基本上设置为 JMS 消息的生产者/消费者。我们使用默认的 HornetQ 配置和 5 个 MDB 池。

两个应用程序都开始正常运行,按预期发送和接收消息。但过了一会儿,所有 MDB 都被锁定(它们每个都收到一条消息,但没有完成处理),JBoss 在此之后挂起一段时间,每十分钟显示以下消息:

[org.jboss.ejb3.invocation] (Thread-21081 (HornetQ-client-global-threads-1636833629)) JBAS014134: EJB Invocation failed on component MyMessageListener for method public abstract void javax.jms.MessageListener.onMessage(javax.jms.Message): javax.ejb.EJBException: JBAS014516: Failed to acquire a permit within 10 MINUTES

从 jarvana 中的 JBoss 代码来看,如果无法获取信号量,似乎会设置此错误:

 /**
 * Get an instance without identity.
 * Can be used by finders,create-methods, and activation
 *
 * @return Context /w instance
 */
public T get() {
    try {
        boolean acquired = semaphore.tryAcquire(timeout, timeUnit);
        if (!acquired)
            throw new EJBException("Failed to acquire a permit within " + timeout + " " + timeUnit);
    } catch (InterruptedException e) {
        throw new EJBException("Acquire semaphore was interrupted");
    }
...

问题是,为什么 MDB 会被锁定?他们不应该超时并继续处理吗?我在standalone.xml 文件中将超时设置为5 分钟,但它们似乎永远不会超时。

<session-bean>
<stateless>
   <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
</stateless>
<stateful default-access-timeout="5000" cache-ref="simple"/>
<singleton default-access-timeout="5000"/>
</session-bean>

有谁知道会发生什么?

我也很乐意接受任何关于如何模拟问题或其他设置 MDB 超时的方法的建议。

任何帮助,将不胜感激。

谢谢你的时间。

编辑:

因此,我终于能够通过简单地将 MessageListener 发送到比实例获取超时更长的时间来重现该问题。以下是测试代码:

<!-- standalone.xml -->
<strict-max-pool name="mdb-strict-max-pool" max-pool-size="5" instance-acquisition-timeout="30" instance-acquisition-timeout-unit="SECONDS"/>

多边开发银行:

@MessageDriven(name = "TestMDB", activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/myQueue"),
    @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")//,
    })
public class TestMDB implements MessageListener {

private final static Logger LOGGER = Logger.getLogger(TestMDB.class
        .toString());

@Resource 
private MessageDrivenContext ctx;

private final static int sleepingTime = MyProperties.SLEEP_MILLISECS; 

/**
 * @see MessageListener#onMessage(Message)
 */
public void onMessage(Message rcvMessage) {
    ObjectMessage msg = null;
    Future<String> future = null;
    MyResource res = null;

    try {
        if (rcvMessage instanceof ObjectMessage) {
            msg = (ObjectMessage) rcvMessage;
            res = (MyResource)msg.getObject();

            LOGGER.info("Received resource: " + res);
            Thread.sleep(sleepingTime);
            LOGGER.info("Released resource: " + res);
        } else {
            LOGGER.warning("Message of wrong type: "
                    + rcvMessage.getClass().getName());
        }
    } catch (JMSException e) {
        throw new RuntimeException(e);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
}
4

2 回答 2

3

似乎这种行为是意料之中的,而且问题可能与应用程序有关。

以下是问题的摘要:

  1. 我们有一个 MDB 实例;
  2. MDB 从队列 Q1 接收消息;
  3. 我们向队列 Q1 发送消息;
  4. MDB 尝试处理它,但由于我们模拟了一个比 instance-acquisition-timeout 花费更长的过程,HornetQ 超时并且我们得到异常:“未能在 X MINUTES 内获得许可”;
  5. 不幸的是,MDB 继续处理,只有在休眠时间之后才被释放
  6. 如果我们在 MDB 发布之前停止 JBoss,应用程序会继续它的处理,因为消息是持久的并且它知道我们没有完成 MDB#onMessage 方法;
  7. 但是由于我们还没有发现应用程序问题,锁再次发生,我们得到一个异常循环;

理想的解决方案是找出 MDB 被锁定的原因,但在那之前,由于应用程序已经投入生产,我们需要一个备用计划。

我相信一种解决方案是在我们达到 Hornet 的等待超时(或事务超时 - @ActivationConfigProperty(propertyName = "transactionTimeout", propertyValue = "30"))后中断 MDB 线程,但似乎并不容易配置(尚未)在 JBoss AS7 中执行此操作。

可悲的方法是计时 MDB 线程执行并在 onMessage 方法中强制中断。这可以通过 ExecutorService 轻松实现(http://stackoverflow.com/questions/6460664/how-can-i-interrupt-method-execution-by-time)

对于遇到同样问题的人,我在网上发现了一些类似的讨论:

http://bryanpendleton.blogspot.com.br/2009/05/timertask-exception-handling.html https://community.jboss.org/thread/162822

希望这可以帮助。

并希望我能检测到应用程序有什么问题。

于 2012-10-22T13:55:20.800 回答
0

我也遇到了这个问题,最后通过以下命令检查JBoss的线程找到了根本原因。

jstack $pid

我们发现了一个线程死锁。在我们解决了僵局之后,JBoss 不再停止。

于 2016-10-12T02:24:20.170 回答