RabbitMQ--Spring整合

本文详细介绍了RabbitMQ的生产者与消费者的配置过程,包括连接工厂的配置、交换机与队列的定义,以及不同类型的交换机的路由规则。同时探讨了消息发送与接收确认机制,并提供了具体的代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);

		//........................手动通知消息生产者。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

	}
}

消息发送确认 与 消息接收确认(ACK)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值