我有一个关于何时关闭在无状态会话bean(EJB)中创建的连接的一般性问题。连接是ActiveMQ,它们是在bean的构造函数中创建的。然后在方法中使用该连接,我想知道何时关闭此连接的适当时间/地点。
关闭连接的单独方法是否合适?是否必须由类使用bean调用?或者我应该简单地使用它关闭方法内的连接?我担心我可能会关闭连接然后重新使用该bean与现在关闭的连接,因为连接是在构造函数中打开的。以下是一些代码:
@Stateless
@LocalBean
public class SendEventsBean {
private static String brokerURL = ".......";
private static transient ConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private transient MessageProducer producer;
public SendEventsBean() {
factory = new ActiveMQConnectionFactory(brokerURL);
try {
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void sendEvent(String id, String description, String area) {
Destination destination;
try {
destination = session.createQueue("FWT." + "events");
TextMessage message = session.createTextMessage(id + " " + description + " " + area);
producer.send(destination, message);
} catch (JMSException e) {
e.printStackTrace();
}
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
}
如您所见,我目前有一个单独的close方法,应该在发送事件后由类使用bean调用。这是合法的还是要求麻烦?我对EJB缺乏经验,并对任何建议持开放态度。使用@EJB注释将bean注入到调用类中。
答案 0 :(得分:2)
JMS API资源是JMS API连接和JMS API会话。通常,在不再使用JMS资源时释放它们非常重要。以下是一些有用的实践。
如果您希望仅在业务方法的生命周期内维护JMS API资源,最好关闭方法中finally块中的资源。 如果您希望在企业bean实例的生命周期内维护JMS API资源,最好使用组件的ejbCreate方法来创建资源并使用组件的ejbRemove方法来关闭资源。如果使用有状态会话Bean或实体bean并且希望将JMS API资源维护在缓存状态,则必须在ejbPassivate方法中关闭该资源并将其值设置为null,并且必须在ejbActivate中再次创建它方法
如果您使用消息驱动bean的ejbCreate方法来创建JMS API连接,则通常使用ejbRemove方法来关闭连接。
答案 1 :(得分:2)
Enterprise Bean有几个Lifecycle methods可用于此类活动:
@PostConstruct
private void onCreate() {
// basically what you have in your present constructor
}
@PreDestroy
private void onDestroy() {
// housecleaning goes here
}
编辑 - 我发现你在这里以“原始”的方式使用ActiveMQ,可能是因为它不是你的appserver的原生JMS组件。但这导致了相当低级的东西,比如EJB代码中的代理URL。知道你为什么这样做可能会有所帮助,因为使用服务器的内置JMS基础设施应该会带来更好的解决方案,如果可能的话。
答案 2 :(得分:2)
在会话bean中存储JMS工厂配置选项不是很好。更好地存储在服务器级别,如下所述: https://www.initworks.com/wiki/display/public/JMS+messages+from+EJBs+on+GlassFish
服务器可以有连接池,它的性能很好。此外,服务器在需要时自动关闭连接,并且您没有在您的代码中处理它。
答案 3 :(得分:2)
更好的方法看起来像这个示例(JavaEE6 JMS样式),它从无状态EJB中向ObjectMessage
发送Queue
:
@Stateless
public class SendEventsBean {
private static final Logger log = Logger.getLogger(SendEventsBean.class);
@Resource(mappedName = "jms/MyConnectionFactory")
private ConnectionFactory jmsConnectionFactory;
@Resource(mappedName = "jms/myApp/MyQueue")
private Queue queue;
public void sendEvent() {
Connection jmsConnection = null;
try {
connection = jmsConnectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
MyObj obj = new MyObj(1, "Foo");
ObjectMessage myObjMsg = session.createObjectMessage(obj);
producer.send(myObjMsg);
} catch (JMSException jmxEx) {
log.error("Couldn't send JMS message: ", jmsEx);
}finally{
if (jmsConnection != null) {
try {
jmsConnection.close();
}catch(JMSException ex) {
log.warn("Couldn't close JMSConnection: ", ex);
}
}
}
}
}
JMS资源应由您的应用程序服务器管理(除非您需要使用动态资源),因此将向您的应用程序公开@Resource
注入。
Oracle不建议在无状态会话Bean中缓存JMS资源。创建JMSConnection对象非常便宜,因为它只是物理连接周围的薄包装器。
因此,可以不缓存它,或者在@PostConstruct
/ @PreDestroy
方法中创建和删除它。事实上,我已经测试了利用EJB生命周期方法的方法,但只会遇到问题。
Session和Producer对象也不是线程安全的,每个线程需要创建一次。您可以通过将它们作为属性存储/缓存在SLSB中来解决线程问题。
所有这些都是保持简单并在生产者方法中本地利用资源的理由。
您需要考虑其他细节/注意事项,例如您可能需要的事务支持的级别和类型。或者处理“毒药信息”,但是用我的简单例子,我试图解决一些基本问题,我在这里看到许多答案。希望这会有所帮助。
答案 4 :(得分:2)
我看到接受的答案是建议每次都创建一个新连接,一个新会话和一个新生成器。它还声明Oracle表示创建新连接非常便宜。那不是真的。这取决于所使用的应用程序服务器。
例如,Weblogic documentation明确指出创建资源和会话的成本很高。我不知道ApacheMQ是否做得更好。我建议您只创建一个连接并保留它。不要在EJB中保留引用,但要保留它。在单例中,每个应用程序范围,会话范围,您可以在任何地方。
如果您这样做,则还会遇到连接可能失败的其他问题。您将想知道您的缓存连接是否与服务器断开连接。自动重新连接功能不起作用。特别是在可自动迁移的JMS服务器的情况下。
您可以使用Connection.setExceptionListener(ExceptionListener)实现错误侦听器。
当调用onError(方法)时,您可以清除缓存。
答案 5 :(得分:2)
此https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions文章发布在JBOSS开发人员线程之一上。这清楚地说明了在JEE应用程序服务器中运行高速缓存连接和其他与JMS相关的资源的原因,这是JMS代码的反模式。
简而言之,JCA层池JMS连接和JMS会话。因此,当您调用createConnection()
或createSession()
时,在大多数情况下,它并不是真正地调用实际的JMS实现来实际创建新的JMS连接或JMS会话,它只是从其自己的内部缓存中返回一个。因此,创建连接和会话并不昂贵,因此只需按以下步骤关闭JMS连接和会话即可
public void sendToExchange(ExchangeMessage exchangeMessage) {
MessageProducer producer = null;
try {
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(targetQueue);
producer.setDisableMessageID(true);
Message message = beanRegistryCore.getJmsExchangeMsgTransformerI().transformToJMSMessage(session, exchangeMessage);
producer.send(message);
producer.setTimeToLive(ttl);//default 15min
logger.elkLog("78", "-1", LogEventsEnum.SENT_TO_EXCHANGE, exchangeMessage.toString());
} catch (Exception e) {
logger.error("LN:80", " Error when sending order to exchange:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.error("LN:87", "JMS producer close error:", e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.error("LN:93", "JMS session close error:", e);
}
try {
if (connection != null)
connection .close();
} catch (JMSException e) {
logger.error("LN:93", "JMS connection close error:", e);
}
}
}