Spring集成RabbitMQ並實現延遲隊列

一、說明

在實際業務場景中可能會用到延時消息發送,例如異步回調失敗時的重發機制。 RabbitMQ本身不具有延時消息隊列的功能,但是可以通過rabbitmq-delayed-message-exchange來實現(也可以通過TTL(Time To Live)、DLX(Dead Letter Exchanges)特性實現,我們主要講解通過延遲插件來實現的方法)。利用RabbitMQ的這種特性,應該可以實現很多現實中的業務,我們可以發揮想象。 

二、安裝插件

RabbitMQ的安裝請參考我的文章“RabbitMQ安裝與使用”,這裏我們重點講插件的安裝。

首先到http://www.rabbitmq.com/community-plugins.html網頁下載適合的“rabbitmq_delayed_message_exchange插件”。下載完成後將它放到RabbitMQ插件安裝目錄({rabbitmq-server}/plugins/),然後執行命令rabbitmq-plugins enable rabbitmq_delayed_message_exchange啓用插件,執行命令rabbitmq-plugins disable rabbitmq_delayed_message_exchange也可以關閉插件。具體過程可以查看參考文檔2。

三、Spring集成RabbitMQ

1、maven配置

		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-amqp</artifactId>
			<version>1.6.6.RELEASE</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
					<version>4.1.6.RELEASE</version>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit</artifactId>
			<version>1.6.6.RELEASE</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
					<version>4.1.6.RELEASE</version>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-messaging</artifactId>
					<version>4.1.6.RELEASE</version>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-tx</artifactId>
					<version>4.1.6.RELEASE</version>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
					<version>4.1.6.RELEASE</version>
				</exclusion>
			</exclusions>
		</dependency>
說明:實現延遲隊列需要Spring在4.1以上,spring-amqp在1.6以上。

2、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:tx="http://www.springframework.org/schema/tx"
	xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
							http://www.springframework.org/schema/context 
						    http://www.springframework.org/schema/context/spring-context-3.1.xsd
						    http://www.springframework.org/schema/tx
						    http://www.springframework.org/schema/tx/spring-tx.xsd 
						    http://www.springframework.org/schema/aop
						    http://www.springframework.org/schema/aop/spring-aop.xsd
						    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd 
						    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.6.xsd">
	<context:property-placeholder location="classpath:rmq-config.properties" ignore-unresolvable="true"/>

	<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
		<property name="host" value="${rabbitmq.host}" />
		<property name="port" value="${rabbitmq.port}" />
		<property name="username" value="${rabbitmq.username}" />
		<property name="password" value="${rabbitmq.password}" />
		<property name="channelCacheSize" value="${rabbitmq.channel.cacheSize}" />
	</bean>

	<bean id="orderConsumer" class="com.xxx.rmq.OrderConsumer"></bean>
	<bean id="messageConverter" class="org.springframework.amqp.support.converter.SimpleMessageConverter" />
	<bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />

	<rabbit:admin connection-factory="connectionFactory" />
	
	<!-- 延遲消息start -->
  	<rabbit:topic-exchange name="delay_exchange" delayed="true">
  		<rabbit:bindings>
			<rabbit:binding queue="delay_queue" pattern="order.delay.notify" />
		</rabbit:bindings>
  	</rabbit:topic-exchange>
	
	<rabbit:queue name="delay_queue" durable="true" auto-declare="true" auto-delete="false" />
	
	<rabbit:template id="delayMsgTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" exchange="delay_exchange" />
	
	<rabbit:listener-container connection-factory="connectionFactory" channel-transacted="false" acknowledge="auto" message-converter="jsonMessageConverter">
		<rabbit:listener queues="delay_queue" ref="orderConsumer" method="delayMsg" />
	</rabbit:listener-container>
	<!-- 延遲消息end -->

</beans>
說明:spring-rabbit-1.6.xsd必須是1.6及以上版本,否則會報“元素 'rabbit:topic-exchange' 中不允許出現屬性 'delayed'”錯誤。具體請查看參考文檔3。

四、延遲隊列的使用

1、發送消息Producer

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/** 
 * 
 * @author Horace 
 * @version 創建時間:2016年10月26日 下午6:34:31 
 */
@Service
public class MessageProducerServiceImpl implements MessageProducerService{
    @Autowired
    private AmqpTemplate delayMsgTemplate;
	@Override
	public void delayMsg(JSONObject msg,int delay) {
		// TODO Auto-generated method stub 
		final int xdelay= delay*1000; 
		delayMsgTemplate.convertAndSend("order.delay.notify", (Object) msg,
				new MessagePostProcessor() {

					@Override
					public Message postProcessMessage(Message message)
							throws AmqpException {
						// TODO Auto-generated method stub
						message.getMessageProperties().setDelay(xdelay);
						return message;
					}
				});
	}
}

2、異步接收消息Consumer

import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/** 
 *
 * @author Horace 
 * @version 創建時間:2016年10月26日 下午2:48:14 
 */
public class OrderConsumer {
	
	private static Logger logger = LoggerFactory.getLogger(OrderConsumer.class);
    
    @Autowired
    private MessageProducerService messageProducerService;
	
	
	public void delayMsg(Object obj) {
		logger.info("[延時消息]" + obj);
		if (obj != null) {
			JSONObject notifyJson = JSONObject.fromObject(obj);
			String notifyUrl = notifyJson.getString("notifyUrl");
			String notifyContent = notifyJson.getString("notifyContent");
			String result = HttpUtil.postMessage(notifyUrl, notifyContent);
			if (StringUtils.isBlank(result)) { // 通知失敗 進入重發機制
				int newNotifyCount = notifyJson.getInt("notifyCount") + 1; //已經通知的次數
				if (newNotifyCount < 5) {
					notifyJson.put("notifyCount", newNotifyCount);
					int spacingInterval = getSpacingInterval(newNotifyCount);
					messageProducerService
							.delayMsg(notifyJson, spacingInterval);
				} else {
					logger.info("通知5次都失敗,等待後臺手工處理!");
				}
			}
		}
	}
	
	/**
	 * 重複通知間隔時間(單位爲秒)
	 * @param notifyCount 已經通知的次數
	 * @return
	 */
	private int getSpacingInterval(int notifyCount) {
		// TODO Auto-generated method stub
		int spacingInterval = 0;
		switch (notifyCount) {
		case 1:
			spacingInterval = 10;
			break;
		case 2:
			spacingInterval = 20;
			break;
		case 3:
			spacingInterval = 30;
			break;
		case 4:
			spacingInterval = 60;
			break;
		case 5:
			spacingInterval = 90;
			break;
		default:
			break;
		}
		return spacingInterval;
	}
	
}


參考文檔:

1、http://blog.csdn.net/tongdao/article/details/51638066 RabbitMQ安裝與使用

2、http://blog.csdn.net/u014308482/article/details/53036770  rabbitmq 實現延遲隊列的兩種方式

3、http://docs.spring.io/spring-amqp/docs/1.6.0.RELEASE/reference/html/_reference.html#delayed-message-exchange

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