Apache ActiveMQ - 6 - ActiveMQ





TOPIC 和 Queue的技术特点对比

 

Topic

Queue

概要

Publish Subscribe messaging 发布订阅消息

Point-to-Point 点对点

有无状态

topic数据默认不落地,是无状态的

Queue数据默认会在mq服务器上以文件形式保存

比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面。

也可以配置成DB存储

完整性保障

并不保证publisher发布的每条数据,Subscriber都能接受到。

Queue保证每条数据都能被receiver接收。

消息是否丢失

一般来说publisher发布消息到某一个topic时,

只有正在监听该topic地址的sub能够接收到消息;

如果没有sub在监听,该topic就丢失了。

Sender发送消息到目标Queue,receiver可以异步接收这个Queue上的消息。

Queue上的消息如果暂时没有receiver来取,也不会丢失。

消息发布就收策略

一对多的消息发布接收策略,

监听同一个topic地址的多个sub都能收到publisher发送的消息。

Sub接收完通知mq服务器

一对一的消息发布接收策略,一个sender发送的消息,只能有一个receiver接收。

receiver接收完后,通知mq服务器已接收,mq服务器对queue里的消息采取删除或其他操作。



ActiveMQ使用线程池实现消息的生产与消费

JMS消息生产者

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
import javax.jms.*;
import java.beans.ExceptionListener;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* JMS消息生产者
*/
public class JMSProducer implements ExceptionListener {

//设置连接的最大连接数
public final static int DEFAULT_MAX_CONNECTIONS = 5;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;

//设置每个连接中的最大活动会话数.
public final static int DEFAULT_MAXIMUM_ACTIVE_SESSION_PER_CONNECTION = 300;
private int maximumActiveSessionPerConnection = DEFAULT_MAXIMUM_ACTIVE_SESSION_PER_CONNECTION;

//线程池数量.
public final static int DEFAULT_THREAD_POOL_SIZE = 50;
private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;

//强制使用同步返回数据的格式.
public final static boolean DEFAULT_USE_ASYNC_SEND_FOR_JMS = true;
private boolean useAsyncSendForJms = DEFAULT_USE_ASYNC_SEND_FOR_JMS;

//是否支持持久化消息
public final static boolean DEFAULT_IS_PERSISTENT = true;
private boolean isPersistent = DEFAULT_IS_PERSISTENT;

//链接地址
private String brokerUrl;
private String userName;
private String passWord;
private ExecutorService threadPool;
private PooledConnectionFactory connectionFactory;

public JMSProducer(String brokerUrl, String userName, String passWord) {
	this(brokerUrl, userName, passWord, DEFAULT_MAX_CONNECTIONS, DEFAULT_MAXIMUM_ACTIVE_SESSION_PER_CONNECTION, DEFAULT_THREAD_POOL_SIZE, DEFAULT_USE_ASYNC_SEND_FOR_JMS, DEFAULT_IS_PERSISTENT);
}

public JMSProducer(String brokerUrl, String userName, String password, int maxConnections, int maximumActiveSessionPerConnection, int threadPoolSize, boolean useAsyncSendForJMS, boolean isPersistent) {
	this.useAsyncSendForJms = useAsyncSendForJMS;
	this.isPersistent = isPersistent;
	this.brokerUrl = brokerUrl;
	this.userName = userName;
	this.passWord = password;
	this.maxConnections = maxConnections;
	this.maximumActiveSessionPerConnection = maximumActiveSessionPerConnection;
	this.threadPoolSize = threadPoolSize;
	init();
}

private void init() {
	//设置Java线程池
	this.threadPool = Executors.newFixedThreadPool(this.threadPoolSize);
	//ActiveMQ的连接工厂
	ActiveMQConnectionFactory actualConnectionFactory = new ActiveMQConnectionFactory(this.userName, this.passWord, this.brokerUrl);
	actualConnectionFactory.setUseAsyncSend(this.useAsyncSendForJms);
	//Active中的连接池工厂
	this.connectionFactory = new PooledConnectionFactory();
	this.connectionFactory.setCreateConnectionOnStartup(true);
	this.connectionFactory.setMaxConnections(this.maxConnections);
	this.connectionFactory.setMaximumActiveSessionPerConnection(this.maximumActiveSessionPerConnection);
}

/**
* 执行发送消息的具体方法.
*
* @param queue
* @param map
*/
public void send(final String queue, final Map<String, Object> map) {
	this.threadPool.execute(new Runnable() {
			@Override
			public void run() {
					try {
							sendMsg(queue, map);
					} catch (Exception e) {
							e.printStackTrace();
					}
			}
	});
}


/**
* 真正的执行消息发送
*
* @param queue
* @param map
* @throws Exception
*/
private void sendMsg(String queue, Map<String, Object> map) throws Exception {
	Connection connection = null;
	Session session = null;
	try {
			//从连接池中获取一个连接
			connection = this.connectionFactory.createConnection();
			//false:表示为非事务型消息,后面的参数表示消息的确认类型.
			session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
			//PTP消息方式
			Destination destination = session.createQueue(queue);
			MessageProducer producer = session.createProducer(destination);
			producer.setDeliveryMode(this.isPersistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
			Message message = getMessage(session, map);
			producer.send(message);
	} catch (Exception e) {
			e.printStackTrace();
	}finally {
			closeSession(session);
			closeConnection(connection);
	}
}

private Message getMessage(Session session, Map<String, Object> map) throws Exception {
	MapMessage message = session.createMapMessage();
	if (null != map && !map.isEmpty()) {
			Set<String> keys = map.keySet();
			for (String key : keys) {
					message.setObject(key, map.get(key));
			}
	}
	return message;
}

private void closeSession(Session session) {
	try {
			if (null != session) {
					session.close();
			}
	} catch (Exception e) {
			e.printStackTrace();
	}
}

private void closeConnection(Connection connection) {
	try {
			if (null != connection) {
					connection.close();
			}
	} catch (Exception e) {
			e.printStackTrace();
	}
}

@Override
public void exceptionThrown(Exception e) {
	e.printStackTrace();
	}
}


JMS消息消费者

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQPrefetchPolicy;
import javax.jms.*;

/**
* JMS消息消费者
*/
public class JMSConsumer implements ExceptionListener {

//队列预取策略.
public final static int DEFAULT_QUEUE_PREFETCH = 10;
private int queuePrefetch = DEFAULT_QUEUE_PREFETCH;

private String brokerUrl;
private String userName;
private String passWord;
private MessageListener messageListener;
private Connection connection;
private Session session;
private String queue;

public void start() throws Exception {
	//ActiveMQ的连接工厂
	ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(this.userName, this.passWord, this.brokerUrl);
	connection = connectionFactory.createConnection();
	//ActiveMQ预取策略
	ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy();
	prefetchPolicy.setQueuePrefetch(queuePrefetch);
	((ActiveMQConnection) connection).setPrefetchPolicy(prefetchPolicy);
	connection.setExceptionListener(this);
	connection.start();
	//回话采用非事务级别,消息到达机制使用自动通知机制.
	session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
	Destination destination = session.createQueue(this.queue);
	MessageConsumer consumer = session.createConsumer(destination);
	consumer.setMessageListener(this.messageListener);
}

/**
* 关闭链接.
*/
public void shutdown() {
	try {

	if (null != session) {
			session.close();
			session = null;
	}
	if (null != connection) {
			connection.close();
			connection = null;
	}

	} catch (Exception e) {
			e.printStackTrace();
	}
}

public int getQueuePrefetch() {
	return queuePrefetch;
}

public void setQueuePrefetch(int queuePrefetch) {
	this.queuePrefetch = queuePrefetch;
}

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 MessageListener getMessageListener() {
	return messageListener;
}

public void setMessageListener(MessageListener messageListener) {
	this.messageListener = messageListener;
}

public Connection getConnection() {
	return connection;
}

public void setConnection(Connection connection) {
	this.connection = connection;
}

public Session getSession() {
	return session;
}

public void setSession(Session session) {
	this.session = session;
}

public String getQueue() {
	return queue;
}

public void setQueue(String queue) {
	this.queue = queue;
}

@Override
public void onException(JMSException e) {

}
}



消息操作接口

import javax.jms.Message;

/**
 * 提供消息操作的回调接口
 */
public interface MessageHandler {
	public void handle(Message message);
}


消息消费者监听服务

import javax.jms.Message;
import javax.jms.MessageListener;
import java.util.concurrent.ExecutorService;

/**
* Created by CYX on 2017/3/17 0017.
*/
public class MultiThreadMessageListener implements MessageListener {

//默认线程池数量
public final static int DEFAULT_HANDLE_THREAD_POOL = 10;
//最大的处理线程数
private int maxHnadleThreads;
//提供消息回调调用接口
private MessageHandler messageHandler;
private ExecutorService handleThreadPool;

public MultiThreadMessageListener(MessageHandler messageHandler) {
	this.messageHandler = messageHandler;
}

public MultiThreadMessageListener(int maxHnadleThreads, MessageHandler messageHandler) {
	this.maxHnadleThreads = maxHnadleThreads;
	this.messageHandler = messageHandler;
	//支持阻塞的固定大小的线程池(自行手动创建)
	this.handleThreadPool = new FixedAndBlockedThreadPoolExecutor(this.maxHnadleThreads);

}

/**
 * 监听程序中自动调用的方法.
 *
 * @param message
 */
@Override
public void onMessage(Message message) {
	//使用支持阻塞的固定大小的线程池来执行操作.
	this.handleThreadPool.execute(new Runnable() {
		@Override
		public void run() {
			try {
				MultiThreadMessageListener.this.messageHandler.handle(message);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	});
}
}


支持阻塞的固定大小线程池

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 支持阻塞的固定大小的线程池.
 */
public class FixedAndBlockedThreadPoolExecutor extends ThreadPoolExecutor {

	//一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义.但功能更强大.
	private ReentrantLock lock = new ReentrantLock();

	private Condition condition = this.lock.newCondition();

	public FixedAndBlockedThreadPoolExecutor(int size) {
		super(size, size, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
	}

	/**
	 * 当线程池中没有空闲线程时,会挂起此方法的调用线程.直到线程池中有空闲线程.
	 *
	 * @param command
	 */
	@Override
	public void execute(Runnable command) {
		//进行同步锁定
		this.lock.lock();
		super.execute(command);
		try {
			//如果线程池的数量已经达到最大线程池的数量,则进行挂起操作.
			if (getPoolSize() == getMaximumPoolSize()) {
				this.condition.wait();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			this.lock.unlock();
		}
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		super.afterExecute(r, t);
		try {
			this.lock.lock();
			this.condition.signal();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}



测试

生产消息测试

import com.activemq.activemq6.JMSProducer;
import java.util.HashMap;
import java.util.Map;

/**
* 生产者
*/
public class JMSProducerTest {

public static void main(String[] args) {
	locationTest();
}

/**
 * JMSProducer可以设置成全局的静态变量,只需要实例化一次即可使用,禁止循环重复实例化JMSProducer(因为其内部存在一个线程池.)
 * <p>
 * (有待考证)
 * 支持openwire协议的默认链接为'tcp://localhost:61616',支持stomp协议的默认链接为'tcp://localhost:61613'.
 * <p>
 * 'nio://localhost:61613','tcp://localhost:61613'  可以在activemq.xml配置文件中进行设置.
 */
private static void locationTest() {
	JMSProducer producer = new JMSProducer("tcp://localhost:61616", "admin", "admin");
	Map<String, Object> map = new HashMap<String, Object>();
	map.put("id", 1);
	map.put("name", "123456");
	map.put("password", "password");
	producer.send("test", map);
}
}


消息接收测试

import com.activemq.activemq6.JMSConsumer;
import com.activemq.activemq6.MessageHandler;
import com.activemq.activemq6.MultiThreadMessageListener;
import javax.jms.MapMessage;
import javax.jms.Message;

/**
* 消费者
*/
public class JMSConsumerTest {

/**
 * JMSConsumer可以设置成全局的静态变量.只需实例化一次即可使用,禁止循环重复实例化JMSConsumer(因为其内部存在一个线程池.)
 *
 * @param args
 */
public static void main(String[] args) throws Exception{
	JMSConsumer consumer = new JMSConsumer();
	consumer.setBrokerUrl("tcp://localhost:61616");
	consumer.setQueue("test");
	consumer.setUserName("admin");
	consumer.setPassWord("admin");
	consumer.setQueuePrefetch(500);
	consumer.setMessageListener(new MultiThreadMessageListener(50, new MessageHandler() {
		@Override
		public void handle(Message message) {
			try {
				System.out.println("name is : " + ((MapMessage) message).getString("id"));
				System.out.println("name is : " + ((MapMessage) message).getString("name"));
				System.out.println("name is : " + ((MapMessage) message).getString("password"));
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}));
	consumer.start();
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值