ActiveMQ 是高性能消息中間件,主要針對JMS實現,當然其他語言也可以使用。其支持點對點、發佈/訂閱、推拉模式,具體看官網,這裏略。
1、先下載ActiveMQ,併成功啓動服務。
2、建立maven項目,添加依賴
activemq-all-5.6.0.jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName" default-lazy-init="false">
<!-- 連接工廠 -->
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616?jms.useAsyncSend=true" />
<!-- jms.useAsyncSend=true 指定異步方式,性能比同步方式提升5倍 -->
</bean>
<!-- 消息列隊(目的地) -->
<bean id="demoQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="demo" />
</bean>
</beans>
4、編寫發送消息和接收消息的簡單java類
public class JmsMQ {
private JmsTemplate jmsTemplate;
private Queue demoQueue;
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.jmsTemplate = new JmsTemplate(connectionFactory);
// this.jmsTemplate.setDeliveryMode(DeliveryMode.NON_PERSISTENT);//消息不持久化
// this.jmsTemplate.setDeliveryPersistent(false);//消息不持久化
}
public void setDemoQueue(Queue demoQueue) {
this.demoQueue = demoQueue;
}
//發送消息
public void simpleSend(final Long size) {
this.jmsTemplate.send(this.demoQueue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
ObjectMessage msg = session.createObjectMessage();
msg.setObject(size);
return msg;
}
});
}
//接收消息
public void simpleReceive() {
Message message = jmsTemplate.receive(demoQueue);
if (message instanceof ObjectMessage) {
ObjectMessage msg = (ObjectMessage) message;
Long size;
try {
size = (Long) msg.getObject();
System.out.println("onMessage [" + size + "]");
} catch (JMSException e) {
e.printStackTrace();
}
} else {
throw new IllegalArgumentException("Message must be of type ObjectMessage.");
}
}
}
5、jmsexample.xml
<bean id="jmsMQ" class="jmsexample.JmsMQ" />
5、編寫測試類
public class JmsMQTest {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("jms.xml", "jmsexample.xml");
JmsMQ mq = (JmsMQ) ctx.getBean("jmsMQ");
long start = System.currentTimeMillis();
for(long i=0;i<1000;i++){
mq.simpleSend(i);//發送消息,異步方式下1000次發送7000毫秒。
}
System.out.println("耗時:" + (System.currentTimeMillis() - start));
//
// long start1 = System.currentTimeMillis();
// for(long i=0;i<10000;i++){
// mq.simpleReceive();//獲取消息
// }
// System.out.println("耗時:" + (System.currentTimeMillis() - start1));
}
}
上面已經完成了JMS開發過程,測試類包含發送消息,接收消息。但是接收消息是主動的,這種方式叫做“拉”模式。
下面是一個監聽指定列隊上消息的例子,也就是“推”模式:
1、編寫監聽消息的java類,需要實現接口
public class ExampleListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
ObjectMessage msg = (ObjectMessage) message;
Long size;
try {
size = (Long) msg.getObject();
System.out.println("onMessage [" + size + "]");
} catch (JMSException e) {
e.printStackTrace();
}
} else {
throw new IllegalArgumentException(
"Message must be of type ObjectMessage.");
}
}
}
2、jmsexample-listener.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName" default-lazy-init="false">
<!-- this is the Message Driven POJO (MDP) -->
<bean id="exampleListener" class="jmsexample.ExampleListener" />
<!-- and this is the message listener container -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="demoQueue" />
<property name="messageListener" ref="exampleListener" />
</bean>
</beans>
3、啓動消息監聽
/**
* 監聽消息列隊
* */
public class ListenerTest {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("jms.xml", "jmsexample-listener.xml");
}
}
到這裏已經講了推拉模式,發消息和接消息。
另外、Spring對JMS進行了包裝,可以將遠程方法調用(RPC)封裝到JMS中,下面請看JMS實現消息方法調用的過程。
重點:MQ不僅可以緩解系統壓力,還可以讓系統與系統之間解耦。通過消息傳遞,可以實現兩個系統之間交互。像觀察者模式一樣,減少了交互對象之間的耦合度。
1、編寫接口
public interface CheckingAccountService {
public void cancelAccount(Long accountId);
public void saveAccount(Long accountId);
}
2、編寫實現類
public class SimpleCheckingAccountService implements CheckingAccountService {
public void cancelAccount(Long accountId) {
double result = 0D;
for(int i=0; i<accountId; ){
result = 31/++i;
}
System.out.println("Cancelling account [" + accountId + "]" + ",result=" + result);
}
public void saveAccount(Long accountId) {
System.out.println("Saving account [" + accountId + "]");
}
}
3、編寫客戶端 client.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName" default-lazy-init="false">
<bean id="checkingAccountService" class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean"><!-- 這是個工廠,返回接口代理,將方法調用封裝爲消息,發送到指定列隊 -->
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
<property name="connectionFactory" ref="connectionFactory"/>
<property name="queue" ref="demoQueue"/>
</bean>
</beans>
4、編寫服務端 server.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName" default-lazy-init="false">
<bean id="checkingAccountService" class="org.springframework.jms.remoting.JmsInvokerServiceExporter"><!-- 這是個代理,將接到的消息轉換爲方法的調用 -->
<property name="serviceInterface" value="com.foo.CheckingAccountService"/>
<property name="service">
<bean class="com.foo.SimpleCheckingAccountService"/>
</property>
</bean>
<!-- 監聽指定列隊-->
<bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="demoQueue"/>
<property name="concurrentConsumers" value="10"/>
<property name="messageListener" ref="checkingAccountService"/><!-- 當列隊有消息,將觸發指定方法,形成方法調用 -->
</bean>
</beans>
5、編寫測試類 Client.java
/**使用Spring JMS調用服務器端方法*/
public class Client {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("jms.xml", "client.xml");
CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService");
service.cancelAccount(1000000000L);
System.out.println("Invoke cancelAccount 1000000000L!");
long start1 = System.currentTimeMillis();
for(long i=0;i<1000;i++){
service.saveAccount(i);
}
System.out.println("耗時:" + (System.currentTimeMillis() - start1));
}
}
6、編寫測試類 Server.java
public class Server {
//啓動服務器,監聽列隊,獲得消息,通過反射調用
public static void main(String[] args) throws Exception {
new ClassPathXmlApplicationContext("jms.xml","server.xml");
}
}
快下班了,細節就不寫了,反正這個東西很簡單,大家一看明瞭!