Spring 的 CachingConnectionFactory。为什么我们需要关闭会话,尽管它们要被缓存?

我正试图了解 Spring CachingConnectionFactory

ActiveMQ documentation 建议在使用 JmsTemplate 时使用 Spring CachingConnectionFactory 或 ActiveMQ PooledConnectionFactory 作为连接工厂实现。

我理解这一点,因为使用普通的 ConnectionFactory 创建了一个连接,启动了一个会话,并且对于 jmsTemplate.send() 的每次调用都关闭了两者,这是非常浪费的。

因此,我正在尝试使用 JmsTemplate 实现自定义 CachingConnectionFactory bean,以便在我可能有许多请求的情况下使用 A) 持久化到 DB B) 排队 JMS。

@Configuration
public class JMSConfig {

    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    @Bean
    @Qualifier("jmsTemplateCached")
    public JmsTemplate jmsTemplateCachingConnectionFactory() {
        cachingConnectionFactory.setSessionCacheSize(10);

        JmsTemplate jmsTemplate = new JmsTemplate();
        jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
        jmsTemplate.setSessionAcknowledgeMode(JmsProperties.AcknowledgeMode.CLIENT.getMode());
        jmsTemplate.setSessionTransacted(true);
        jmsTemplate.setDeliveryPersistent(true);
        jmsTemplate.setConnectionFactory(cachingConnectionFactory);
        return jmsTemplate;
    }
}

我的第一个问题是关于 Spring Docs For CachngConnecionFactory 的,它说:

SingleConnectionFactory 子类,添加了 Session 缓存和 MessageProducer 缓存。默认情况下,此 ConnectionFactory 还将“reconnectOnException”属性切换为“true”,从而允许自动恢复底层 Connection。默认情况下,只会缓存一个 Session,并根据需要创建和处理更多请求的 Session。考虑在高并发环境的情况下提高“sessionCacheSize”值。

但随后以粗体显示:

注意:此 ConnectionFactory 需要显式关闭从其共享连接中获得的所有会话。无论如何,这是本机 JMS 访问代码的通常建议。但是,对于这个 ConnectionFactory,它的使用是强制性的,以便实际允许 Session 重用。

这是否意味着如果我通过模板或我的 CachingConnectionFactory bean“手动”创建连接,我只需要关闭会话?换句话说,例如:

    Connection connection = jmsTemplateCached.getConnectionFactory().createConnection();

    Session sess = connection.createSession(true, JmsProperties.AcknowledgeMode.CLIENT.getMode());

    MessageProducer producer = sess.createProducer(activeMQQueue);

    try {
        producer.send(activeMQQueue, new ActiveMQTextMessage());
        sess.commit();
    } catch (JMSException e) {
        sess.rollback();
    } finally {
        sess.close();
    }

如果我使用下面的模板,我应该关闭还是不关闭会话?

    @Autowired
    public JmsTemplate jmsTemplateCached;

    @Transactional
    public InboundResponse peristAndEnqueueForProcessing(InboundForm  inboundForm) throws IrresolvableException, JsonProcessingException, JMSException {

    //Removed for clarity, an entity has been persisted and is then to be enqueued via JMS.

        log.debug("Queue For Processing : {}", persistedRequest);

        String serialisedMessage = objectMapper.writeValueAsString(persistedRequest);
        ActiveMQTextMessage activeMQTextMessage = new ActiveMQTextMessage();
        activeMQTextMessage.setText(serialisedMessage);

        //Will throw JMS Exception on failure
        Session sessionUsed = jmsTemplateCached.execute((session, messageProducer) -> {
            messageProducer.send(activeMQQueue, activeMQTextMessage);
            session.commit();
            return session;
        });

        return response;
    }

其次,如果上面的 jmsTemplate.execute() 抛出异常,那么 session 会发生什么?它会在 x 时间后回滚吗?

stack overflow Spring's CachingConnectionFactory. Why do we need to close sessions although they are to be cached?
原文答案
author avatar

接受的答案

JmsTemplate 在每次操作后可靠地关闭其资源(将会话返回到缓存),包括 execute()

该注释与直接使用会话的用户代码相关;关闭操作被拦截并用于将会话返回到缓存,而不是实际关闭它。您必须调用 close,否则会话将被孤立。

是的,如果它的 sessionTransacted 为真,事务将(立即)回滚。

你不应该调用 commit - 当执行正常退出时模板会这样做(如果它是 sessionTransacted )。


答案: