Queue:
消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费、其它的则不能消费此消息了,当消费者不存在时,消息会一直保存,直到有消费消费。
Topic:
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。当生产者发布消息,不管是否有消费者。都不会保存消息。一定要先有消息的消费者,后有消息的生产者。
对比:
| Topic | Queue |
概要 | Publish Subscribe messaging 发布订阅消息 | Point-to-Point点对点 |
有无状态 | topic数据默认不落地,是无状态的。 | Queue数据默认会在mq服务器上以文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kahadb下面。也可以配置成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里的消息采取删除或其他操作。 |
代码(用到的类都是JMS包下的类):
消息生产者:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
public class TextProducer {
/**
* 发送消息到ActiveMQ中,参数为消息内容
* @param data
*/
public static void sendTextMessage(String data) {
//连接工厂
ConnectionFactory factory = null;
//连接
Connection conn = null;
//目的地
Destination dest = null;
//会话
Session session = null;
//消息发送者
MessageProducer producer = null;
//消息对象
Message message = null;
try {
//创建ActiveMQ服务的连接工厂
//三个参数:有认证,用户名、密码、连接地址
//无参构造:默认为本地连接
//单参构造:无验证模式
factory = new ActiveMQConnectionFactory("guest", "guest",
"tcp://192.168.1.123:61616");
//通过工厂,创建连接对象
conn = factory.createConnection();
//启动连接,消息的发送者不是必须启动连接,消息的消费者是必须启动连接
//producer在发送消息的时候,会进行检查,如果没有启动,就会自动启动
conn.start();
/*
* 创建会话的时候必须传递2个参数,分别代表是否支持事务和如何确认消息处理
* 参数1:
* true - 支持事务,参数2对producer为无效的,建议传递的数据是Session.SESSION_TRANSACTED
* false - 不支持事务,参数2必须传递且有效
* 参数2:
* AUTO_ACKNOWLEDGE - 自动确认消息,消息的消费者处理消息后,自动确认
* CLIENT_ACKNOWLEDGE - 客户端手动确认,消息的消费者处理后,必须手动确认
* DUPS_OK_ACKNOWLEDGE - 有副本的客户端手动确认
* 需要批量发送多条消息时,可以使用事务,在最后使用session.commit();提交事务
*/
//通过连接对象,创建会话
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建目的地,参数是目的地的名称,是目的地的唯一标志
//dest = session.createQueue("first MQ");
//创建消息订阅,消息不会被保存到队列中,发出即销毁,需要先有消费的消费者,再有消息的生产者
dest = session.createTopic("test topic");
//创建消息的发送者producer
//创建的消息发送者,发送的消息一定到指定的目的地中
producer = session.createProducer(dest);
//创建文本消息对象,作为具体数据内容的载体
message = session.createTextMessage(data);
//使用producer发送消息到ActiveMQ中的目的地。如果消息发送失败,抛出异常
producer.send(message);
System.out.println("消息发送成功");
} catch(Exception e) {
e.printStackTrace();
} finally {
//回收资源
if(producer!=null) {
try {
producer.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(session!=null) {
try {
session.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
sendTextMessage("第一条ActiveMQ");
}
}
消息消费者:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class TextConsumer {
public static void receiveTextMessage(String destName) {
String receiveMessage = "";
ConnectionFactory factory = null;
Connection conn = null;
Session session = null;
Destination dest = null;
//消息的消费者,用于接收消息的对象
MessageConsumer consumer = null;
Message message = null;
try {
factory = new ActiveMQConnectionFactory("admin", "admin",
"tcp://192.168.1.123:61616");
conn = factory.createConnection();
//消息的消费者必须启动连接,否则无法处理消息
conn.start();
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//获得目的地对象,参数为消息队列的目的地名称
//dest = session.createQueue(destName);
//获取消息订阅,消息不会被保存到队列中,发出即销毁,需要先有消费的消费者,再有消息的生产者
dest = session.createTopic("test topic");
//创建消息的消费者对象,在指定的目的地中获取消息
consumer = session.createConsumer(dest);
//获取消息队列中的消息
//receive方法是一个主动获取消息的方法,执行一次,拉取一个消息
message = consumer.receive();
//取得文本消息
receiveMessage = ((TextMessage)message).getText();
System.out.println(receiveMessage);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(consumer!=null) {
try {
consumer.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(session!=null) {
try {
session.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
receiveTextMessage("first MQ");
}
}