0

我正在使用带有 Glassfish 的 Java Enterprise (3.1)。我有两个单独的 EAR,它们通过 JMS 同步通信。进一步来说:

EAR1 使用 JMS 消息来告诉 EAR2 做什么。EAR1 开始侦听来自 EAR2 (QueueReceiver.receive) 的回答。EAR2 接收到消息并进行相应的处理,然后将 JMS 消息连同输出一起发送回 EAR1。

这一切都很好。直到我得到这个例外:

[#|2011-05-10T15:05:27.382+0200|WARNING|glassfish3.1|javax.enterprise.resource.resourceadapter.com.sun.enterprise.connectors|_ThreadID=90;_ThreadName=Thread-1;|RAR5117 : Failed to obtain/create connection from connection pool [ jms/QueueConnectionFactory ]. Reason : com.sun.appserv.connectors.internal.api.PoolingException: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.|#]

所以看起来容器没有重用 MDB。相反,它会创建新的,直到我达到极限。我知道这是因为 EAR2 中的 MDB 使用 JMS 发回结果。我的猜测是,MDB 实例中仍然分配了一些资源,这会导致这种行为。

如果我只是使用 MDB 打印收到的消息,我可以整天继续发送消息,所以它肯定与 JMS 连接有关。

我已经在这两天了,所以如果有人愿意提供一些帮助,将不胜感激。

此代码整天有效:

package xxx;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName="destinationType",
        propertyValue="javax.jms.Queue")
}, mappedName = "AssociationQueue1")
public class AssociationMDB implements MessageListener {

    @Override
    public void onMessage(Message arg0) {
        MapMessage msg = (MapMessage)arg0;
        String source = null;
        String target = null;
        try {
            source = msg.getString("source");
             target = msg.getString("target");
        } catch (JMSException e) {
            e.printStackTrace();
        }       
        System.out.println(source + " " + target);
    }
}

虽然这个没有:

package xxx;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName="destinationType",
        propertyValue="javax.jms.Queue")
}, mappedName = "AssociationQueue1")
public class AssociationMDB implements MessageListener {

    @Override
    public void onMessage(Message arg0) {
        Logger logger = Logger.getLogger(this.getClass().getSimpleName());
        QueueConnection qConnect = null;
        QueueSession qSession = null;
        QueueSender qSender = null;
        try {
            InitialContext context = new InitialContext();
            Queue responseQ = (Queue)context.lookup("AssociationQueue2");
            QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup("jms/QueueConnectionFactory");
            qConnect = factory.createQueueConnection();
            qSession = qConnect.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
            qConnect.start();
            qSender = qSession.createSender(responseQ);

            TextMessage answer = qSession.createTextMessage();
            answer.setText("hey");
            qSender.send(answer);
            logger.info("message sent");
        }
        catch (JMSException jmse) {
            jmse.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if(qSender != null) {
                    qSender.close();
                    logger.info("cleaning qSender");
                }
                if(qSession != null) {
                    qSession.close();
                    logger.info("cleaning qSession");
                }
                if(qConnect != null) {
                    qConnect.close();
                    logger.info("cleaning qConnect");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }   
        }

    } 

(我也尝试过使用更新更花哨的 EJB 东西,比如符号等,但也没有用......)

塞巴斯蒂安

4

2 回答 2

2

模式本身似乎没有问题。肯定允许 MDB 将消息发布到另一个队列。这是对某事的答复还是只是委派工作与手头的问题无关。

似乎有问题的是你打电话给qConnect.start(). 这准备连接以侦听入站流量。因此,对于发送消息,这不是必需的。您也不必明确关闭会话和发件人。尽管您确实在 finally 块中关闭了连接,但如果上面的任何代码抛出,它很容易受到连接泄漏的影响。

顺便说一句,您“不重用 MDB”的措辞也不完全正确。我认为您的意思是“不重用连接”?

您似乎也在同一个 Glassfish 实例上运行两个 EAR。这意味着 EAR1 使用与 EAR2 相同的连接池,因此连接泄漏也可能是由调用QueueReceiver.receive.

最后请注意,在 Java EE 中,特别是 EJB* 中实际上根本不允许直接监听入站流量的 JMS 连接。这是 JMS API 独立运行并在 Java EE 产品中使用时表现不同的令人讨厌的部分之一。有些服务器(例如 JBoss AS)有不同的连接工厂,有些可以用来监听,有些不能。我不知道 Glassfish 的确切细节,但是您启动连接以静默收听这一事实违反了规范,并且可能是您问题的主要原因。

*) 是否仅涉及 EJB 或 Servlet 还有一些不确定性。例如,JBoss AS 6 禁止 EJB 和 Servlet 使用兼容 Java EE 的 java:/JmsXA 来监听连接,但 Weblogic 8 只允许 EJB 而不是 Servlet。

于 2011-05-22T16:34:33.630 回答
0

Arjan 给出了很好的解释。感谢那。我只是想补充一点。

就我而言,我通过使用 @Resource 而不是上下文查找来获取连接工厂,我想这非常相似。现在重要的是要注意 QueueConnection.close() 是由容器处理的。因此,触发关闭并不一定意味着您的连接在此调用被触发时关闭。在所有@Resource 上记住这一点很重要。在创建 MDB 时,资源将通过 DI 进行初始化。在销毁 MDB 时,它们将再次被释放。这是为了提交和回滚的目的。

因此,如果您可能从 MDB 会话中发送多条消息,这些连接将仅在您的 MDB 会话完成后被释放。是的,我知道,通常一个请求 = 一个响应,但在我的情况下,我收到一个 CSV 作为请求,以生成多个 XML 消息并将每一行路由到一个队列,如果你明白的话。因此,一般来说,从您的 sendMessage 方法中调用 ConnectionFactory.createQueueConnection 可能是一个冒险的想法。而是将 QueueConnection 实例作为参数传递。

于 2013-09-25T11:07:00.173 回答