springboot集成rabbitMQ(消费者篇)

maven的配置类还是和前面的一样。

package com.te.mm.factoryconfig;



import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.te.mm.Entity.ObjConsert;
/**
 * 创建一个生产者工厂,然后注册到spring容器中,当然我们也可以使用springboot的自动装配
 * 
 * 自动装备所在的包package org.springframework.boot.autoconfigure.amqp;
 * @ConfigurationProperties(prefix = "spring.rabbitmq") RabbitProperties.class
 * 对springboot自动装配有过了解的童鞋,就你那个看懂上面springboot是如何进行对MQ装配的了
 * @author Administrator
 *
 */

@Configuration
@EnableRabbit
public class RabbitProducerConfig {


	 
    @Value("${spring.rabbitmq.host}")
    private String host;
 
    @Value("${spring.rabbitmq.port}")
    private int port;
 
    @Value("${spring.rabbitmq.username}")
    private String username;
 
    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHosthost;

    @Bean
    public ConnectionFactory connectionFactory() {
      
    	CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHosthost);
        return connectionFactory;
    }
 
	@Bean(name="tt")//和kafka的配置仓库类一莫一样很牛逼的
	public RabbitListenerContainerFactory<SimpleMessageListenerContainer> tt(){
		//ConcurrentKafkaListenerContainerFactory

		//消息的统一过滤器
		MessageConverter messageConverter = new ObjConsert();
		SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
		factory.setConcurrentConsumers(5);//允许同时消费数量为5
		factory.setMaxConcurrentConsumers(10);//允许同时最大消费数量为10
		factory.setReceiveTimeout(10000L);//10秒
	    factory.setMessageConverter(messageConverter);//具体的逻辑要自己在ObjConsert里面写
		factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);//设置手动提交
		factory.setConnectionFactory(connectionFactory());
	    return  factory;
	}
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory());
      //template.setDefaultReceiveQueue(queue);//设置默认接收队列
        return template;
    }

    @Bean(name="defauilTemplate")
    public RabbitTemplate rabbitTemplateDefault() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory());
      template.setDefaultReceiveQueue("fastSending");//设置默认接收队列
        return template;
    }

}

上文中的SimpleRabbitListenerContainerFactory 是笔者看了kafka的类似源码,写的,很强大,可以扩展N多的东西

/***
 * 消费者异常处理器()
 * 有时候我们使用的时候,可能是一个异常类对应一个死信队列
 * @author 刘新杨
 *   菩提本无树,
 *   明镜亦非台。
 */
@Service(value="RabbitConsumerListenerErrorHandler")//使用的时候可以直接在@rabbitMQ中errorHandler设置名字即可
public class RabbitConsumerListenerErrorHandler implements RabbitListenerErrorHandler {

	@Override
	public Object handleError(Message amqpMessage, org.springframework.messaging.Message<?> message,
			ListenerExecutionFailedException exception) throws Exception {
	
		System.out.println("消费失败的异常是:"+amqpMessage.getMessageProperties().getConsumerQueue());
		return "此处应该做消费重新消费,或者发送到死信交换机";
	}

上图中可以为单一消费者服务,做重发消息的机制,也可以通过记录日志的形式后续做补偿。

package com.te.mm.listener;

import java.util.List;
import java.util.Map;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;
import com.te.mm.Entity.Order;

/**
 * 正常情况下消费者队列应该在其他项目中,这里只做演示所以就放在了在这里
 * 
 * @author Administrator
 *
 */
@Component
public class RabbitReceiver {

	// SimpleRabbitListenerContainerFactory此类可以自定义一些配置详细请点击

	// @RabbitListener(bindings = @QueueBinding(value = @Queue(value =
	// "${spring.rabbitmq.listener.order.queue.name}", durable =
	// "${spring.rabbitmq.listener.order.queue.durable}"), exchange =
	// @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}", durable
	// = "${spring.rabbitmq.listener.order.exchange.durable}", type =
	// "${spring.rabbitmq.listener.order.exchange.type}",
	// ignoreDeclarationExceptions =
	// "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
	// key = "${spring.rabbitmq.listener.order.key}"),containerFactory="tt")
	// @RabbitHandler
	// public void onMessage(Message message, Channel channel) throws Exception {
	//
	//
	// System.out.println("*******");
	// System.out.println("消息体内容:" + message.getPayload());
	// long deTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
	// channel.basicAck(deTag, false);
	// }

