ActiveMQ使用连接池实现消息的生产和消费
使用背景:
应用ActiveMQ的消息队列特性来替换Timer框架的定时任务功能。 Timer定时器是单线程的,很容易出现一个任务出现异常,其余任务全部停止的问题,这就是线程阻塞问题。当然你可以使用线程池的方式实现多线程任务并发执行,但若是应用中定时任务多且逻辑复杂还要考虑内存资源的问题。 所以综合参考网上的众多资源,编写了基于ActiveMQ的连接池实现消息的产生和消费,在此作为记录。
环境搭建
在MQ的官网上下载相关版本的ActiveMQ,需要依据自己环境的jdk版本和Spring版本。本人使用的是Spring3.2,使用的ActiveMQ版本是5.9,且引入相关jar包,见截图:
代码实现
连接池管理类:
public class MQPoolFactory{
private static final String USERNAME="admin";
private static final String PASSWORD="admin";
private static final String BROKERURL="tcp://127.0.0.1:61616";
private static final int SESSIONCACHESIZE = 20;//session缓存大小设置
private static final int INIT_CONNECTION_SIZE = 1;//初始化连接数
private static final int MAX_CONNECTION_SIZE = 5;//最大连接数
private static final int MAX_SESSION_SIZE = 100;//每个连接可创建的最大session数
private static final int MIN_SESSION_SIZE = 10;//每个连接可创建的最小session数
private static final int MAX_PRODUCER_SESSION = 10;//每个session可建的最大producer数
private static final int MAX_CONSUMER_SESSION = 10;//每个session可建的最大consumer数
private LinkedList<ConnectionPool> connectionPool = new LinkedList<ConnectionPool>();//存放空闲连接的链表
private LinkedList<SessionPool> sessionPools = new LinkedList<SessionPool>();
/**
* 缺省设置,默认加载MQ工厂初始化方法
* @throws Exception
*/
public MQPoolFactory() throws Exception{
initMQFactory();
}
/**
* 初始化MQ
*/
public void initMQFactory() throws Exception{
if(INIT_CONNECTION_SIZE > 0 && INIT_CONNECTION_SIZE <= MAX_CONNECTION_SIZE) {
try {
for(int i = 0;i<INIT_CONNECTION_SIZE;i++) {
Connection connection = buildConnection();
ConnectionPool connPool = new ConnectionPool();
connPool.setConnection(connection);
if(MIN_SESSION_SIZE > 0 && MIN_SESSION_SIZE <= MAX_SESSION_SIZE) {
connPool.setActiveSessions(MIN_SESSION_SIZE);
for (int j = 0; j < MIN_SESSION_SIZE; j++) {
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//默认设置MQ自动检测消息是否送达
SessionPool sessionPool = new SessionPool();
sessionPool.setConnection(connection);
sessionPool.setSession(session);
sessionPools.addLast(sessionPool);
}
}
connectionPool.add(connPool);
}
}catch(JMSException e) {
throw new Exception("MQ初始化异常", e);
}
}
}
/**
* 构建方法
* @throws JMSException
*/
private synchronized Connection buildConnection() throws JMSException {
MqConfigBean bean = new MqConfigBean(BROKERURL, USERNAME, PASSWORD, SESSIONCACHESIZE);
ActiveMQConnectionFactory targetFactory = new ActiveMQConnectionFactory(bean.getUserName(),bean.getPassword(),bean.getBrokerURL());
//targetFactory.setUseAsyncSend(true);//强制使用同步返回数据格式
CachingConnectionFactory connectoryFacotry = new CachingConnectionFactory();
connectoryFacotry.setTargetConnectionFactory(targetFactory);
connectoryFacotry.setSessionCacheSize(bean.getSessionCacheSize());//设置缓存大小,优化性能
Connection connection = connectoryFacotry.createConnection();
connection.start();//开启连接
return connection;
}
/**
* 从连接池中获取连接
* @throws Exception
*/
private synchronized ConnectionPool getConnectionPool() throws Exception {
ConnectionPool connPool = null;
if(connectionPool != null && connectionPool.size() > 0) {
for (ConnectionPool connectionPool : connectionPool) {
int poolSessionSize = connectionPool.getActiveSessions();//获取该连接中session活跃的会话数量
if (poolSessionSize < MAX_SESSION_SIZE) {//取相对会话比较少的连接
connPool = connectionPool;
}
}
//若当前连接池中的连接全部处于使用状态,则在不超过最大连接数的前提下添加新的连接
if(connPool == null && connectionPool.size() < MAX_CONNECTION_SIZE) {
try {
Connection connection = buildConnection();
connPool = new ConnectionPool();
connPool.setConnection(connection);
if(MIN_SESSION_SIZE > 0 && MIN_SESSION_SIZE <= MAX_SESSION_SIZE) {
connPool.setActiveSessions(MIN_SESSION_SIZE);
for (int j = 0; j < MIN_SESSION_SIZE; j++) {
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//默认设置MQ自动检测消息是否送达
SessionPool sessionPool = new SessionPool();
sessionPool.setConnection(connection);
sessionPool.setSession(session);
sessionPools.addLast(sessionPool);
}
}
connectionPool.add(connPool);
}catch(JMSException e) {
throw new Exception("getConnection方法创建Connection异常",e);
}
}
}
return connPool;
}
/**
* 获取生产者的session信息
*/
private SessionPool getProducerSessionPool() {
SessionPool sesPool = null;
if(sessionPools != null && sessionPools.size() > 0) {
try {
ConnectionPool connPool = getConnectionPool();
for(SessionPool pool : sessionPools) {
if(pool.getConnection() == connPool.getConnection()) {
int poolProducerSize = pool.getAvailableProducer();//获取当前session中已存在的生产者的数量
if(poolProducerSize < MAX_PRODUCER_SESSION) {//当前session中生产者较少的优先使用
sesPool = pool;
}
}
}
//当前session资源被占满,则重新创建一个新的session
if(sesPool == null && connPool.getActiveSessions() < MAX_SESSION_SIZE) {
try {
Connection conn = connPool.getConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
sesPool = new SessionPool();
sesPool.setConnection(conn);
sesPool.setSession(session);
sessionPools.addLast(sesPool);
}catch(Exception e) {
throw new Exception("getProducerSession方法创建Session异常",e);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
return sesPool;
}
/**
* 获取消费者的session信息
*/
private SessionPool getConsumerSessionPool() {
SessionPool sesPool = null;
if(sessionPools != null && sessionPools.size() > 0) {
try {
ConnectionPool connPool = getConnectionPool();
for(SessionPool pool : sessionPools) {
if(pool.getConnection() == connPool.getConnection()) {
int poolConsumerSize = pool.getAvailableConsumer();//获取当前session中已存在的生产者的数量
if(poolConsumerSize < MAX_CONSUMER_SESSION) {//当前session中消费者较少的优先使用
sesPool = pool;
}
}
}
//当前session资源被占满,则重新创建一个新的session
if(sesPool == null && connPool.getActiveSessions() < MAX_SESSION_SIZE) {
try {
Connection conn = connPool.getConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
sesPool = new SessionPool();
sesPool.setConnection(conn);
sesPool.setSession(session);
sessionPools.addLast(sesPool);
}catch(Exception e) {
throw new Exception("getProducerSession方法创建Session异常",e);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
return sesPool;
}
/**
* 获取生产者的连接信息
* @param name 消息队列名称 messageType p2p or topic
* @param isVIP 可扩展 若为true则代表 当前session中无消费者和生产者
* @throws Exception
*/
public ProducerPool getProducerPool(String name,String messageType) throws Exception{
SessionPool sessionPool = getProducerSessionPool();
Session session = sessionPool.getSession();
try {
Destination dt = null;
if(messageType.equals(MessageTypeEnum.TOPIC.getTypeName())) {
dt = session.createTopic(name);
}else if(messageType.equals(MessageTypeEnum.QUEUE.getTypeName())) {
dt = session.createQueue(name);
}
MessageProducer producer = session.createProducer(dt);
ProducerPool producerPool = new ProducerPool();
producerPool.setProducer(producer);
producerPool.setConnection(sessionPool.getConnection());
producerPool.setSession(session);
producerIncreament(sessionPool);
return producerPool;
}catch(Exception e) {
throw new Exception("获取生产者连接异常",e);
}
}
/**
* 获取消费者的连接信息
* @param name 消息队列名称 messageType p2p or topic
* @param isVIP 可扩展 若为true则代表 当前session中无消费者和生产者
* @throws Exception
*/
public ConsumerPool getConsumerPool(String name,String messageType) throws Exception{
SessionPool sessionPool = getConsumerSessionPool();
Session session = sessionPool.getSession();
try {
Destination dt = null;
if(messageType.equals(MessageTypeEnum.TOPIC.getTypeName())) {
dt = session.createTopic(name);
}else if(messageType.equals(MessageTypeEnum.QUEUE.getTypeName())) {
dt = session.createQueue(name);
}
MessageConsumer consumer = session.createConsumer(dt);
ConsumerPool consumerPool = new ConsumerPool();
consumerPool.setConsumer(consumer);
consumerPool.setConnection(sessionPool.getConnection());
consumerPool.setSession(session);
consumerIncreament(sessionPool);
return consumerPool;
}catch(Exception e) {
throw new Exception("获取消费者连接异常",e);
}
}
/**
* 连接池中可用生产者加一
* @param sessionPool
*/
private void producerIncreament(SessionPool sessionPool){
if(sessionPool!=null){
for(SessionPool sePool : sessionPools){
if(sePool==sessionPool){
int cnt = sePool.getAvailableProducer();
cnt++;
sePool.setAvailableProducer(cnt);
}
}
}
}
/**
* 连接池中可用生产者减一
* @param producerPool
*/
public void producerDecreament(ProducerPool producerPool){
if(producerPool!=null){
for(SessionPool sessionPool : sessionPools){
if(sessionPool.getConnection()==producerPool.getConnection()
&& sessionPool.getSession()==producerPool.getSession()){
int cnt = sessionPool.getAvailableProducer();
cnt--;
sessionPool.setAvailableProducer(cnt);
}
}
}
}
/**
* 连接池中可用消费者加一
* @param sessionPool
*/
private void consumerIncreament(SessionPool sessionPool){
if(sessionPool!=null){
for(SessionPool sePool : sessionPools){
if(sePool==sessionPool){
int cnt = sePool.getAvailableConsumer();
cnt++;
sePool.setAvailableConsumer(cnt);
}
}
}
}
/**
* 连接池中可用消费者减一
* @param consumerPool
*/
public void consumerDecreament(ConsumerPool consumerPool){
if(consumerPool!=null){
for(SessionPool sessionPool : sessionPools){
if(sessionPool.getConnection()==consumerPool.getConnection()
&& sessionPool.getSession()==consumerPool.getSession()){
int cnt = sessionPool.getAvailableConsumer();
cnt--;
sessionPool.setAvailableConsumer(cnt);
}
}
}
}
/**
* 释放所有连接
* @return
* @throws AMQFactoryException
*/
public boolean disposeAll() throws Exception{
try {
if(sessionPools!=null && sessionPools.size()>0){
for (SessionPool sessionPool : sessionPools) {
sessionPool.getSession().close();
}
sessionPools.clear();
}
if(connectionPool!=null && connectionPool.size()>0){
for(ConnectionPool connectionPool : connectionPool){
connectionPool.getConnection().stop();
connectionPool.getConnection().close();
}
connectionPool.clear();
}
return true;
} catch (JMSException e) {
throw new Exception("释放连接出错",e);
}
}
/**
* 释放生产者连接
* @throws Exception
*/
public void closeProducerConnection(MessageProducer producer) throws Exception {
if(producer!=null){
try {
producer.close();
} catch (JMSException e) {
throw new Exception("释放producer连接出错",e);
}
}
}
/**
* 释放消费者连接
* @throws Exception
*/
public void closeConsumerConnection(MessageConsumer consumer) throws Exception {
if(consumer!=null){
try {
consumer.close();
} catch (JMSException e) {
throw new Exception("释放consumer连接出错",e);
}
}
}
/**
* 编写MQ配置bean
*/
@SuppressWarnings("unused")
private static class MqConfigBean{
private String brokerURL;
private String userName;
private String password;
private int sessionCacheSize;
public MqConfigBean(){
}
public MqConfigBean(String brokerURL, String userName, String password, int sessionCacheSize) {
this.brokerURL = brokerURL;
this.userName = userName;
this.password = password;
this.sessionCacheSize = sessionCacheSize;
}
public String getBrokerURL() {
return brokerURL;
}
public void setBrokerURL(String brokerURL) {
this.brokerURL = brokerURL;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getSessionCacheSize() {
return sessionCacheSize;
}
public void setSessionCacheSize(int sessionCacheSize) {
this.sessionCacheSize = sessionCacheSize;
}
}
}
枚举类
public enum MessageTypeEnum {
TOPIC("topic"),QUEUE("queue");
private String typeName;
MessageTypeEnum(String typeName){
this.typeName = typeName;
}
public String getTypeName() {
return typeName;
}
}
消息发送管理类
public class MQPoolSender {
public boolean sendStrMsg(AMQPoolFactory amqFactory,String name,String messageType,String msg) {
if(amqFactory == null) {
log.error("MQ工厂实例为null");
}
if("".equals(name) || name == null) {
log.error("消息队列名称为空");
}
if("".equals(msg) || msg == null) {
log.error("待发送的消息为空");
}
try {
//获取生产者的连接池信息
ProducerPool producerPool = null;
if(onUsing) {//若内存中有则拿出来进行复用
if(mapProPool.containsKey(name)){
producerPool = mapProPool.get(name);
}
}else if(producerPool == null){
producerPool = amqFactory.getProducerPool(name,messageType);
}
Session session = producerPool.getSession();
MessageProducer producer = producerPool.getProducer();
try {
TextMessage textMsg = session.createTextMessage(msg);
long time = 60 * 1000;// 延时1分钟发送
// long period = 10 * 1000;// 每个10s
// int repeat = 6;// 6次
textMsg.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time);
// textMsg.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period);
// textMsg.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
producer.send(textMsg);
System.out.println(">>>>>>>>>>>>>>>>向消息队列"+name+"发送消息为:"+textMsg.getText());
return true;
}catch (JMSException e) {
System.out.println("消息发送失败"+e);
}finally {
//释放生产者连接
amqFactory.closeProducerConnection(producer);
//连接池中的可用生产者减一
amqFactory.producerDecreament(producerPool);
}
}
}catch(Exception e) {
System.out.println("消息发送失败"+e);
}
return false;
}
}
消息接受监听管理类
public class MQReceiverListeners {
public boolean setListener(AMQPoolFactory amqFactory,String name,Class<?> className,String messageType) {
try {
ConsumerPool consumerPool = amqFactory.getConsumerPool(name, messageType);
MessageConsumer consumer = null;
try {
//利用反射机制实例化监听类
DjMessageListener listener = (DjMessageListener) className.newInstance();
consumer = consumerPool.getConsumer();
consumer.setMessageListener(listener);
return true;
}catch (JMSException e) {
System.out.println("消息监听异常"+e);
}finally {
amqFactory.closeConsumerConnection(consumer);
amqFactory.consumerDecreament(consumerPool);
}
}catch (Exception e) {
System.out.println("消息监听异常"+e);
}
return false;
}
}
消息监听实现类
public class DjMessageListener implements MessageListener {
public void onMessage(Message message) {
System.out.println(">>>>>>>>>>>>>>>>>>>>>监听到有消息进来");
try {
TextMessage textMsg = (TextMessage) message;
String text = textMsg.getText();
System.out.println("》》》》》》》》》》》》》》》》》》》》》》》》收到消息"+text);
} catch (JMSException e) {
e.printStackTrace();
}
}
MQ消息队列调度管理类
public class MQManager{
private static volatile MQManager instance = null;
private MQPoolFactory amqFactory = null;
private MQPoolSender poolSender = new MQPoolSender();
private MQReceiverListeners poolReceiver = new MQReceiverListeners ();
//缺省模式
private MQManager() {
initMQ();
};
/**
* 使用单例模式进行类实例化
*/
public static AMQManager getInstance() {
if(instance == null) {
synchronized(AMQManager.class) {
if(instance == null) {
instance = new AMQManager();
}
}
}
return instance;
}
/**
* 使用缺省设置来初始化MQ连接
* @param isUsePool
*/
public void initMQ(boolean isUsePool) {
try {
amqFactory = new AMQPoolFactory();
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 使用连接池的方式来发送的信息
*
*/
public boolean sendStrMsg(String messageNme,String messageType,String msg,boolean onUsing){
return poolSender.sendStrMsg(amqFactory, messageNme, messageType, msg, onUsing);
}
/**
* 使用连接池的方式设置消费者监听
*/
public boolean setListener(String messageNme, Class<?> className,String messageType) {
return poolReceiver.setListener(amqFactory, messageNme, className, messageType);
}
}
测试:
public static void main(String[] agrs) {
MQManager amqManager = MQManager.getInstance();
for(int i=0;i<10;i++) {
amqManager.sendStrMsg("testQueue", "queue", "第"+i+"个定时任务");
}
}
测试结果:
总结:
之前都是直接使用MQ的工厂模式直接创建连接、生产者和消费者,这个其实在小型应用中是没什么问题的,但是在一些大型应用,且存在高并发的情况下,频繁去创建连接,会占用消耗内存资源,这次是综合参考网上诸多demo加上一些特定场景来实现的。 当然MQ也有自带的连接池,有时间会把自带的连接池详细写一下。若有不足之处,欢迎评论区批评。