在Spring框架中使用嵌入的消息队列代理服务(一)

1. 背景

  通常,在Web应用中,客户端发起HTTP请求,访问服务接口,服务端进行处理,并反馈消息,通信即告结束。
  但是,当服务端处理过程较长时,情况就会变得复杂。例如,客户端会考虑添加超时机制,即超过指定时间还没有得到服务端反馈时,将会强制终止连接,并认为远程服务不可用。
  消息队列(Message Queue)通常作为中间件,通过异步的机制,来解决上述这些实时性要求不高、且耗时较长的接口通信问题。
  其优势是,大大缩短客户端的通信等待时间,且能够将请求稳妥地交付给服务端,保证请求的正常执行。
  在用法上,通常会独立地创建一个消息队列服务,所有的生产者(客户端和Web应用)都可以往队列中发送消息,同时所有的消费者(Web应用)都可以侦听并读取消息,并进行后续处理。
  在这种模式下,消息队列是一个公共的消息中转站,多个应用之间可以实现数据的互通。这种方式下,各个应用与消息队列服务之间是通过TCP协议建立长连接,而并非HTTP。
  相对于公共的消息队列服务,在有些场合,也需要采用异步机制,但无需在多个应用之间互通。例如:导出数据记录并保存为excel、执行图片识别任务等。在这些场合下,是否可以考虑不部署单独的消息队列服务,而是嵌入到当前应用中

2. 解决方案

  官方参考:http://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection.html。

2.1 Spring MVC框架下的解决方案
(1)代理服务的定义

  源代码:WEB-INF/mq-broker.xml,该文件在web.xml中被引用。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:amq="http://activemq.apache.org/schema/core" 
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans 
						   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
  						   http://activemq.apache.org/schema/core 
  						   http://activemq.apache.org/schema/core/activemq-core-5.11.0.xsd">

	<!-- Create an embedded ActiveMQ Broker -->
	<!-- http://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection.html -->
	<amq:broker brokerName="embedded" dataDirectory="./data" useJmx="false" persistent="true">
		<amq:transportConnectors>
			<amq:transportConnector name="openwire" uri="tcp://localhost:12345" />
		</amq:transportConnectors>
		<amq:plugins>
			<amq:simpleAuthenticationPlugin>
				<amq:users>
					<amq:authenticationUser username="admin" password="***" groups="admins,publishers,consumers"/>
               		<amq:authenticationUser username="publisher" password="***" groups="publishers,consumers"/>
               		<amq:authenticationUser username="consumer" password="***" groups="consumers"/>
               		<amq:authenticationUser username="guest" password="***" groups="guests"/>
              	</amq:users>
			</amq:simpleAuthenticationPlugin>
		</amq:plugins>
	</amq:broker>
		
	<import resource="mq.xml"/>
	
</beans>

  其中需要注意的是,
  ① 消息队列代理服务由框架来创建,无需代码。
  ② 在配置中指定不同的账户和群组,而不是采用系统默认的admin/admin
  ③ 该消息队列支持持久化。

(2)客户端的定义

  源代码:WEB-INF/mq.xml,该文件在mq-broker.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"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans 
						   http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

	<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName" value="java:comp/env/myJMS/ConnectionFactory"></property>
	</bean>
	
	<!-- P2P模式 -->
	<bean id="messageQueue" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName" value="java:comp/env/myJMS/MessageQueue"></property>
	</bean>
	
	<!-- ### 当代码结构改变时需修改此处类路径  ###-->
	<bean id="messageDispatcher" class="com.foo.app1.integration.MessageDispatcher">
	</bean>
	<bean id="receiveMessageListener" class="com.foo.app1.integration.ReceiveMessageListener">
		<property name="messageDispatcher" ref="messageDispatcher"></property>
	</bean>
		
	<bean id="listenerContainerQueue" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
		<property name="connectionFactory" ref="connectionFactory"></property>
		<property name="destination" ref="messageQueue"></property>
		<property name="messageListener" ref="receiveMessageListener"></property>
	</bean>
	
	<!-- P2P模式 -->
	<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
		<property name="connectionFactory" ref="connectionFactory"></property>
		<property name="defaultDestination" ref="messageQueue"></property>
	</bean>
	
</beans>

  其中,
  ① 使用JNDI定义的消息队列连接工厂(connectionFactory)和消息队列(messageQueue)。
  ② 定义了一个消息侦听器ReceiveMessageListener
  ③ 定义的消息发送模板jmsTemplate

  ①中的JNDI资源定义如下:
  源代码:conf/server.xml,其中的连接信息和账户信息与创建消息队列代理服务(mq-brokder.xml)的一致。

<Context>
	……
	<Resource name="myJMS/ConnectionFactory"
		auth="Container"
		type="org.apache.activemq.ActiveMQConnectionFactory"
		description="JMS Connection Factory"
		factory="org.apache.activemq.jndi.JNDIReferenceFactory"
		brokerURL="tcp://localhost:12345"
		brokerName="embedded"
		userName="consumer"
		password="***"/>

	<Resource name="myJMS/MessageQueue"
		auth="Container"
		type="org.apache.activemq.command.ActiveMQQueue"
		description="Foo Queue"
		factory="org.apache.activemq.jndi.JNDIReferenceFactory"
		physicalName="Foo.Queue"/>
</Context>
(3)消息的发送

  消息发送的思路是调用消息发送模板jmsTemplate的发送方法(“send”)即可。
  源代码:MessageSenderService.java

this.jmsTemplate.send(new MessageCreator() {
	public Message createMessage(Session session) throws JMSException {
		TextMessage textMessage = session.createTextMessage(msg.toString(TAG0));
		return textMessage;
	}
});
(4)消息的接收

  消息接收的思路是通过消息侦听器ReceiveMessageListener来捕获。
  源代码:ReceiveMessageListener.java

public class ReceiveMessageListener implements MessageListener {
	……
	@Override
	public void onMessage(Message message) {
		
		final String TAG0 = Module_Tag+"::onMessage()";
		
		if (message instanceof TextMessage) { //文本消息
			final TextMessage msg = (TextMessage) message;
			String text = null;
			
			try {
				text = msg.getText();
			} catch(JMSException e) {
				this.logger.E(TAG0, e.getMessage() );
			}
			……
		}
	}
	……
};
(5)相关依赖包

  源代码:pom.xml

<!-- 消息队列组件 -->
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-all</artifactId>
	<version>5.11.4</version>
	<type>jar</type>
</dependency>
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-jaas</artifactId>
	<version>5.11.4</version>
</dependency>

  Spring框架的版本为:4.3.25.RELEASE。

3 结语

  嵌入式的消息队列服务适用于接口异步操作的场合,例如:数据记录导出、图像生成、调用第三方的工具服务等。
  嵌入模式的优势在于,既不考虑网络通信(哪怕是LAN),又能使用消息队列的功能特性。
  出于稳定安全考虑,消息队列建议支持持久化(哪怕是Web应用重启),且采用一定安全机制(ActiveMQ支持的是JAAS)。

相关文档:

  (1)有关ActiveMQ jar文件版本导致Spring MVC框架代码冲突的问题,可参见:【开发笔记】Spring MVC框架升级错误:找不到ReflectionUtils.doWithLocalFields方法

在Spring框架中使用嵌入的消息队列代理服务(二)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值