ActiveMQ
特性及优势
1. 实现JMS1.1规范,支持J2EE1.4以上。
2. 可运行与任何JVM和大部分web容器(ActiveMQ works great in any JVM)
3. 支持多种语言客户端(java, C, C++, Ajax, ActionScript等等)
4. 支持多种协议(stomp, openwire, REST)
5. 良好的Spring支持(ActiveMQ has great Spring Support)
6. 速度很快,JBossMQ的十倍(ActiveMQ is very fast; often 10x faster than JBossMQ)
7. 与OpenJMS、JBossMQ等开源jms provider相比,ActiveMQ有apache的支持,持续发展的优势明显
Queue与Topic的比较
1. JMS Queue执行load balancer语义
一条消息仅能被一个consumer收到。如果在message发送的时候没有可用的consumer,那么它讲被保存一直到能处理该message的consumer可用。如果一个consumer收到一条message后却不响应它,那么这条消息将被转到另外一个consumer那儿。一个Queue可以有很多consumer,并且在多个可用的consumer中负载均衡。
2. Topic实现publish和subscribe语义
一条消息被publish时,他将发送给所有感兴趣的订阅者,所以零到多个subscriber将接收到消息的一个拷贝。但是在消息代理接收到消息时,只有激活订阅的subscriber能够获得消息的一个拷贝。
3. 分别对应两种消息模式
Point-to-Point(点对点),Publisher/Subscriber Model(发布/订阅者)
其中在Publicher/Subscriber模式下又有Nondurable subscription(非持久化订阅)和durable subscription(持久化订阅)两种消息处理方式。
启动ActiveMQ
run ActiveMQ
as a foregroud process:
./activemq console
run ActiveMQ
as a daemon process:
./activemq start
终止
CTRL+C
./activemq stop
ActiveMQ默认启动时,启动了内置的jetty服务器,提供一个用于监控ActiveMQ的admin应用。
- URL: http://127.0.0.1:8161/admin/
- Login: admin
- Passwort: admin
- Navigate to "Queues"
- Add a queue name and click create
什么情况下使用ActiveMQ?
- 多个项目之间集成
(1) 跨平台
(2) 多语言
(3) 多项目 - 降低系统间模块的耦合度,解耦
(1) 软件扩展性 - 系统前后端隔离
(1) 前后端隔离,屏蔽高安全区
ActiveMQ消息持久化
http://www.open-open.com/lib/view/open1371822097588.html
消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重新启动后仍然可以将消息发送出去,如果把这种持久化和ReliableMessaging结合起来应该是很好的保证了消息的可靠传送。
消息持久性的原理很简单,就是在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等,然后试图将消息发送给接收者,发送成功则将消息从存储中删除,失败则继续尝试。消息中心启动以后首先要检查制定的存储位置,如果有未发送成功的消息,则需要把消息发送出去。
ActiveMQ持久化方式:AMQ、KahaDB、JDBC、LevelDB。
1、AMQ
AMQ是一种文件存储形式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32M,如果一条消息的大小超过了32M,那么这个值必须设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本。
2、KahaDB
KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。
3、JDBC
可以将消息存储到数据库中,例如:Mysql、SQL Server、Oracle、DB2。
4、LevelDB
这种文件系统是从ActiveMQ5.8之后引进的,它和KahaDB非常相似,也是基于文件的本地数据库储存形式,但是它提供比KahaDB更快的持久性。与KahaDB不同的是,它不是使用传统的B-树来实现对日志数据的提前写,而是使用基于索引的LevelDB。
JMS + Spring
Spring提供了用于简化JMS API使用的抽象框架,并且对用户屏蔽了JMS API中1.0.2和1.1版本的差异。
JMS的功能大致上分为两块,叫做消息制造和消息消耗。JmsTemplate用于制造消息和同步消息接收。和J2EE的事件驱动Bean风格类似,对于异步接收消息,Spring提供了一些消息监听容器来创建消息驱动的POJO(MDP)
public class TestProducer implements Runnable {
@Override
public void run() {
// 连接工厂,创建JMS连接
ActiveMQConnectionFactory connectionFactory;
// JMS客户端到JMS服务端的连接
Connection connection = null;
// 一个发送或接收消息的线程(一个继承了Runnable的接口)
Session session = null;
try {
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);
// 消息是否异步发送
connectionFactory.setUseAsyncSend(true);
// 从工厂构造连接对象(根据url,user和password创建一个jms Connection,如果是durable模式,还需要给connection设置一个clientId)
connection = connectionFactory.createConnection();
connection.start();
/* 在connection的基础上创建一个session,同时设置是否支持事务ACKNOWLEDGE标识。
*
第一个参数:是否使用事务:当消息发送者向消息提供者(即消息代理)发送消息时,消息发送者等待消息代理的确认,没有回应则抛出异常,消息发送程序负责处理这个错误。
第二个参数:消息的确认模式:
AUTO_ACKNOWLEDGE:自动确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收。
CLIENT_ACKNOWLEDGE:客户端确认模式。会话对象依赖于应用程序对被接收的消息调用一个acknowledge()方法。
一旦这个方法被调用,会话会确认最后一次确认之后所有接收到的消息。这种模式允许应用程序以一个调用来接收,处理并确认一批消息。
注意:在管理控制台中,如果连接工厂的Acknowledge Policy(确认方针)属性被设置为"Previous"(提前),
但是你希望为一个给定的会话确认所有接收到的消息,那么就用最后一条消息来调用acknowledge()方法。
DUPS_OK_ACKNOWLEDGE:允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;
而且允许重复确认。在需要考虑资源使用时,这种模式非常有效。注意:如果你的应用程序无法处理重复的消息的话,你应该避免使用这种模式。
如果发送消息的初始化尝试失败,那么重复的消息可以被重新发送。
// AUTO_ACKNOWLEDGE : 指定消息提供者在每次收到消息时自动发送确认。消息只向目标发送一次,但传输过程中可能因为错误而丢失消息。
// CLIENT_ACKNOWLEDGE : 由消息接收者确认收到消息,通过调用消息的acknowledge()方法(会通知消息提供者收到了消息)
// DUPS_OK_ACKNOWLEDGE : 指定消息提供者在消息接收者没有确认发送时重新发送消息(这种确认模式不在乎接收者收到重复的消息)。
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 消息的目的地,TestQueue是一个服务器的Queue,需要在ActiveMq的console中配置
Destination destination = session.createQueue("TestQueue");
// Destination destination = session.createTopic("TestTopic");
// 消息的提供者/发送者
MessageProducer producer = session.createProducer(destination);
// 设置消息是否持久化,NON_PERSISTENT = 1,PERSISTENT = 2
// 若设置为PERSISTENT,则消息是持久化的,MQ重启后未被消费的消息依然在Pending Messages里
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 发送信息
sendMessage(session, producer);
// commit的对象是事务,所以session的transacted字段为true时才需要commit,否则抛出异常IllegalStateException: Not a transacted session
// session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != session)
session.close();
if (null != connection)
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
private static void sendMessage(Session session, MessageProducer producer) throws Exception {
TextMessage message = session.createTextMessage("Test消息");
System.out.println("Test消息 " + " send!");
producer.send(message);
}
public static void main(String[] args) {
Thread thread = new Thread(new TestProducer());
thread.setDaemon(false);
thread.start();
}
}
public class TestConsumer implements Runnable, ExceptionListener {
@Override
public void run() {
// 连接工厂,创建JMS连接
ConnectionFactory connectionFactory;
// JMS客户端到JMS服务端的连接
Connection connection = null;
// 一个发送或接收消息的线程(一个继承了Runnable的接口)
Session session = null;
try {
// 创建连接工厂
connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnectionFactory.DEFAULT_USER,
ActiveMQConnectionFactory.DEFAULT_PASSWORD,
ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL);
// 从工厂构造连接对象
connection = connectionFactory.createConnection();
connection.start();
// 获取连接
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 消息的目的地,TestQueue时一个服务器的Queue,需要在ActiveMq的console中配置
Destination destination = session.createQueue("TestQueue");
// 消息的消费者/接收者
MessageConsumer consumer = session.createConsumer(destination);
/*
* consumer调用receive方法实现同步
*/
// 接收消息,参数:接收消息的超时时间,为0的话则不超时,receive返回下一个消息,但是超时了或者消费者被关闭,返回null
// 用一个死循环让consumer不停地接收消息
// while (true) {
Message message = consumer.receive();
if (message instanceof TextMessage) {
TextMessage msg = (TextMessage) message;
System.out.println("收到文本信息: " + msg.getText());
} else {
System.out.println("收到未知信息: " + message);
}
// }
/*
* consumer调用setMessageListener方法实现异步
*/
//TODO 异步还没弄懂
// consumer.setMessageListener(new MessageListener() {
// @Override
// public void onMessage(Message message) {
// TextMessage msg = (TextMessage) message;
// if (null != msg) {
// try {
// System.out.println("收到信息: " + msg.getText());
// } catch (JMSException e) {
// System.out.println("获取信息异常!");
// }
// }
// }
// });
// SESSION_TRANSACTED为true时才需要commit,否则抛出异常IllegalStateException: Not a transacted session
// session.commit();
// consumer.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != session)
session.close();
if (null != connection)
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
@Override
public void onException(JMSException exception) {
System.out.println("JMS Exception occured. Shutting down client.");
}
public static void main(String[] args) {
Thread thread = new Thread(new TestConsumer());
thread.setDaemon(false);
thread.start();
}
}