消息系统
消息经常被用在应用间的通信,或者业务中的异步处理。消息被保存在队列中,直到被接收者取出。由于消息的发送者不需要同步等待消息接受者的响应,消息的异步接收降低了系统集成的耦合性,提升了系统之间的协作效率,使得系统能够更快的响应用户,提供更高的吞吐。当系统处于峰值压力时,分布式消息队列还能够作为缓存,削峰填谷,缓解集群的压力,避免整个系统被压垮。
开源的消息系统主要有ActiveMQ、Kafka、RabbitMQ、ZeroMQ等。
ActiveMQ & JMS
ActiveMQ是Apache所提供的一个开源的消息系统,完全采用Java来实现,因此它能够很好的支持J2EE提出的JMS规范。JMS(Java Message Service,既Java消息服务)是一组Java应用程序接口,它提供消息的创建、发送、接收、读取等一系列服务。JMS定义了一组公共应用程序接口和相应的语法,类似于Java数据库的统一访问接口JDBC,它是一种API。
1.消息类型
JMS支持的消息类型包括TextMessage(简单文本)、ObjectMessage(可序列化的对象)、MapMessage(键值对)、BytesMessage(字节流)、StreamMessage(流)、Message(无有效负载的消息)等。消息的发送是异步的,因此,消息的发布者发送完消息之后,不需要等待消息接受者立即响应,这样便提高了系统之间的协作效率。
2.消息模式
JMS支持两种消息发送和接收模型。P2P(Point-to-Point,即点对点)模型和Pub/Sub(Publish/Subscribe,即发布/订阅)模型。P2P模型是基于Queue(队列)的,消息的生成者将消息发送到队列,消息的消费者从队列中接收消息,队列的存在使得消息的异步传输成为了可能。Pub/Sub模型定义了如何向一个内容节点发布和订阅消息,这个内容节点称为Topic(主题)。Topic可以认为是消息传递的中介,消息的发布者将消息发布到某Topic上,而消息的订阅者则从此Topic上订阅消息。主题使得消息的发布者和消息的订阅者之间保持互相独立,不需要进行接触即可保证消息的传递,发布/订阅模型在消息的一对多广播时使用。
如上图所示,对于点对点消息传输模型来说,多个消息的生产者和消息的消费者都可以注册到同一个消息队列中去,当消息的生产者发送一条消息之后,只有其中一个消息消费者会接收到消息生产者发送的消息,而不是所有的消息消费者都会接收到该条消息。只要队列中有消息,不管消息消费者是什么时候被创建的,都会接收队列中的消息。
如上图所示,对于发布/订阅消息传输模型来说,消息的发布者需将消息投递给Topic,而消息的订阅者则需要在相应的Topic中进行注册,以便接收相应Topic的消息,与点对点模型不同的是,消息发布者发布的消息将自动发送给所有订阅了该Topic的消息订阅者。当消息订阅者某段时间由于某种原因断开了与消息发布者的连接时,这个时间段的消息将会丢失,除非将消息的订阅模式设置为持久订阅,这时消息的发布者将会为消息的订阅者保留这段时间所产生的消息。当消息的订阅者重新连接消息发布者时,消息订阅者仍然可以获得这部分的消息,而不至于丢失这部分消息。与点对点模型不同的是,以消息订阅者注册Listener到Topic的时间节点来算,消息订阅者只会接收此时间节点之后的消息,不会接收此时间节点之前的消息。
3.ActiveMQ安装
接下来就是安装ActiveMQ了,简单的下载-解压-启动(命令是./activemq start),此处不再赘述。需要注意的是,ActiveMQ是纯Java实现的,所以ActiveMQ的安装依赖于Java环境,因此安装ActiveMQ之前,请先保证本机上的Java环境是可用的。为了可以访问到ActiveMQ服务,我们需要开启相应的端口号,包括61616和8161。
4.通过JMS访问ActiveMQ
ActiveMQ实现了JMS规范提供的一系列接口,如创建Session,建立连接,发送消息等,通过这些接口,能够实现消息发送、消息接收、消息发布、消息订阅的功能。
需要引入activemq-core-xxx.jar和activemq-protobuf-xxx.jar
使用JMS来完成ActiveMQ基于Queue的点对点模型的消息发送:
/*
* 操作步骤:
* 1.获得JMS Connection Factory,通过我们提供的特定环境的连接信息来构造Factoty
* 2.利用Factory创建JMS Connection
* 3.启动Connection
* 4.通过Connection创建JMS Session
* 5.通过Session指定JMS Destination
* 6.通过Session创建JMS Producer,并提供Destination
* 7.通过Session创建JMS Message
* 8.发送Message
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://192.168.0.101:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
// true:事务消息;false:非事务消息
// AUTO_ACKNOWLEDGE:Session会自动确认所接受的消息;CLIENT_ACKNOWLEDGE:由客户端程序通过调用消息的确认方法来确认所收到的消息
// DUPS_OK_ACKNOWLEDGE:一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收,而且允许重复确认,
// 在需要考虑资源使用时,这种模式非常有效。注意:如果你的应用程序无法处理重复的消息的话,你应该避免使用这种模式
// SESSION_TRANSACTED:如果会话是事务的则使用此模式,忽略设置的其他模式值,
// 在事务开启之后,和session.commit()之前,所有消费的消息,要么全部正常确认,要么全部回滚,
// 交易模式是很是有效的,但避免应用事务发送一个消息,因为这会带来额外的开销提交或回滚事务
Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
// MessageQueue:消息队列的名称
Destination destination = session.createQueue("MessageQueue");
MessageProducer producer = session.createProducer(destination);
// NON_PERSISTENT:不持久化;PERSISTENT:持久化
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 消息如果在规定时间内没有被消费者消费,将自动从队列中移除
//producer.setTimeToLive(5000);
// 消息类型:TextMessage(简单文本)、ObjectMessage(可序列化的对象)、MapMessage(键值对)
// BytesMessage(字节流)、StreamMessage(流)、(Message)无有效负载的消息
ObjectMessage message = session.createObjectMessage("hello ActiveMQ P2P!");
producer.send(message);
// 如果session是以非事务的方式创建的,必须以session.close()的方式将消息提交到服务器队列
// 如果session是以事务的方式创建的,必须以session.commit()的方式将消息提交到服务器队列
session.commit();
System.out.println("success!");
//producer.close();
//session.close();
//connection.close();
基于点对点模型的消息接收:
/**
* 操作步骤:
* 1.获得JMS Connection Factory,通过我们提供的特定环境的连接信息来构造Factoty
* 2.利用Factory创建JMS Connection
* 3.启动Connection
* 4.通过Connection创建JMS Session
* 5.通过Session指定JMS Destination
* 6.通过Session创建JMS Consumer,并提供Destination
* 7.Consumer消费消息
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://192.168.0.101:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("MessageQueue");
MessageConsumer consumer = session.createConsumer(destination);
/*
while (true) {
// 消费消息:参数是接受消息的超时时间,为0的话则不超时
ObjectMessage message = (ObjectMessage)consumer.receive(20000);
if (message != null) {
String messageContent = (String)message.getObject();
System.out.println(messageContent);
System.out.println("success.");
} else {
System.out.println("no message.");
break;
}
}
*/
consumer.setMessageListener(new MessageListener(){
public void onMessage(Message message) {
ObjectMessage tm = (ObjectMessage)message;
try {
System.out.println(tm.getObject().toString());
} catch (JMSException e) {
e.printStackTrace();
}
System.out.println("success.");
}
});
//consumer.close();
//session.close();
//connection.close();
通过JMS来创建ActiveMQ的Topic,并给Topic发消息:
/**
* 操作步骤:
* 1.获得JMS Connection Factory,通过我们提供的特定环境的连接信息来构造Factoty
* 2.利用Factory创建JMS Connection
* 3.启动Connection
* 4.通过Connection创建JMS Session
* 5.通过Session指定JMS Topic
* 6.通过Session创建JMS Publisher,并提供Topic
* 7.通过Session创建JMS Message
* 8.发送Message
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://192.168.0.101:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("TopicName");
MessageProducer producer = session.createProducer(topic);
// NON_PERSISTENT:不持久化;PERSISTENT:持久化
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage message = session.createTextMessage();
message.setText("Hello ActiveMQ Pub/Sub3!");
producer.send(message);
System.out.println("success.");
//producer.close();
//session.close();
//connection.close();
消息发送到对应的Topic之后,需要将Listener注册到需要订阅的Topic上,以便能够接收该Topic的消息:
/**
* 操作步骤:
* 1.获得JMS Connection Factory,通过我们提供的特定环境的连接信息来构造Factoty
* 2.利用Factory创建JMS Connection
* 3.启动Connection
* 4.通过Connection创建JMS Session
* 5.通过Session指定JMS Topic
* 6.通过Session创建JMS Subscriber,并提供Topic
* 7.通过Subscriber将Listener注册到相应的Topic上
* 8.接收Message
*/
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD,
"tcp://192.168.0.101:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("TopicName");
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener(){
public void onMessage(Message message) {
TextMessage tm = (TextMessage)message;
try {
System.out.println(tm.getText());
System.out.println("success.");
} catch (JMSException e) {
System.out.println("error.");
e.printStackTrace();
}
}
});
参考自《大型分布式网站架构设计与实践》
如发现任何问题还请及时提出,会及时更新的!