Spring jms 和 ActiveMQ 開發消息服務

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>


3、jms.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="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");
	}
}

快下班了,細節就不寫了,反正這個東西很簡單,大家一看明瞭!






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章