实际操作Spring+RabbitMQ遇到的问题及解决办法记录

摘要:这是用来记录在使用Spring 与RabbitMQ过程中遇到的问题

问题一

参考一些博客的Spring与RabbitMQ配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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.xsd">


<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:rabbitmq.properties</value>
</list>
</property>
</bean>

<!-- 连接服务配置 -->
<rabbit:connection-factory id="connectionFactory"
host="172.29.163.xx"
username=""
password="XXXXXXXX "
host5672
/>


<!-- spring template声明-->
<rabbit:template exchange="message" id="rabbitTemplate" connection-factory="connectionFactory"/>
<rabbit:admin connection-factory="connectionFactory" />
<!--添加这句话会报redeclare exchange错误-->

<!-- queue 队列声明-->
<!-- 参数说明:1.durable 消息队列是否持久化;2.auto-delete 当所有消费客户端连接断开后,是否自动删除队列-->
<!-- 3.exclusive 仅创建者可以使用的私有队列,断开后是否自动删除-->
<rabbit:queue durable="true" auto-delete="false" exclusive="false" name="task_queue"/>

<!-- exchange 交换机 绑定 -->
<rabbit:direct-exchange name="message" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="task_queue" key="task_queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>

按照这样的配置,会报如下错误:

CachingConnectionFactory] [] - Channel shutdown: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - cannot redeclare exchange ‘exchangname’ in vhost ‘something’ with different type, durable, internal or autodelete value, class-id=40, method-id=10)

这个错误是重复声明exchange路由的原因,一开始以为是exchange的名称和id没有配置对,来来回回改过重新测试,来来回回对比博客和我自己的配置修改,还是报这个错误,知道我在stackoverflow找到了一个回答:http://stackoverflow.com/questions/28298247/spring-amqp-cannot-redeclare-exchange-xml-config

Actually there is no need to declare them at all. For client application it’s just enough to know exchange and queue names and, of course, routing keys.
From other side, if your application is based on those beans (, etc.), you just need to remove . Only AmqpAdmin is responsible to declare those entities on the Broker.

作为一个客户端程序,不需要使用

1
<rabbit:admin connection-factory="connectionFactory" />

这句配置,因为我们的RabbitMQ是连接远程的RabbitMQ Server 而不是本地的RabbitMQ Server,所以当我们同时配置了远程和admin,就会出现重定义了exchange路由的错误,特别是在远程的RabbitMQ Server已经有了一个同名的exchange。

问题二(解决原因)

解决上一个问题之后,跑了测试程序,在控制台上显示:

1
[INFO ] 2016-04-04 19:43:52 AbstractConnectionFactory:Created new connection: SimpleConnection@60bcbc9f [delegate=amqp://guest@172.29.152.2:5672/]

但是在远程的接受程序中,并没有接收到任何消息
我们打开RabbitMQ Management ,重新发送一次消息,并且查看RabbitMQManagement Web页面上的Connection与Exchange信息,仍然显示没有接收到任何消息。

经过反复尝试,发现将connectionFactory中的host参数删去即可是远程RabbitMQ-Server接收到消息,并且显示连接状态。

  • 疑问一:为什么第一次加了端口号,本地显示已经新建连接,但远程RabbitMQ-Server中并没有显示链接。
  • 疑问二:为什么删去端口号之后,远程RabbitMQ就能看到链接状态,并且在Exchange可以看到接收信息。
  • 疑问三:我们以为是远程的RabbitMQ-Server的5672端口没有开启,导致其接收不到信息,然而经过查看远程信息发现,远程RabbitMQ有打开 5672端口。

问题三(未清楚原因)

解决完上述两个问题之后,后台的处理程序仍然没有接收到任何信息,再次查看RabbitMQManagement后发现,信息已经进入Exchange,但并没有被Queue队列接受。
此为测试程序:

1
2
3
4
5
@RequestMapping(value = "/createUser",method = RequestMethod.GET)
public String sendMessageToRabbitMQ(){
rabbitTemplate.convertAndSend("1232123");
return "redirect:/Login/index";
}

通过查看Spring AMQP 的API Demo之后发现他们是这么写的:

1
2
3
4
5
@RequestMapping(value = "/createUser",method = RequestMethod.GET)
public String sendMessageToRabbitMQ(){
rabbitTemplate.convertAndSend("queue","1232123");
return "redirect:/Login/index";
}

通过这样修改后,远程后台程序可以接收到消息。

  • 疑问一:在配置中,我已经将exchange与queue绑定,照理来说不应该还要在程序中显示地输入queue的参数。
  • 疑问二:是否配置仍存在不合理之处

补充:

1
convertAndSend(queueKey, object);

JMS模版中这段代码的意思是:将Java对象转换为消息发送到匹配Key的交换机中Exchange,由于配置了JSON转换,这里是将Java对象转换成JSON字符串的形式。原文:Convert a Java object to an Amqp Message and send it to a default exchange with a specific routing key.
而Exchange交换机与消息队列是需要routing-key绑定的,即队列通过routing-key绑定到Exchange,并从Exchange中匹配routing-key来获取消息。所一此处的第一个参数queueKey应该填写routing-key。

问题四

Spring中如果存在多个配置文件,在读取时,会报“Could not resolve placeholder”错误。
很有可能是你使用了多个PropertyPlaceholderConfigurer或者多个< context:property-placeholder >的原因。

当我在前期完成Spring与Shiro,Mybatis和RabbitMQ时就发生了这个错误,我的三个xml文件,都使用了< contex:property-placeholeder>标签读取不同的配置文件,则一定会出现“Could not resolve placeholder”错误。

1
2
3
4
5
6
7
8
9
10
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置xml等配置文件的位置-->
<param-value>
classpath:springConfig/applicationContext.xml,
classpath:mybatisConfig/spring-mybatis.xml,
classpath:shiroConfig/spring-shiro.xml,
classpath:rabbitmqConfig/spring-rabbitmq.xml
</param-value>
</context-param>

一定要记住,不管是在一个Spring文件还是在多个Spring文件被统一load的情况下,直接写:

1
2
<context:property-placeholder location="" />  
<context:property-placeholder location="" />

是不被允许的。
解决方法:

1
2
3
<context:property-placeholder location="xxx.properties" ignore-unresolvable="true"  
/>

<context:property-placeholder location="yyy.properties" ignore-unresolvable="true" />

注意所有的配置xml都要加上ignore-unresolvable=”true”,一个加另一个不加也是不行的。

摘自: http://blog.csdn.net/yiluoak_47/article/details/12980781