简介:
本篇文章是我个人最近学习rabbitMq的一次总结,通过实际框架中的项目配置和代码,来实现rpc风格的rabbitMq的使用,主要分为两个项目,分别实现发送,回复和接受回复的功能。
本篇文章不是rabbitMq的入门篇,所有对于新入门的同学,对exchange,queue,routingKey这些概念还不懂的话,不建议看这篇文章。
主要分为两个项目:
1、spring框架的主要实现发送消息和接受回复消息的功能,以下简称生产者
2、spring boot框架的项目主要实现接受消息和回复消息的功能,以下简称消费者
废话不多说,直接上代码
1、生产者pom.xml片段,主要在原有框架中,添加spring-rabbit的依赖
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.6.5.RELEASE</version>
</dependency>
2、生产者配置文件,在resources下面新建rabbitMq.properties,内容如下
#rabbitMq
mq.ip=192.168.1.100
mq.port=5672
mq.userName=rabbitmq_producer
mq.password=123456
mq.virtualHost=test_vhosts
3、rabbit的spring配置文件,在resources下面新建spring-rabbitMq.xml,同时在web.xml中,像加载别的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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd" >
<context:annotation-config/>
<!-- 引入配置文件 ps:在别的配置文件中引入了,所有这里注释掉了
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:rabbitMq.properties" />
</bean>
-->
<!--从RabbitMQ Java Client创建RabbitMQ连接工厂对象-->
<bean id="rabbitMQConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
<property name="username" value="${mq.userName}" />
<property name="password" value="${mq.password}" />
<property name="host" value="${mq.ip}" />
<property name="port" value="${mq.port}" />
<property name="virtualHost" value="${mq.virtualHost}" />
</bean>
<!--基于RabbitMQ连接工厂对象构建spring-rabbit的连接工厂对象Wrapper-->
<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg name="rabbitConnectionFactory" ref="rabbitMQConnectionFactory" />
</bean>
<!--构建RabbitAmdin对象,它负责创建Queue/Exchange/Bind对象-->
<bean id="rabbitAdmin" class="org.springframework.amqp.rabbit.core.RabbitAdmin">
<constructor-arg name="connectionFactory" ref="connectionFactory" />
<property name="autoStartup" value="true"/>
</bean>
<!--构建Rabbit Template对象,用于发送RabbitMQ消息,本程序使用它发送消息,并接收返回消息-->
<bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<constructor-arg name="connectionFactory" ref="connectionFactory" />
<property name="useTemporaryReplyQueues" value="false"/>
<property name="messageConverter" ref="serializerMessageConverter"/>
<property name="messagePropertiesConverter" ref="messagePropertiesConverter"/>
<property name="replyAddress" value="springReplyMessageQueue"/>
<property name="receiveTimeout" value="60000"/>
<property name="replyTimeout" value="1000000000" />
</bean>
<!--RabbitMQ消息转化器,用于将RabbitMQ消息转换为AMQP消息,我们这里使用基本的Message Converter -->
<bean id="serializerMessageConverter"
class="org.springframework.amqp.support.converter.SimpleMessageConverter" />
<!--Message Properties转换器,用于在spring-amqp Message对象中的Message Properties和RabbitMQ的
Message Properties对象之间互相转换 -->
<bean id="messagePropertiesConverter"
class="org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter" />
<!--定义AMQP Queue-->
<bean id="springMessageQueue" class="org.springframework.amqp.core.Queue">
<constructor-arg name="name" value="springMessageQueue" />
<constructor-arg name="autoDelete" value="false" />
<constructor-arg name="durable" value="true" />
<constructor-arg name="exclusive" value="false" />
<!--定义AMQP Queue创建所需的RabbitAdmin对象-->
<property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
<!--判断是否需要在连接RabbitMQ后创建Queue-->
<property name="shouldDeclare" value="true" />
</bean>
<!--定义AMQP Exchange-->
<bean id="springMessageExchange" class="org.springframework.amqp.core.DirectExchange">
<constructor-arg name="name" value="springMessageExchange" />
<constructor-arg name="durable" value="true" />
<constructor-arg name="autoDelete" value="false" />
<!--定义AMQP Queue创建所需的RabbitAdmin对象-->
<property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
<!--判断是否需要在连接RabbitMQ后创建Exchange-->
<property name="shouldDeclare" value="true" />
</bean>
<util:map id="emptyMap" map-class="java.util.HashMap" />
<!--创建Exchange和Queue之间的Bind-->
<bean id="springMessageBind" class="org.springframework.amqp.core.Binding">
<constructor-arg name="destination" value="springMessageQueue" />
<constructor-arg name="destinationType" value="QUEUE" />
<constructor-arg name="exchange" value="springMessageExchange" />
<constructor-arg name="routingKey" value="springMessage" />
<constructor-arg name="arguments" ref="emptyMap" />
</bean>
<!--侦听springMessageQueue队列消息的Message Listener-->
<bean id="consumerListener"
class="com.sime.rabbit.listener.RabbitMQConsumer" />
<!--创建侦听springMessageQueue队列的Message Listener Container ,ps:也可以理解为,用来创建connection和通道以及绑定消费者到queue -->
<bean id="messageListenerContainer"
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="messageConverter" ref="serializerMessageConverter" />
<property name="connectionFactory" ref="connectionFactory" />
<property name="messageListener" ref="rabbitTemplate" />
<property name="queues" ref="springReplyMessageQueue" />
<!-- 设置消息确认方式为手动确认 自动确认为AUTO 自动是 MANUAL-->
<property name="acknowledgeMode" value="AUTO" />
</bean>
<bean id="consumerTagStrategy" class="com.sime.rabbit.bean.CustomConsumerTagStrategy"/>
<!-- ################################### rpc 模式 ###################################################################### -->
<!--定义AMQP Reply Queue-->
<bean id="springReplyMessageQueue" class="org.springframework.amqp.core.Queue">
<constructor-arg name="name" value="springReplyMessageQueue" />
<constructor-arg name="autoDelete" value="false" />
<constructor-arg name="durable" value="true" />
<constructor-arg name="exclusive" value="false" />
<property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
<property name="shouldDeclare" value="true" />
</bean>
<!--定义AMQP Reply Exchange-->
<bean id="springReplyMessageExchange" class="org.springframework.amqp.core.DirectExchange">
<constructor-arg name="name" value="springReplyMessageExchange" />
<constructor-arg name="durable" value="true" />
<constructor-arg name="autoDelete" value="false" />
<!--定义AMQP Queue创建所需的RabbitAdmin对象-->
<property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
<property name="shouldDeclare" value="true" />
</bean>
<!--创建Reply Exchange和Reply Queue之间的Bind-->
<bean id="springReplyMessageBind" class="org.springframework.amqp.core.Binding">
<constructor-arg name="destination" value="springReplyMessageQueue" />
<constructor-arg name="destinationType" value="QUEUE" />
<constructor-arg name="exchange" value="springReplyMessageExchange" />
<constructor-arg name="routingKey" value="springReplyMessage" />
<constructor-arg name="arguments" ref="emptyMap" />
</bean>
</beans>
需要注意的是,springReplyMessageQueue是专门用来回复的队列
4、SysRabbitProductTestController.java. 是写的一个用来测试发送消息并接收回复的测试类,这里面需要注意的是
rabbitTemplate.sendAndReceive() 这个方法是默认等待5秒钟,如果在5秒内没有接收到回复的话,会返回一个空的message,这个默认的时间,实在spring-rabbit.xml中的rabbittemplete的配置的replyTimeout 来设置.
另外:使用sendAndReceive 这个方法发送消息时,消息的
correlationId会变成系统动编制的1,2,3这个样子,而不是我们想要的
uuid的格式。
import com.rabbitmq.client.AMQP;
import com.sime.controller.base.BaseController;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.UUID;
/*
*
* rabbitMq 发送测试
*/
@Controller
@RequestMapping("rabbitTest/")
public class SysRabbitProductTestController extends BaseController {
@Autowired
private MessagePropertiesConverter messagePropertiesConverter;
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("send")
@ResponseBody
public String send(){
String correlationId = UUID.randomUUID().toString();
Date sendTime = new Date();
AMQP.BasicProperties props =
new AMQP.BasicProperties("text/plain",
"UTF-8",
null,
2,
0, correlationId, "springReplyMessageExchange", null,
null, sendTime, null, null,
"SpringProducer", null);
MessageProperties sendMessageProperties =
messagePropertiesConverter.toMessageProperties(props, null,"UTF-8");
sendMessageProperties.setReceivedExchange("springReplyMessageExchange");
sendMessageProperties.setReceivedRoutingKey("springReplyMessage");
sendMessageProperties.setRedelivered(true);
String message = "fa song xiaoxi ------------ 走你";
try {
//发送并接收回执
Message sendMessage = MessageBuilder.withBody(message.getBytes("UTF-8"))
.andProperties(sendMessageProperties)
.build();
Message replyMessage = rabbitTemplate.sendAndReceive("springMessageExchange",
"springMessage", sendMessage);
String replyMessageContent = null;
replyMessageContent = new String(replyMessage.getBody(),"UTF-8");
System.out.println("接收到回执: "+replyMessageContent);
return replyMessageContent;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
6、下面是消费者的pom.xml 的重要片段
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
7、application.yml 关于rabbitMq的配置
spring:
rabbitmq:
username: rabbitmq_consumer
host: 192.168.1.100
port: 5672
password: 123456
virtual-host: test_vhosts
8、常量配置类AppConstants
public class AppConstants {
public static final String SEND_QUEUE_NAME = "springMessageQueue";
public static final String SEND_EXCHANGE_NAME = "springMessageExchange";
public static final String SEND_MESSAGE_KEY = "springMessage";
}
9、配置类 RabbitConfig.java
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.sime.constants.AppConstants;
import com.sime.rabbit.FinancMessageListener;
import com.sime.service.UserService;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpoint;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableConfigurationProperties(RabbitProperties.class)
public class RabbitConfig {
@Autowired
private RabbitProperties rabbitProperties;
@Bean("connectionFactory")
public ConnectionFactory getConnectionFactory() {
com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory =
new com.rabbitmq.client.ConnectionFactory();
rabbitConnectionFactory.setHost(rabbitProperties.getHost());
rabbitConnectionFactory.setPort(rabbitProperties.getPort());
rabbitConnectionFactory.setUsername(rabbitProperties.getUsername());
rabbitConnectionFactory.setPassword(rabbitProperties.getPassword());
rabbitConnectionFactory.setVirtualHost(rabbitProperties.getVirtualHost());
ConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitConnectionFactory);
return connectionFactory;
}
@Bean(name="rabbitAdmin")
public RabbitAdmin getRabbitAdmin() {
RabbitAdmin rabbitAdmin = new RabbitAdmin(getConnectionFactory());
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
@Bean(name="serializerMessageConverter")
public MessageConverter getMessageConverter(){
return new SimpleMessageConverter();
}
@Bean(name="messagePropertiesConverter")
public MessagePropertiesConverter getMessagePropertiesConverter()
{
return new DefaultMessagePropertiesConverter();
}
@Bean(name="rabbitTemplate")
public RabbitTemplate getRabbitTemplate()
{
RabbitTemplate rabbitTemplate = new RabbitTemplate(getConnectionFactory());
rabbitTemplate.setUseTemporaryReplyQueues(false);
rabbitTemplate.setMessageConverter(getMessageConverter());
rabbitTemplate.setMessagePropertiesConverter(getMessagePropertiesConverter());
return rabbitTemplate;
}
@Bean(name="springMessageQueue")
public Queue createQueue(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
{
Queue sendQueue = new Queue(AppConstants.SEND_QUEUE_NAME,true,false,false);
rabbitAdmin.declareQueue(sendQueue);
return sendQueue;
}
@Bean(name="springMessageExchange")
public Exchange createExchange(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
{
DirectExchange sendExchange = new DirectExchange(AppConstants.SEND_EXCHANGE_NAME,true,false);
rabbitAdmin.declareExchange(sendExchange);
return sendExchange;
}
@Bean(name="springMessageBinding")
public Binding createMessageBinding(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
{
Map<String,Object> arguments = new HashMap<String,Object>();
Binding sendMessageBinding =
new Binding(AppConstants.SEND_QUEUE_NAME, Binding.DestinationType.QUEUE,
AppConstants.SEND_EXCHANGE_NAME, AppConstants.SEND_MESSAGE_KEY, arguments);
rabbitAdmin.declareBinding(sendMessageBinding);
return sendMessageBinding;
}
}
10.消息接收Listener类
public class FinancMessageListener implements ChannelAwareMessageListener {
@Autowired
private MessagePropertiesConverter messagePropertiesConverter;
@Autowired
private UserService userService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println("+++++++++++++++++++++++++++++接收到消息了+++++++++++++++++++++++++++"+userService);
System.out.println(new String(message.getBody(),"UTF-8"));
try
{
MessageProperties messageProperties = message.getMessageProperties();
AMQP.BasicProperties rabbitMQProperties =
messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
String messageContent = null;
messageContent = new String(message.getBody(),"UTF-8");
String consumerTag = messageProperties.getConsumerTag();
System.out.println(consumerTag+ " get The message content is:" + messageContent);
/**************************返回消息,5秒以内返回*************************************************/
//RPC回复 ,发送消息
String replyMessageContent = "Consumer1 返回的信息 '" + messageContent +" consumerTag: "+consumerTag+ "'";
String cId = rabbitMQProperties.getCorrelationId();
AMQP.BasicProperties replyRabbitMQProps =
new AMQP.BasicProperties("text/plain",
"UTF-8",
null,
2,
0, cId, null, null,
null, null, null, null,
consumerTag, null);
Envelope replyEnvelope =
new Envelope(messageProperties.getDeliveryTag(), true,
"springReplyMessageExchange", "springReplyMessage");
MessageProperties replyMessageProperties =
messagePropertiesConverter.toMessageProperties(replyRabbitMQProps,
replyEnvelope,"UTF-8");
Message replyMessage = MessageBuilder.withBody(replyMessageContent.getBytes("UTF-8"))
.andProperties(replyMessageProperties)
.build();
rabbitTemplate.send("springReplyMessageExchange","springReplyMessage", replyMessage);
channel.basicAck(messageProperties.getDeliveryTag(), false);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
截止到此,关键的部分的代码就完成了,仅做记录