优化生产者连接工厂(带有session缓存)

之前的连接工厂中,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,其应用内的分布式事务均能生效。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值