我们现实中往往有这样的需求:
1. 消息接收方和发送方都是集群。
2. 同一个消息的接收方可能有多个集群进行消息的处理。
3. 不同集群对于同一条消息的处理不能相互干扰。
希望可以达到如下的效果:
对于集群消息,采用单独采用queue或者topic都不满足要求。
采用Queue模型导致:单独的queue,消息可能被其他集群消费
采用Topic模型导致,采用topic消息可能被同一集群的相同应用重复消费。
解决方案一,级联
将Jms的Topic和Queue进行级联使用
缺点,实现方式繁重,需要独立的中转的消息订阅者来完成,多了一次消息的投递和一次消息消费过程,对效率有影响,增加了消息中间件负载压力。
解决方案二
ActiveMQ提供了虚拟主题和组合Destinations都可以达到这个效果。
虚拟Destination
对于消息发布者来说,就是一个正常的Topic,名称以VirtualTopic.开头。例如VirtualTopic.vtgroup
对于消息接收端来说,是个队列,不同应用里使用不同的前缀作为队列的名称,即可表明自己的身份即可实现消费端应用分组。
例如Consumer.A.VirtualTopic. vtgroup,说明它是名称为A群组的消费端,同理Consumer.B.VirtualTopic. vtgroup说明是一个名称为B群组的客户端。
默认虚拟主题的前缀是 :VirtualTopic.
消费虚拟地址默认格式:Consumer.*.VirtualTopic.
生产者
public class VtProducer {
//默认连接用户名
private static final String USERNAME
= ActiveMQConnection.DEFAULT_USER;
//默认连接密码
private static final String PASSWORD
= ActiveMQConnection.DEFAULT_PASSWORD;
//默认连接地址
private static final String BROKEURL
= ActiveMQConnection.DEFAULT_BROKER_URL;
//发送的消息数量
private static final int SENDNUM = 10;
public static void main(String[] args) {
ConnectionFactory connectionFactory;
Connection connection = null;
Session session;
Destination destination;
MessageProducer messageProducer;
connectionFactory
= new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKEURL);
try {
connection = connectionFactory.createConnection();
connection.start();
session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
destination = session.createTopic("VirtualTopic.vtgroup");
messageProducer = session.createProducer(destination);
for(int i=0;i<SENDNUM;i++){
String msg = "vtgroup "+i+" "+System.currentTimeMillis();
TextMessage message = session.createTextMessage(msg);
System.out.println("发送消息:"+msg);
messageProducer.send(message);
}
session.commit();
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消费者A
public class VtConsumerA {
private static final String USERNAME
= ActiveMQConnection.DEFAULT_USER;//默认连接用户名
private static final String PASSWORD
= ActiveMQConnection.DEFAULT_PASSWORD;//默认连接密码
private static final String BROKEURL
= ActiveMQConnection.DEFAULT_BROKER_URL;//默认连接地址
public static void main(String[] args) {
ConnectionFactory connectionFactory;//连接工厂
Connection connection = null;//连接
Session session;//会话 接受或者发送消息的线程
Destination destination;//消息的目的地
MessageConsumer messageConsumer;//消息的消费者
//实例化连接工厂
connectionFactory = new ActiveMQConnectionFactory(VtConsumerA.USERNAME,
VtConsumerA.PASSWORD, VtConsumerA.BROKEURL);
try {
//通过连接工厂获取连接
connection = connectionFactory.createConnection();
//启动连接
connection.start();
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个消息队列
destination = session.createQueue("Consumer.A.VirtualTopic.vtgroup");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
try {
System.out.println("Accept msg : "
+((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
}
}
}
消费A2:
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个消息队列
destination = session.createQueue("Consumer.A.VirtualTopic.vtgroup");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
消费B:
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个消息队列
destination = session.createQueue("Consumer.B.VirtualTopic.vtgroup");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
消费C:
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个消息队列
destination = session.createQueue("Consumer.C.VirtualTopic.vtgroup");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
组合Destination
组合队列允许用一个虚拟的destination代表多个destinations。这样就可以通过composite destinations在一个操作中同时向多个destination发送消息。
多个destination之间采用“,”分割。例如:
Queue queue = new ActiveMQQueue("FOO.A,FOO.B,FOO.C");
或
Destination destination = session.createQueue("my-queue,my-queue2");
如果希望使用不同类型的destination,那么需要加上前缀如queue:// 或topic://,例如:
Queue queue = new ActiveMQQueue("cd.queue,topic://cd.mark");
生产者:
session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("cd.queue,topic://cd.mark,otherqueue");
messageProducer = session.createProducer(destination);
消费者otherqueue:
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个连接HelloWorld的消息队列
destination = session.createQueue("otherqueue");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
消费者queueA:
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个连接HelloWorld的消息队列
destination = session.createQueue("cd.queue");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
消费者queueB:
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个连接HelloWorld的消息队列
destination = session.createQueue("cd.queue");
//创建消息消费者
messageConsumer = session.createConsumer(destination);
消费者topic
//创建session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建一个连接HelloWorld的消息队列
destination = session.createTopic("cd.mark");
//创建消息消费者
messageConsumer = session.createConsumer(destination);