1、生产者
<!--配置connection-factory,指定连接rabbit server参数 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbit.connect.host}" port="${rabbit.connect.port}" username="${rabbit.connect.username}"
password="${rabbit.connect.password}" channel-cache-size="${rabbit.connect.channelCacheSize}"
publisher-returns="true" publisher-confirms="true" />
<!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
<rabbit:admin connection-factory="connectionFactory" />
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory" />
<!--定义queue 说明:durable:是否持久化;
auto_delete: 当所有消费客户端连接断开后,是否自动删除队列;
exclusive: 仅创建者可以使用的私有队列,断开后自动删除 -->
<rabbit:queue name="paycenter.notify.downstream" durable="true" auto-delete="false" exclusive="false" />
<rabbit:queue name="my.test" durable="true" auto-delete="false" exclusive="false"/>
<rabbit:queue name="my.test1" durable="true" auto-delete="false" exclusive="false"/>
<!--定义exchange路由规则,有以下四种路由规则
fanout-exchange:广播
topic-exchange:bingingkey 、routingkey模糊匹配
direct-exchange:bingingkey 、routingkey完全匹配(最常用)
headers-exchange:根据发送的消息内容中的headers属性进行匹配
??同一类型的只配置一个就行??比如direct-exchange 只需要定义一个就行
??将这种模式的绑定关系都绑定到这里就行 queue是唯一的
并不是通过exchange和bindingKey共同决定唯一性的
exchange只是一个路由规则??????
消费的时候 只是根据queue这个唯一值去决定去哪个queque消费
-->
<!--定义direct-exchange -->
<rabbit:direct-exchange name="directExchange">
<rabbit:bindings>
<rabbit:binding key="paycenter.notify.downstream" queue="paycenter.notify.downstream" />
<rabbit:binding key="myTest1" queue="my.test"/>
<rabbit:binding key="myTest2" queue="my.test1"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!--定义topic-exchange -->
<rabbit:topic-exchange name="mq.asdfExChange" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding pattern="mq.asdf.send" queue="mq.asdf"></rabbit:binding>
<rabbit:binding pattern="mq.asdf2.send" queue="mq.asdf2"></rabbit:binding>
<rabbit:binding pattern="mq.asdf.send" queue="mq.asdf2"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
@Controller
@RequestMapping("/amqpTest")
public class AmqpTestController {
@Autowired
private AmqpTemplate rabbitTemplate;
@RequestMapping("/sendMsg")
@ResponseBody
public String sendAmqbMsg(Model model,@RequestParam(value="msg",defaultValue="hello world!!!")String msg){
//两个不同的rabbitTemplate 放在同一个queque中
log.info("两个不同的rabbitTemplate 放在同一个queque-->myTest1中");
rabbitTemplate1.convertAndSend("directExchange", "myTest1", "rabbitTemplate1 myTest1 my.test");
rabbitTemplate2.convertAndSend("directExchange", "myTest1", "rabbitTemplate2 myTest1 my.test");
Thread.sleep(30000);
//两个相同同的rabbitTemplate 放在相同的Exchange(统一类型的Exchange定义一个即可他只是一个路由规则 queue具有唯一性的队列)中 queue相同 是否queue相同
log.info("两个相同同的rabbitTemplate 放在不相同的Exchangel中 queue-->my.test相同 是否queue相同");
rabbitTemplate.convertAndSend("directExchange", "myTest1", "rabbitTemplate1 myTest1 my.test");
rabbitTemplate.convertAndSend("directExchange", "myTest2", "rabbitTemplate1 myTest2 my.test1");
}
}
2、消费者
<!--配置connection-factory,指定连接rabbit server参数 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbit.connect.host}"
port="${rabbit.connect.port}"
username="${rabbit.connect.username}"
password="${rabbit.connect.password}"
channel-cache-size="${rabbit.connect.channelCacheSize}"
publisher-returns="true"
publisher-confirms="true" />
<!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
<rabbit:admin connection-factory="connectionFactory" />
<bean id="notifyDownStreamConsumer" class="com.emax.paycenter.mq.consumer.NotifyDownStreamConsumer" />
<bean id="testMQCServiceImpl" class="com.emax.paycenter.TestMQCServiceImpl"></bean>
<bean id="testMQCServiceImpl1" class="com.emax.paycenter.TestMQCServiceImpl1"></bean>
<!--
receive-timeout:等待接收超时时长 影响连接创建和销毁
concurrency:消费者个数
max-concurrency:最大消费者个数
min-start-interval:陆续启动 减少并发环境(或是三方系统突然的网络延迟) 大量连接导致的性能耗损
min-stop-interval:陆续销毁 减少突然的安静 导致大量可用连接被销毁
min-consecutive-active: 连续N次没有接收发生超时 则认定为需要创建 消费者
min-consecutive-idle: 连续N次发生了接收超时 则认定消费者需要销毁
prefetch:每个消费者预读条数 因为异步调用三方 性能瓶颈在网络与三方系统所以预读取条数设置为1(默认为5) 只有一条消息被ACK才会接收下一条消息
transaction-size:会影响prefetch的数量
-->
<!--监听器 监听器各个参数的设置
多个queue的监听器统一全配置在此处就可以 -->
<rabbit:listener-container connection-factory="connectionFactory"
receive-timeout="30000"
concurrency="30"
max-concurrency="100"
min-start-interval="5000"
min-stop-interval="60000"
min-consecutive-active="2"
min-consecutive-idle="2"
prefetch="1"
transaction-size="1"
>
<rabbit:listener ref="notifyDownStreamConsumer" queue-names="paycenter.notify.downstream" />
<rabbit:listener ref="testMQCServiceImpl" queue-names="my.test"/>
<rabbit:listener ref="testMQCServiceImpl1" queue-names="my.test1"/>
</rabbit:listener-container>
public class AsdfConsumer implements MessageListener{
@Override
public void onMessage(Message message) {
MessageProperties m=message.getMessageProperties();
String msg= new String (message.getBody());
System.out.println("消费掉了:"+msg+"------->>>>>");
}
}
package com.emax.paycenter.mq.consumer;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
/**
* Created by wangxiaodong on 2018/11/26.
*/
public class MessageConsumer implements ChannelAwareMessageListener {
private Logger logger = LoggerFactory.getLogger(MessageConsumer.class);
@Override
public void onMessage(Message message, Channel channel) throws Exception {
// TODO Auto-generated method stub
// 消息监听接口实现
// 1.MessageListener消费者消息监听(自动进行任务完成确认)
// ①基于实现MessageListener的消费者监听消息时,如果xml里没有配置acknowledge,则是默认如同xml配置acknowledge="auto" ,
// 是自动确认消费者完成任务(消息ack), 如果此时消费者抛出异常 ,消息会返回该队列并发送给其他消费者 ,
// 如没有其他消费者,则会继续发到该消费者
// ②如果xml配置中acknowledge="manual",则无法收到消息。
// 该消息会停留在服务器,然后会发给可以收到消息的消费者。
//
// 2.ChannelAwareMessageListener消费者消息监听(手动进行任务完成确认)
// ①基于实现ChannelAwareMessageListener的消费者监听消息时,xml配置中acknowledge="auto"或不配置acknowledge时,
// 调用方法进行消费者任务完成确认时会报如下异常(com.rabbitmq.client.ShutdownSignalException: channel error;)
// ②所以若要实现手动消费则任务完成确认,xml的监听标签中需要配置acknowledge="manual" 手动确认消费者任务完成(消息ack)
// 消息确认 如未调用如下方法确认,则消息不再发到该消费者(如有其它的消费者,则轮询到其他的消费者),multiple 为false只确认当前一个消息收到,true确认所有consumer获得的消息
// (1)消息确认 如未确认则消息不在发到该消费者,multiple 为 false只确认当前一个消息收到,true确认所有consumer获得的消息
// channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// (2)消息确认并返回队列 如未确认则消息不在发到该消费者,multiple 为 false只确认当前一个消息收到,true确认所有consumer获得的消息;
// requeue 为true该消息重新回到队列,并发到该队列的其他消费者,为false则直接丢掉该消息
// channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
// (3) 拒绝消息 requeue 为true该消息重新回到队列,并发到该队列的其他消费者,如没有其他消费者,则会一直发到该消费者,为false则直接丢掉该消息
// channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
//..........手动消息确认。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//// xml配置中acknowledge="auto" 时 是自动确认ack 如果此时消费者抛出异常 消息会发到该队列其他消费者 如没有其他消费者 则会一直发到该消费者
// if(true){
// throw new NullPointerException(".....admin.....消费者异常。。。。。。。。");
// }
logger.error("收到");
//消息确认 如未确认则消息不在发到该消费者,multiple 为 false只确认当前一个消息收到,true确认所有consumer获得的消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
logger.info("business-admin MessageConsumer receive message 出现异常 并将该消息重新入队列------->:{}", message);
logger.info("messageid:" + message.getMessageProperties().getDeliveryTag() + " ...messageBody:" + message.getBody());
//消息确认并返回队列 如未确认则消息不在发到该消费者,multiple 为 false只确认当前一个消息收到,true确认所有consumer获得的消息;requeue 为true该消息重新回到队列,并发到该队列的其他消费者,为false则直接丢掉该消息
// channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
//拒绝消息 requeue 为true该消息重新回到队列,并发到该队列的其他消费者,如没有其他消费者,则会一直发到该消费者,为false则直接丢掉该消息
// channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
//........................手动通知消息生产者。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}
}