之前的连接工厂中,JmsTemlate每次发送消息时都会重新创建connection、session、productor等对象,从而导致性能的下降,是否有类似pool的连接池协助我们提高性能呢?答案必然是肯定的。
本章概要
1、缓冲连接工厂对比选择;
2、源码分析;
3、CachingConnectionFactory连接工厂的实现;
4、探索:ActiveMQXAConnectionFactory;
缓冲连接工厂对比选择
1、ActiveMQ自身即有相关的实现PooledConnectionFactory,其只会缓存connection,session和productor,不会缓存consumer,故其适用于生产者。
2、而spring基于JMS标准进行了连接池的实现,故其不仅仅适用于ActiveMQ,并且还提供了对consumer的缓存实现。Spring定义了CachingConnectionFactory和SingleConnectionFactory,其均有缓存功能,但考虑到CachingConnectionFactory类扩展自SingleConnectionFactory,其有了更多的实现。
小结:故本章将通过CachingConnectionFactory实现带有缓冲池的连接工厂。
源码分析
1、从ActiveMQAutoConfiguration入手
再看ActiveMQConnectionFactoryConfiguration
以及ActiveMQXAConnectionFactoryConfiguration
其中均有条件注解@ConditionalOnMissingBean({ ConnectionFactory.class }),而此时我们正需要注册自定义CachingConnectionFactory,故一旦我们注册了系统默认的装配的ConnectionFactory失效。
2、先了解下即将要注册的CachingConnectionFactory,
又或者
均需要设置其真正生成连接的ConnectionFactory,考虑到1中默认的装载已经失效,我们需要自己实现ConnectionFactory的注册。
3、在ActiveMQConnectionFactoryConfiguration中可以找到
创建我们的ConnectionFactory,考虑到1.1中的描述其注解配置不会生效,故准备直接复制一个一样的ActiveMQConnectionFactoryConfiguration来定义实现,但确发现 ActiveMQConnectionFactoryFactory类是没有公开的,无法被我自定义的包路径所识别应用,故只能从ActiveMQConnectionFactoryFactory自定义实现开始了。
4、当然我们也可以很简单的直接在定义CachingConnectionFactory时直接new一个ActiveMQConnectionFactory实例。以上2、3的分析主要是结合springboot实现机制同步实现,便于更好的理解。
CachingConnectionFactory连接工厂的实现
1、创建ActiveMQConnectionFactory的工厂类,主要负责创建连接:
package com.shf.activemq.config;
import java.lang.reflect.InvocationTargetException;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* 创建ActiveMQConnectionFactory的工厂类
* @author song
*
*/
public class ActiveMQConnectionFactoryFactory {
private static final String DEFAULT_EMBEDDED_BROKER_URL = "vm://localhost?broker.persistent=false";
private static final String DEFAULT_NETWORK_BROKER_URL = "tcp://localhost:61616";
private final ActiveMQProperties properties;
ActiveMQConnectionFactoryFactory(ActiveMQProperties properties) {
Assert.notNull(properties, "Properties must not be null");
this.properties = properties;
}
public <T extends ActiveMQConnectionFactory> T createConnectionFactory(Class<T> factoryClass) {
try {
return doCreateConnectionFactory(factoryClass);
} catch (Exception ex) {
throw new IllegalStateException("Unable to create ActiveMQConnectionFactory", ex);
}
}
private <T extends ActiveMQConnectionFactory> T doCreateConnectionFactory(Class<T> factoryClass) throws Exception {
ActiveMQConnectionFactory factory = createConnectionFactoryInstance(factoryClass);
ActiveMQProperties.Packages packages = this.properties.getPackages();
if (packages.getTrustAll() != null) {
factory.setTrustAllPackages(packages.getTrustAll().booleanValue());
}
if (!(packages.getTrusted().isEmpty())) {
factory.setTrustedPackages(packages.getTrusted());
}
return (T) factory;
}
private <T extends ActiveMQConnectionFactory> T createConnectionFactoryInstance(Class<T> factoryClass)
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
String brokerUrl = determineBrokerUrl();
String user = this.properties.getUser();
String password = this.properties.getPassword();
if ((StringUtils.hasLength(user)) && (StringUtils.hasLength(password))) {
return (T) ((ActiveMQConnectionFactory) factoryClass
.getConstructor(new Class[] { String.class, String.class, String.class })
.newInstance(new Object[] { user, password, brokerUrl }));
}
return (T) ((ActiveMQConnectionFactory) factoryClass.getConstructor(new Class[] { String.class })
.newInstance(new Object[] { brokerUrl }));
}
String determineBrokerUrl() {
if (this.properties.getBrokerUrl() != null) {
return this.properties.getBrokerUrl();
}
if (this.properties.isInMemory()) {
return "vm://localhost?broker.persistent=false";
}
return "tcp://localhost:61616";
}
}
2、配置注册连接工厂:
package com.shf.activemq.config;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQXAConnectionFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置注册连接工厂
* @author song
*
*/
@Configuration
public class ActiveMQXAConnectionFactoryConfiguration {
/**
* 创建普通连接工厂
* @param properties
* @return
*/
@Bean(name = "jmsConnectionFactory")
public ActiveMQConnectionFactory nonXaJmsConnectionFactory(ActiveMQProperties properties) {
return new ActiveMQConnectionFactoryFactory(properties)
.createConnectionFactory(ActiveMQConnectionFactory.class);
}
}
3、注册带有session缓冲的连接工厂
package com.shf.activemq.config;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.connection.CachingConnectionFactory;
/**
* 定义带有session缓冲的连接工厂
* @author song
*
*/
@Configuration
@EnableConfigurationProperties({ ActiveMQProperties.class })
public class ActiveMQConnectionFactoryConfiguration {
/**
* 注入MQ连接工厂
*/
@Autowired
@Qualifier(value="jmsConnectionFactory")
private ConnectionFactory connectionFactory;
/**
* 创建带有缓冲session的连接工厂
* @return
*/
@Bean(name="cachingConnectionFactory")
@Primary
public CachingConnectionFactory getConnectionFactory(){
CachingConnectionFactory cachingConnectionFactory=new CachingConnectionFactory();
//目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory
cachingConnectionFactory.setTargetConnectionFactory(connectionFactory);
//Session缓存数量,这里属性也可以直接在这里配置
cachingConnectionFactory.setSessionCacheSize(10);
return cachingConnectionFactory;
}
}
4、优化JmsTemplate注册,其将应用3中定义的CachingConnectionFactory :
package com.shf.activemq.config;
import javax.jms.DeliveryMode;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
/**
* 自定义JmsTemplate,支持事务
* @author song
*
*/
@Configuration
@DependsOn(value="cachingConnectionFactory")
public class JmsTemplateConfiguration {
private final ObjectProvider<DestinationResolver> destinationResolver;
private final ObjectProvider<MessageConverter> messageConverter;
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
public JmsTemplateConfiguration(ObjectProvider<DestinationResolver> destinationResolver,
ObjectProvider<MessageConverter> messageConverter) {
this.destinationResolver = destinationResolver;
this.messageConverter = messageConverter;
}
/**
* 配置队列生产者的JmsTemplate
* @param connectionFactory
* @return
*/
@Bean(name="jmsQueueTemplate")
@Primary
public JmsTemplate jmsQueueTemplate() {
//设置创建连接的工厂
// JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
//优化连接工厂,这里应用缓存池 连接工厂就即可
JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
//设置P2P队列消息类型
jmsTemplate.setPubSubDomain(false);
DestinationResolver destinationResolver = (DestinationResolver) this.destinationResolver.getIfUnique();
if (destinationResolver != null) {
jmsTemplate.setDestinationResolver(destinationResolver);
}
MessageConverter messageConverter = (MessageConverter) this.messageConverter.getIfUnique();
if (messageConverter != null) {
jmsTemplate.setMessageConverter(messageConverter);
}
//deliveryMode, priority, timeToLive 的开关,要生效,必须配置为true,默认false
jmsTemplate.setExplicitQosEnabled(true);
//DeliveryMode.NON_PERSISTENT=1:非持久 ; DeliveryMode.PERSISTENT=2:持久
jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
//默认不开启事务
System.out.println("默认是否开启事务:"+jmsTemplate.isSessionTransacted());
//如果不启用事务,则会导致XA事务失效;
//作为生产者如果需要支持事务,则需要配置SessionTransacted为true
jmsTemplate.setSessionTransacted(true);
return jmsTemplate;
}
/**
* 配置发布订阅生产者的JmsTemplate
* @param connectionFactory
* @return
*/
@Bean(name="jmsTopicTemplate")
public JmsTemplate jmsTopicTemplate() {
//设置创建连接的工厂
// JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
//优化连接工厂,这里应用缓存池 连接工厂就即可
JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
//设置发布订阅消息类型
jmsTemplate.setPubSubDomain(true);
DestinationResolver destinationResolver = (DestinationResolver) this.destinationResolver.getIfUnique();
if (destinationResolver != null) {
jmsTemplate.setDestinationResolver(destinationResolver);
}
MessageConverter messageConverter = (MessageConverter) this.messageConverter.getIfUnique();
if (messageConverter != null) {
jmsTemplate.setMessageConverter(messageConverter);
}
//deliveryMode, priority, timeToLive 的开关,要生效,必须配置为true,默认false
jmsTemplate.setExplicitQosEnabled(true);
//DeliveryMode.NON_PERSISTENT=1:非持久 ; DeliveryMode.PERSISTENT=2:持久
jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
//默认不开启事务
System.out.println("默认是否开启事务:"+jmsTemplate.isSessionTransacted());
//如果不启用事务,则会导致XA事务失效;
//作为生产者如果需要支持事务,则需要配置SessionTransacted为true
jmsTemplate.setSessionTransacted(true);
return jmsTemplate;
}
}
5、通过之前章节中的单元测试代码验证, 不仅CachingConnectionFactory 连接工厂配置成功,其事务仍然生效。
探索
在源码中我们看到了ActiveMQXAConnectionFactoryConfiguration中不仅有常规普通的连接工厂,还有ActiveMQXAConnectionFactory工厂注册,其标注了
需要注入XAConnectionFactoryWrapper的实例,但其实源码中并没有注解配置XAConnectionFactoryWrapper的bean实例,如果需要应用ActiveMQXAConnectionFactory我们需要优先注册XAConnectionFactoryWrapper,本案例采用AtomikosXAConnectionFactoryWrapper 。
1、注册AtomikosXAConnectionFactoryWrapper 实例:
package com.shf.activemq.config;
import org.springframework.boot.jta.atomikos.AtomikosXAConnectionFactoryWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置AtomikosXAConnectionFactoryWrapper的bean实例,否则无法创建ActiveMQXAConnectionFactory工厂
* @author song
*
*/
@Configuration
public class AtomikosXAConnectionFactoryWrapperConfiguration {
@Bean
public AtomikosXAConnectionFactoryWrapper atomikosXAConnectionFactoryWrapper(){
return new AtomikosXAConnectionFactoryWrapper();
}
}
2、调整上述的ActiveMQXAConnectionFactoryConfiguration,新增一个ActiveMQXAConnectionFactory的bean注册:
package com.shf.activemq.config;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQXAConnectionFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 配置注册连接工厂
* @author song
*
*/
@Configuration
public class ActiveMQXAConnectionFactoryConfiguration {
/**
* 创建XA连接工厂
* @param properties
* @param wrapper
* @return
* @throws Exception
*/
@Bean(name = "xaJmsConnectionFactory")
public ConnectionFactory jmsConnectionFactory(ActiveMQProperties properties, XAConnectionFactoryWrapper wrapper)
throws Exception {
ActiveMQXAConnectionFactory connectionFactory = (ActiveMQXAConnectionFactory) new ActiveMQConnectionFactoryFactory(
properties).createConnectionFactory(ActiveMQXAConnectionFactory.class);
return wrapper.wrapConnectionFactory(connectionFactory);
}
/**
* 创建普通连接工厂
* @param properties
* @return
*/
@Bean(name = "jmsConnectionFactory")
public ActiveMQConnectionFactory nonXaJmsConnectionFactory(ActiveMQProperties properties) {
return new ActiveMQConnectionFactoryFactory(properties)
.createConnectionFactory(ActiveMQConnectionFactory.class);
}
}
3、调整配置CachingConnectionFactory 中注入的ConnectionFactory 实例:
package com.shf.activemq.config;
import javax.jms.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.connection.CachingConnectionFactory;
/**
* 定义带有session缓冲的连接工厂
* @author song
*
*/
@Configuration
@EnableConfigurationProperties({ ActiveMQProperties.class })
public class ActiveMQConnectionFactoryConfiguration {
/**
* 注入MQ连接工厂
*/
@Autowired
// @Qualifier(value="jmsConnectionFactory")//虽然不是ActiveMQXAConnectionFactory,但在JTA事务管理器的事务处理下能够很好的实现分布式事务
@Qualifier(value="xaJmsConnectionFactory")//在JTA事务管理器的事务处理下能够很好的实现分布式事务
private ConnectionFactory connectionFactory;
/**
* 创建带有缓冲session的连接工厂
* @return
*/
@Bean(name="cachingConnectionFactory")
@Primary
public CachingConnectionFactory getConnectionFactory(){
CachingConnectionFactory cachingConnectionFactory=new CachingConnectionFactory();
//目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory
cachingConnectionFactory.setTargetConnectionFactory(connectionFactory);
//Session缓存数量,这里属性也可以直接在这里配置
cachingConnectionFactory.setSessionCacheSize(10);
return cachingConnectionFactory;
}
}
4、通过之前章节中的单元测试代码验证, 不仅CachingConnectionFactory 连接工厂配置成功,其事务仍然生效。
小结:不管注入使用的是
ActiveMQXAConnectionFactory还是
ActiveMQConnectionFactory,其应用内的分布式事务均能生效。