我的诉求是,建一个订阅通道,然后多个客户端监听,当某个客户端掉线后,再上线的时候可以收到它没有接收到的消息。
在找到这篇文章前,《如何实现ActiveMq的Topic的持久订阅》(http://www.mytju.com/classcode/news_readNews.asp?newsID=486)给了我提示。要设置clientID啊~
我根据参考的文章编写了工程,测试后发现是ok的。并且发现了一个细节。
比如,有一个clientID=ID_1,有个通道是topic_channel。往topic_channel中发送一批消息(A批)后,首次启动ID_1的监听,ID_1没有收到。关掉ID_1的监听,再往topic_channel中发送一批消息(B批)后,再次启动ID_1的监听,收到了B批消息。这个现象说明了什么呢?说明你不仅要有自己唯一的ID(clientID)还要事先跟这个通道报告一声,说你要监听它,订阅的消息来了之后给它留一份,等着它来拿。在你报告之前产生的消息,你是拿不到的,因为没有你的份。
为了体现我真的动了脑筋,思考了。所以我发表的工程,是做了一些修改的,比如,通道名啊,ID名啊,文件名啊,还调整了一下文件结构,哈哈哈~
当然我还做了一个质的突破,我在一个工程里面做了两个接收者,监听同一个订阅通道。
实现步骤:
1、配置发送xml,applicationContext-send.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:property-placeholder location="classpath:/properties/jms.properties" />
<!-- 配置JMS连接工厂 -->
<bean id="myConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- Session缓存数量 -->
<property name="sessionCacheSize" value="10" />
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- MQ地址 -->
<property name="brokerURL" value="${brokerUrl}" />
<!-- 是否异步发送 -->
<property name="useAsyncSend" value="true" />
</bean>
</property>
</bean>
<!-- 发送消息的目的地(一个主题) -->
<bean id="myDestination" class="org.apache.activemq.command.ActiveMQTopic">
<!-- 设置消息主题的名字 -->
<constructor-arg index="0" value="${send.name}" />
</bean>
<!-- 配置JMS模版 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="myConnectionFactory" />
<property name="defaultDestination" ref="myDestination" />
<!-- 订阅发布模式 -->
<property name="pubSubDomain" value="true" />
<property name="receiveTimeout" value="10000" />
</bean>
</beans>
2、编写发送java,ActiveMQsender.java
package com.by.activeMQ;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
public class ActiveMQsender {
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ApplicationContext/applicationContext-send.xml");
JmsTemplate jmsTemplate = (JmsTemplate) ctx.getBean("jmsTemplate");
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage msg = session.createTextMessage();
// 设置消息属性
msg.setStringProperty("mood", "happy");
// 设置消息内容
msg.setText("Hello World!");
return msg;
}
});
System.out.println("send end");
}
}
3、配置接收xml,applicationContext-receive.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:property-placeholder location="classpath:/properties/jms.properties" />
<!-- 第一个接收者 -->
<!-- 配置JMS连接工厂 -->
<bean id="myConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- Session缓存数量 -->
<property name="sessionCacheSize" value="10" />
<!-- 接收者ID -->
<property name="clientId" value="${topic.clientId}" />
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- MQ地址 -->
<property name="brokerURL" value="${brokerUrl}" />
</bean>
</property>
</bean>
<!-- 发送消息的目的地(一个主题) -->
<bean id="myDestination" class="org.apache.activemq.command.ActiveMQTopic">
<!-- 设置消息主题的名字 -->
<constructor-arg index="0" value="${topic.name}" />
</bean>
<!-- 生产消息配置 (自己定义)-->
<bean id="myTopicConsumer" class="com.by.activeMQ.ActiveMQreceiver" />
<!-- 消息监听器 -->
<bean id="myTopicListener"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="myTopicConsumer" />
<!-- 接收消息的方法名称 -->
<property name="defaultListenerMethod" value="receive" />
<!-- 不进行消息转换 -->
<property name="messageConverter"><null/></property>
</bean>
<!-- 消息监听容器 -->
<bean id="myListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="myConnectionFactory" />
<!-- 发布订阅模式 -->
<property name="pubSubDomain" value="true"/>
<!-- 消息持久化 -->
<property name="subscriptionDurable" value="true"/>
<property name="receiveTimeout" value="10"/>
<!-- 接收者ID -->
<property name="clientId" value="${topic.clientId}" />
<property name="durableSubscriptionName" value="${topic.clientId}"/>
<property name="destination" ref="myDestination" />
<property name="messageListener" ref="myTopicListener" />
</bean>
<!-- 第二个接收者 -->
<!-- 配置JMS连接工厂 -->
<bean id="myConnectionFactory2"
class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- Session缓存数量 -->
<property name="sessionCacheSize" value="10" />
<!-- 接收者ID -->
<property name="clientId" value="${topic2.clientId}" />
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- MQ地址 -->
<property name="brokerURL" value="${brokerUrl}" />
</bean>
</property>
</bean>
<!-- 发送消息的目的地(一个主题) -->
<bean id="myDestination2" class="org.apache.activemq.command.ActiveMQTopic">
<!-- 设置消息主题的名字 -->
<constructor-arg index="0" value="${topic2.name}" />
</bean>
<!-- 生产消息配置 (自己定义)-->
<bean id="myTopicConsumer2" class="com.by.activeMQ.ActiveMQreceiver2" />
<!-- 消息监听器 -->
<bean id="myTopicListener2"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="myTopicConsumer2" />
<!-- 接收消息的方法名称 -->
<property name="defaultListenerMethod" value="receive" />
<!-- 不进行消息转换 -->
<property name="messageConverter"><null/></property>
</bean>
<!-- 消息监听容器 -->
<bean id="myListenerContainer2"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="myConnectionFactory2" />
<!-- 发布订阅模式 -->
<property name="pubSubDomain" value="true"/>
<!-- 消息持久化 -->
<property name="subscriptionDurable" value="true"/>
<property name="receiveTimeout" value="10"/>
<!-- 接收者ID -->
<property name="clientId" value="${topic2.clientId}" />
<property name="durableSubscriptionName" value="${topic2.clientId}"/>
<property name="destination" ref="myDestination2" />
<property name="messageListener" ref="myTopicListener2" />
</bean>
</beans>
4、编写接收java,ActiveMQreceiver.java
package com.by.activeMQ;
import javax.jms.JMSException;
import javax.jms.TextMessage;
import org.springframework.jms.JmsException;
public class ActiveMQreceiver {
public void receive(TextMessage message) throws JmsException, JMSException {
String info = "this is receiver, "
+ " mood is " + message.getStringProperty("mood") + ","
+ "say " + message.getText();
System.out.println(info);
}
}
5、编写另一个接收java,ActiveMQreceiver.java
package com.by.activeMQ;
import javax.jms.JMSException;
import javax.jms.TextMessage;
import org.springframework.jms.JmsException;
public class ActiveMQreceiver2 {
public void receive(TextMessage message) throws JmsException, JMSException {
String info = "this is receiver2,"
+ " mood is " + message.getStringProperty("mood") + ","
+ "say " + message.getText();
System.out.println(info);
}
}
6、编写一个main,开启接收监听,openReceive.java
package com.by.activeMQ;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class openReceive {
public static void main(String[] args) {
@SuppressWarnings({ "unused", "resource" })
ApplicationContext ctx = new ClassPathXmlApplicationContext("ApplicationContext/applicationContext-receive.xml");
while(true) {
}
}
}
7、编写一个配置文件,jms.properties
#send
send.name=Topic_Mood
#receive
topic.name=Topic_Mood
topic.clientId=client_LiLei
topic2.name=Topic_Mood
topic2.clientId=client_HanMei
#url
brokerUrl=failover:(tcp://10.0.0.232:61616)?initialReconnectDelay=1000
8、pom里面添加activeMQ的依赖
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.1</version>
</dependency>
耶,运行就ok了。
发送消息的输出是这样的:
2016-08-05 11:27:19 [ main:0 ] - [ INFO ] Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@16011db4: startup date [Fri Aug 05 11:27:19 CST 2016]; root of context hierarchy
2016-08-05 11:27:19 [ main:31 ] - [ INFO ] Loading XML bean definitions from class path resource [ApplicationContext/applicationContext-send.xml]
2016-08-05 11:27:19 [ main:187 ] - [ INFO ] Loading properties file from class path resource [properties/jms.properties]
2016-08-05 11:27:19 [ main:392 ] - [ INFO ] Established shared JMS Connection: ActiveMQConnection {id=ID:MDG42V9PSU28IKA-60542-1470367639797-1:1,clientId=null,started=false}
2016-08-05 11:27:19 [ ActiveMQ Task-1:467 ] - [ INFO ] Successfully connected to tcp://10.0.0.232:61616
send end
接收消息的输出是这样的:
2016-08-05 11:28:04 [ ActiveMQ Task-1:490 ] - [ INFO ] Successfully connected to tcp://10.0.0.232:61616
2016-08-05 11:28:04 [ main:498 ] - [ INFO ] Established shared JMS Connection: ActiveMQConnection {id=ID:MDG42V9PSU28IKA-60544-1470367684739-1:1,clientId=client_LiLei,started=false}
2016-08-05 11:28:04 [ ActiveMQ Task-1:504 ] - [ INFO ] Successfully connected to tcp://10.0.0.232:61616
2016-08-05 11:28:04 [ main:509 ] - [ INFO ] Established shared JMS Connection: ActiveMQConnection {id=ID:MDG42V9PSU28IKA-60544-1470367684739-3:1,clientId=client_HanMei,started=false}
this is receiver2, mood is happy,say Hello World!
this is receiver, mood is happy,say Hello World!
啦啦啦,不知道大家注意了没有,配置另一个接收者就是,把第一个接收者的配置复制,然后添加个2,再把接收类复制,添加个2,就搞定了。哈哈哈~这种方式也适用于mongodb啊这种配置。在一个工程里面操作两个mongodb数据库。
注:我将代码上传到了csdn。