同步MDB通信,max-pool-size问题

时间:2011-05-10 17:46:19

标签: java glassfish ejb-3.1 message-driven-bean

我正在使用带有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之类的东西,比如符号,但是也没有用......)

塞巴斯蒂安

2 个答案:

答案 0 :(得分:2)

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

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

您的“不重复使用MDB”的措辞也不完全正确。我认为你的意思是“不重用连接”?

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

最后请注意,在Java EE中实际上根本不允许直接侦听入站流量的JMS连接,尤其是EJB *。这是一个令人烦恼的部分,其中JMS API独立运行并在Java EE产品中使用。有些服务器(例如JBoss AS)有不同的连接工厂,其中一些可用于监听,有些则不能。我不知道Glassfish的具体细节,但是您开始静默监听连接的事实违反了规范,可能是您问题的主要根源。

*) 还有一些不明确的问题,它是关于EJB还是Servlet。例如。 JBoss AS 6禁止EJB和Servlet使用符合Java EE的java:/ JmsXA监听连接,但仅使用Weblogic 8而不是Servlet。

答案 1 :(得分:0)

阿尔詹给出了很好的解释。感谢那。我想补充一些东西。

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

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