我应该何时关闭在无状态会话bean中创建的JMS连接?

我有一个关于何时关闭在无状态会话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注入到调用类中。

更好的方法看起来像这个示例(JavaEE6 JMS样式),它ObjectMessage状态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中来遇到线程问题。

所有这些都是保持简单并在生产者方法中本地利用资源的原因。

您需要考虑其他细节/注意事项,例如您可能需要的事务支持的级别和类型。 或者处理“有毒信息”,但是我的简单例子是我试图解决一些基本问题,我在这里看到许多答案。 希望这可以帮助。

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方法来关闭连接。

Enterprise Beans有几种生命周期方法可用于此类活动:

 @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基础架构可以带来整体更好的解决方案,如果可能的话。

在会话bean中存储JMS工厂配置选项不是很好。 更好地存储在服务器级别,如下所述: https : //www.initworks.com/wiki/display/public/JMS+messages+from+EJBs+on+GlassFish

服务器可以有连接池,它的性能很好。 此外,服务器会在需要时自动关闭连接,并且您没有在您的代码中处理它。

我看到接受的答案是建议每次都创建一个新的连接,一个新的会话和一个新的生产者。 它还声明Oracle表示创建新连接非常便宜。 那不是真的。 这取决于所使用的应用程序服务器。

例如, Weblogic文档明确指出创建资源和会话的成本很高。 我不知道ApacheMQ是否做得更好。

我建议你只创建一个连接并保留它。 不要在EJB中保留引用,但要保留它。 在单例中,每个应用程序范围,会话范围,您可以在任何地方。

如果您这样做,则还有一个问题,即连接可能会失败。 您将想知道您的缓存连接是否与服务器断开连接。 自动重新连接function不起作用。 特别是在可自动迁移的JMS服务器的情况下。

您可以使用Connection.setExceptionListener(ExceptionListener)实现错误侦听器。

当调用onError(方法)时,您可以清除缓存。