版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/oMaverick1/article/details/52330068
1. RabbitMQ簡介
1.1. RabbitMQ
RabbitMQ是由Erlang(愛立信公司)語言開發,實現Advanced Message Queuing Protocol (AMQP高級消息隊列協議)的消息中間件。消息中間件主要用於組件之間的解耦,消息的發送者無需知道消息使用者的存在,反之亦然。
1.2. 結構圖
• Broker:消息隊列服務器實體,例如RabbitMQ服務
• Vhost:虛擬主機,默認爲“/”,一個broker裏可以有多個vhost,區分不同用戶權限,類似java的命令空間
• Connection:應用程序與broker連接,可有多個連接
• Channel:消息通道,connection中可建立多個channel,每個channel代表一個會話任務,所有操作都在channel中進行。
• Exchange:消息交換機,channel中可有多個,用於投遞消息。應用程序發送消息時先把消息給交換機,由交換機投遞給隊列,不是直接給隊列。
類型有三種:fanout(廣播)、Direct(處理路由鍵,輪播實現)、Topic(支持消息模糊匹配)
• Queue:隊列,用於存放消息
• Message:消息,應用程序需要發送的數據
• Bind:根據routingKey綁定exchange與queue規則,決定消息發送的方向
1.3. 對象間關係
2. rabbitMQ與spring集成
使用spring封裝的rabbitmq的 https://github.com/spring-projects/spring-amqp 做集成。
2.1. 發送消息Producer
發送接口
public interface SimpleMQProducer {
/**
* 發送消息至MQ
*/
public void sendDataToMQ(Object message);
/**
* 發送消息至MQ
*/
public void sendDataToMQ(Object message, String msgid);
}
發送接口實現
public class SmartMQProducer implements InitializingBean,SimpleMQProducer{
protected final Loggerx logger = Loggerx.getLogger("dao");
protected RabbitTemplate rabbitTemplate = new RabbitTemplate();
protected String queue;
protected String exchange;
protected String routingKey;
protected ConnectionFactory connectionFactory;
protected MessageConverter messageConverter;
protected RetryTemplate retryTemplate;
protected ConfirmCallback confirmCallback;
protected ReturnCallback failedCallback;
public RabbitTemplate getRabbitTemplate() {
return rabbitTemplate;
}
public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void setQueue(String queue) {
this.queue = queue;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public void setRoutingKey(String routingKey) {
this.routingKey = routingKey;
}
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
public void setRetryTemplate(RetryTemplate retryTemplate) {
this.retryTemplate = retryTemplate;
}
public void setConfirmCallback(ConfirmCallback confirmCallback) {
this.confirmCallback = confirmCallback;
}
public void setFailedCallback(ReturnCallback failedCallback) {
this.failedCallback = failedCallback;
}
@Override
public void sendDataToMQ(Object message) {
CorrelationData correlationId = null;
try {
correlationId = new CorrelationData(GUID.genTxNo(25));
} catch (Exception e) {
logger.error(LogType.EX, "產生消息id失敗",e);
correlationId = new CorrelationData(UUID.randomUUID().toString());
}
this.rabbitTemplate.convertAndSend(this.routingKey, message, correlationId);
logger.info(LogType.EX, "發送到MQ的消息內容["+JsonUtil.toJSONString(message)+"],消息ID["+correlationId.getId()+"]");
}
@Override
public void sendDataToMQ(Object message, String msgid) {
CorrelationData correlationId = new CorrelationData(msgid);
this.rabbitTemplate.convertAndSend(this.routingKey, message, correlationId);
logger.info(LogType.EX, "發送到MQ的消息內容["+JsonUtil.toJSONString(message)+"],消息ID["+correlationId.getId()+"]");
}
@Override
public void afterPropertiesSet() throws Exception {
this.rabbitTemplate.setQueue(this.queue);
this.rabbitTemplate.setExchange(this.exchange);
this.rabbitTemplate.setRoutingKey(this.routingKey);
this.rabbitTemplate.setConnectionFactory(this.connectionFactory);
this.rabbitTemplate.setMessageConverter(this.messageConverter);
this.rabbitTemplate.setMandatory(true);
if (null == this.failedCallback) {
// 確認消息是否到達broker服務器,也就是隻確認是否正確到達exchange中即可,只要正確的到達exchange中,broker即可確認該消息返回給客戶端ack。
this.rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
//記錄本地日誌
Object object = StringUtil.ByteToObject(message.getBody());
logger.error(LogType.EX, "消息發送到MQ失敗,內容["+object+"]");
}
});
}else {
this.rabbitTemplate.setReturnCallback(this.failedCallback);
}
//設置回調
this.rabbitTemplate.setConfirmCallback(this.confirmCallback);
}
}
發送到MQ失敗回調處理
public abstract class SmartMQFailedCallBack implements ReturnCallback {
protected final Loggerx logger = Loggerx.getLogger("bo");
/**
* 確認消息是否到達broker服務器,也就是隻確認是否正確到達queue中即可,只要正確的到達queue中,broker即可確認該消息返回給客戶端ack。
*/
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
if (null != message) {
Object object = StringUtil.ByteToObject(message.getBody());
logger.error(LogType.EX, "消息發送到MQ失敗,內容["+object+"]");
executeFailedMessage(object);
}else {
logger.error(LogType.EX, "消息發送到MQ失敗,消息內容爲null");
}
}
public abstract void executeFailedMessage(Object message);
}
發送到MQ後回調處理(不分成功或失敗)
public abstract class SmartMQConfirmCallBack implements ConfirmCallback{
protected final Logger logger = Logger.getLogger("bo");
/**
* 確認消息是否到達broker服務器,也就是隻確認是否正確到達exchange中即可,只要正確的到達exchange中,broker即可確認該消息返回給客戶端ack。
*
*/
public void confirm(CorrelationData correlationData, boolean ack) {
if (ack) {
logger.info(LogType.INFO, "消息成功消費,消息ID["+correlationData.getId()+"]");
} else {
logger.error(LogType.EX, "消息失敗消費,消息ID["+correlationData.getId()+"]");
}
executeCallBack(correlationData.getId(),ack);
}
public abstract void executeCallBack(String msgID,boolean ack);
}
發送端spring
<?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:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd">
<!-- 連接服務配置 -->
<bean id="mqConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="xxx.xxx.xx.xx"/>
<property name="username" value="guest"/>
<property name="password" value="guest"/>
<property name="virtualHost" value="/"/>
<property name="channelCacheSize" value="50"/>
<property name="publisherConfirms" value="true"/>
<property name="publisherReturns" value="true"/>
</bean>
<rabbit:admin connection-factory="mqConnectionFactory" />
<!-- 聲明消息轉換器爲SimpleMessageConverter -->
<bean id="msgConverter" class="org.springframework.amqp.support.converter.SimpleMessageConverter" />
<!-- 消息發送重試 ,可選項-->
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="500" />
<property name="multiplier" value="10.0" />
<property name="maxInterval" value="10000" />
</bean>
</property>
</bean>
<!-- queue 隊列聲明 -->
<!-- durable=true,交換機持久化,rabbitmq服務重啓交換機依然存在,保證不丟失; durable=false,相反 -->
<!-- auto-delete=true:無消費者時,隊列自動刪除; auto-delete=false:無消費者時,隊列不會自動刪除 -->
<!-- 排他性,exclusive=true:首次申明的connection連接下可見; exclusive=false:所有connection連接下都可見-->
<rabbit:queue id="test" durable="true" auto-delete="false" exclusive="false" name="test" />
<!-- exchange queue binging key 綁定 -->
<!-- durable=true,交換機持久化,rabbitmq服務重啓交換機依然存在,保證不丟失; durable=false,相反 -->
<!-- auto-delete=true:無消費者時,隊列自動刪除; auto-delete=false:無消費者時,隊列不會自動刪除 -->
<rabbit:direct-exchange name="test" durable="true" auto-delete="false" id="test">
<rabbit:bindings>
<rabbit:binding queue="test" key="test_key" />
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 消息發送到mq回調處理,需要處理錯誤消息,可選項 -->
<bean id="testfailedCallback" class="xxx.TestMsgFailedCallBack"></bean>
<!-- 消息發送到mq回調處理,接着業務處理 ,可選項-->
<bean id="testconfirmCallback" class="xxx.TestconfirmCallback"></bean>
<bean id="testProducer" class="XXXX.SmartMQProducer">
<property name="connectionFactory" ref="mqConnectionFactory" />
<property name="messageConverter" ref="msgConverter" />
<property name="retryTemplate" ref="retryTemplate" />
<property name="confirmCallback" ref="testconfirmCallback" />
<property name="failedCallback" ref="testfailedCallback" />
<property name="exchange" value="test" />
<property name="queue" value="test" />
<property name="routingKey" value="test" />
</bean>
</beans>
2.2. 消費端Consumer
接收端spring配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 連接服務配置 -->
<bean id="mqConnectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg value="xxx.xxx.xxx.xxx" />
<property name="username" value="guest" />
<property name="password" value="guest" />
<property name="virtualHost" value="/" />
<property name="channelCacheSize" value="50" />
</bean>
<!-- 創建rabbitAdmin 代理類 -->
<rabbit:admin connection-factory="mqConnectionFactory" />
<!-- 聲明消息轉換器爲SimpleMessageConverter -->
<bean id="msgConverter"
class="org.springframework.amqp.support.converter.SimpleMessageConverter" />
<!-- queue 隊列聲明 -->
<rabbit:queue id="test" name="test" durable="true" auto-delete="false" exclusive="false" />
<!-- exchange queue binging key 綁定 -->
<!-- durable=true,交換機持久化,rabbitmq服務重啓交換機依然存在,保證不丟失; durable=false,相反 -->
<!-- auto-delete=true:無消費者時,隊列自動刪除; auto-delete=false:無消費者時,隊列不會自動刪除 -->
<!-- 通過Binding來判定Queue、Exchange、routingKey -->
<rabbit:direct-exchange id="test" name="test"
durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="test" key="test_key" />
</rabbit:bindings>
</rabbit:direct-exchange>
<bean id="testMsgHandler" class="xxxx.testMsgHandler" />
<bean id="testMsgAdapter" class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="testHandler" />
<property name="defaultListenerMethod" value="handleTxMsg"></property>
<property name="messageConverter" ref="msgConverter"></property>
</bean>
<bean id="testlistenerContainer" class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="queueNames" value="test"></property>
<property name="connectionFactory" ref="mqConnectionFactory"></property>
<property name="messageListener" ref="testAdapter"></property>
<property name="maxConcurrentConsumers" value="100" />
<property name="concurrentConsumers" value="20" /
</bean>
</beans>
接收端封裝
public abstract class ConsumerHandler{
protected static Logger logger = Logger.getLogger("bo");
public void handleTxMsg(Object message) {
try {
//基類的業務處理
}
logger.info(LogType.MQ, "消息內容["+JsonUtil.toJSONString(message)+"]");
handler(message);
} catch (Exception e) {
logger.error(LogType.EX, e, e);
} finally {
LogContextHolder.getInstance().removeLogContext();
}
}
//實際的業務處理
public abstract void handler(Object message);
}