4

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

4

6 回答 6

6

更好的方法看起来像这个示例(JavaEE6 JMS 样式),它从无状态 EJB中ObjectMessage向 a发送一个: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 中,您可能会遇到线程问题。

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

您还需要考虑其他细节/考虑因素,例如您可能需要的交易支持的级别和类型。或“毒药”的处理,但通过我的简单示例,我试图解决我在此处的许多答案中看到的一些基本问题。希望这可以帮助。

于 2014-10-15T17:49:55.140 回答
3

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

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

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

如果你这样做了,你会遇到一个额外的问题,即连接可能会失败。您将想知道您的缓存连接是否与服务器断开连接。自动重新连接功能效果不佳。特别是在可自动迁移的 JMS 服务器的情况下。

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

当 onError(method) 被调用时,你可以清除缓存。

于 2016-11-14T16:00:59.513 回答
2

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

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

于 2013-11-04T16:37:41.357 回答
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 方法来关闭连接。

于 2013-11-04T16:24:36.143 回答
2

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

@PostConstruct
private void onCreate() {
    // basically what you have in your present constructor
}

@PreDestroy
private void onDestroy() {
    // housecleaning goes here
}

编辑 - 我看到您在这里以“原始”方式使用 ActiveMQ,可能是因为它不是您的应用服务器的本机 JMS 组件。但这会导致非常低级的东西,例如 EJB 代码中的代理 URL。了解您为什么以这种方式做事可能会有所帮助,因为使用服务器的内置 JMS 基础架构应该会产生一个整体更好的解决方案,如果这当然可能的话。

于 2013-11-04T16:25:08.843 回答
2

https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions文章发布在 JBOSS 开发人员线程之一上。这清楚地解释了缓存连接和其他 JMS 相关资源作为 JMS 代码的反模式的原因,该代码在 JEE 应用程序服务器中运行。

简而言之,JCA 层池化 JMS 连接和 JMS 会话。因此,当您调用createConnection()or时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);
            }
        }
    }
于 2019-08-18T17:13:25.060 回答