	@RabbitListener(containerFactory = "tt", errorHandler = "RabbitConsumerListenerErrorHandler", queues = "TestDirectQueue")
	@RabbitHandler // 此注解加上之后可以接受对象型消息
	public void onOrderMessage(@Payload List<Order> orders, Channel channel, @Headers Map<String, Object> heads)
			throws Exception {

		for (Order order : orders) {
			System.out.println(order);
		}
		// 此处也可以带messagid等信息作为唯一标识来确保消息的幂等操作
		/**
		 * 做幂等操作可用redis或者DB来存储唯一标识符,每次消费前
		 * 先查询是否消费了,如果没有消费就在消费逻辑。
		 */
		System.out.println("消息唯一标识符:" + heads.get("number"));
	
		long deTag = (Long) heads.get(AmqpHeaders.DELIVERY_TAG);
		System.out.println(deTag);
		// 告诉服务器,已经消费成功,
		channel.basicAck(deTag, false);
	}

	@RabbitListener(containerFactory="tt", errorHandler = "RabbitConsumerListenerErrorHandler", queues = "dead_queue")
	@RabbitHandler
	public void onDeadMessage2(Message message, Channel channel, @Headers Map<String, Object> heads) throws Exception {

		System.out.println("死信队列message"+new String(message.getBody()));
		long deTag = (Long) heads.get(AmqpHeaders.DELIVERY_TAG);
	
			// 正常业务逻辑,当出现异常的时候,走死信队列重新消费。

			System.out.println(deTag);
			// 告诉服务器,已经消费成功,
			channel.basicAck(deTag, false);		 
	}
	
	@RabbitListener(containerFactory="tt", errorHandler = "RabbitConsumerListenerErrorHandler", queues = "test_queue_1")
	@RabbitHandler
	public void delayMessage(Message message, Channel channel, @Headers Map<String, Object> heads) throws Exception {

		System.out.println("分发队列message"+new String(message.getBody()));
		long deTag = (Long) heads.get(AmqpHeaders.DELIVERY_TAG);
	
			// 正常业务逻辑,当出现异常的时候,走死信队列重新消费。

			System.out.println(deTag);
			// 告诉服务器,已经消费成功,
			channel.basicAck(deTag, false);		 
	}
	@RabbitListener(containerFactory="tt", errorHandler = "RabbitConsumerListenerErrorHandler", queues = "TestFanoutQueue")
	@RabbitHandler
	public void faountMessage(Message message, Channel channel, @Headers Map<String, Object> heads) throws Exception {

		System.out.println("延时队列message"+new String(message.getBody()));
		long deTag = (Long) heads.get(AmqpHeaders.DELIVERY_TAG);
	
			// 正常业务逻辑,当出现异常的时候,走死信队列重新消费。

			System.out.println(deTag);
			// 告诉服务器,已经消费成功,
			channel.basicAck(deTag, false);		 
	}
	@RabbitListener(containerFactory="tt", errorHandler = "RabbitConsumerListenerErrorHandler", queues = "TestFanoutQueue2")
	@RabbitHandler
	public void faount2Message(Message message, Channel channel, @Headers Map<String, Object> heads) throws Exception {

		System.out.println("分发队列message"+new String(message.getBody()));
		long deTag = (Long) heads.get(AmqpHeaders.DELIVERY_TAG);
	
			// 正常业务逻辑,当出现异常的时候,走死信队列重新消费。

			System.out.println(deTag);
			// 告诉服务器,已经消费成功,
			channel.basicAck(deTag, false);		
			/**
			 * 其实此处可以使用basicNack,那么就不需要在添加消息的过期时间了。
			 * 下面的代码可以实现,当业务逻辑出现异常的时候走死信交换机,重新消费。
			 * 或者第三个参数设置为true则会重新消费(要注意幂等性,如果只有一个消费者那么
			 * 说明消费者代码逻辑有误,重新消费无多大意义)
			 */
//			try {
//				System.err.println("业务逻辑");
//			} catch (Exception e) {
//				
			//channel.basicNack(deTag, false,false);		
//			}
	}
}

上图为消费者的核心代码

下面我们详细讲一下相关注解

@Payload 加上之后spring会把message格式的数据自动转换为此注解后面跟着的对象

@RabbitListener 核心消费注解,参数如下:

String id() borker中的唯一标识。

containerFactory() 里面配置消息是否自动确认,超时时间,最大消费数量,第一张图说明

String[] queues  可以写一个队列,可以写多个队列{“queue1”,"queue2"}

Queue[] queuesToDeclare()  可以声明队列使用的 @Queue("里面是队列的名字,持久化等参数")

    QueueBinding[] bindings() default {} 可以使用QueueBinding(注解在实现消费者队列创建,交换机创建,绑定创建,参数设置等)

    String priority()  这种队列的优先级,数字越大,优先级越高

String group() default ""; 具体没用过,不知道和kafka的组概念是否相同

String errorHandler() 自己定义的消费者异常处理器

大致消费者端重要的参数就那么多。

        源码地址https://github.com/LxyTe/Study/tree/master/TechnicalIntegration

                                  -------------------本文知识储备均来自于蚂蚁课堂,感谢余总的指点

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值