上文介绍了JMS规范的消息模式和组成构件,本文将说一下如何基于ActiveMQ来完成消息队列的使用。
点对点模式
服务端代码:
String user = ActiveMQConnection.DEFAULT_USER;
String password = ActiveMQConnection.DEFAULT_PASSWORD;
String url = ActiveMQConnection.DEFAULT_BROKER_URL;
String subject = "queue.test";
ConnectionFactory contectionFactory = new ActiveMQConnectionFactory( user, password, url);
try {
Connection connection = contectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);//消息是否持久化,仅queue模式可用
for (int i = 0; i < 1000; i++) {
MapMessage message = session.createMapMessage();
message.setString("msg", "test"+i);
producer.send(message);
session.commit();
System.out.println("发送消息:" + message.getString("msg"));
}
producer.close();
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
消费者代码
String user = ActiveMQConnection.DEFAULT_USER;
String password = ActiveMQConnection.DEFAULT_PASSWORD;
String url = ActiveMQConnection.DEFAULT_BROKER_URL;
String subject = "TOOL.DEFAULT";
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory( user, password, url);
Connection connection;
try {
connection = connectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
MessageConsumer message = session.createConsumer(destination);
message.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
MapMessage message = (MapMessage) msg;
try {
System.out.println("收到消息:" + message.getString("msg")));
session.commit();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 实现了一个监听器,监听消息的传递,这样只要每有一个消息,都会即时的传递到程序中。
但是,这样的处理,在高并发的时候,因为它是被动接收,并没有考虑到程序的处理能力,可能会压跨系统,那要怎么办呢?
*/
});
Thread.sleep(30000);
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
发布/订阅模式
生产者代码
String user = ActiveMQConnection.DEFAULT_USER;
String password = ActiveMQConnection.DEFAULT_PASSWORD;
String url = "tcp://localhost:61616";
String subject = "TOPIC_TEST2";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(user, password, url);
Connection connection;
try {
connection = factory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.TRUE, Session.CLIENT_ACKNOWLEDGE);
Topic topic = session.createTopic(subject);
MessageProducer producer = session.createProducer(topic);
for (int i = 0; i < 20; i++) {
MapMessage message = session.createMapMessage();
message.setString("count", "Test"+i);
producer.send(message);
System.out.println("--发送消息:" + message.getString("count"));
}
session.commit();
session.close();
connection.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
消费者代码
String user = ActiveMQConnection.DEFAULT_USER;
String password = ActiveMQConnection.DEFAULT_PASSWORD;
String url = ActiveMQConnection.DEFAULT_BROKER_URL;
String subject = "TOPIC_TEST2";
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(user, password, url);
Connection connection;
try {
connection = factory.createConnection();
connection.start();
final Session session = connection.createSession(Boolean.TRUE, Session.CLIENT_ACKNOWLEDGE);
Topic topic = session.createTopic(subject);
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message msg) {
MapMessage message = (MapMessage) msg;
try {
System.out.println("--订阅者[2]收到消息:" + message.getString("count"));
session.commit();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
}
对于上面使用的api,有几点需要补充说明一下:
在使用Connection创建Session的时候,接口是这样的:createSession(boolean transacted, int acknowledgeMode)。
第一个参数transacted是否支持事务,如果为true,则会忽略第二个参数将被设置为SESSION_TRANSACTED(0)。当第一个参数为false时,acknowledgeMode的值选择如下三个枚举值:
Session.AUTO_ACKNOWLEDGE(1)为自动确认,客户端发送和接收消息不需要做额外的工作。哪怕是接收端发生异常,也会被当作正常发送成功。
Session.CLIENT_ACKNOWLEDGE(2)为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的acknowledge方法。jms服务器才会当作发送成功,并删除消息。
Session.DUPS_OK_ACKNOWLEDGE(3)允许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;而且允许重复确认。
需要说明的是,上述配置均只在点对点模式下生效。
Session接口支持创建如下类型的消息:
createTextMessage() 纯字符串的数据
createObjectMessage() 序列化的对象
createStreamMessage() 流,可以用来传递文件等
createBytesMessage() 字节
createMapMessage() 键值对
createMessage() javax.jms.Message
ActiveMQ集群
ActiveMQ集群自身并没有集成集群部署功能,因此需要借助ZooKeeper来实现集群。
使用ZooKeeper注册所有的ActiveMQ Broker。只有其中的一个Broker可以提供服务,被视为 Master,其他的 Broker 处于待机状态,被视为Slave。Master会将所有的存储操作实时同步给所有Slave。如果Master因故障而不能提供服务,Zookeeper会从Slave中选举出一个Broker充当Master。
ActiveMQ的主从模式特点有:
1)Master 和 Slave各自都单独存储持久化的消息,它们不共享数据;
2)Master收到持久化消息时,需要先同步(sync)给Slave之后,才向Producer发送ACK确认;
3)只有Master负责Client的请求,Slave不接收Client请求。Slave连接到Master,负责备份消息;
4)Master出现故障,Slave有两种处理方式:(1)自己成为Master,(2)关闭;
5)Master 与 Slave之间可能会出现“Split Brain”现象(脑裂问题)。比如:Master本身是正常的,但是Master与Slave之间的网络出现故障,网络故障导致Slave认为Master已经宕机,因为它自己会成为Master(根据配置:shutdownOnMasterFailure)。此时,对Client而言,就会存在两个Master;
6)Slave 只能同步它连接到Master之后的消息。在Slave连接到Master之前Producer向Master发送的消息将不会同步给Slave,这可以通过配置(waitForSlave)参数,只有当Slave也启动之后,Master才开始初始化TransportConnector接受Client的请求(Producer的请求);
7)如果Master或者Slave其中之一宕机,它们之间不同步的消息无法自动进行同步,此时只能手动恢复不同步的消息了。也就是说:“ActiveMQ没有提供任何有效的手段,能够让master与slave在故障恢复期间,自动进行数据同步”;
8)对于非持久化消息,并不会同步给Slave。因此,Master宕机,非持久化消息会丢失。
也就是说,目前ActiveMQ集群并不是高可用的。
ActiveMQ的配置文件
http://wosyingjun.iteye.com/blog/2314683