我正在使用带有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之类的东西,比如符号,但是也没有用......)
塞巴斯蒂安
答案 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实例作为参数传递。