RabbitMQ学习之路 - 消费者并发配置及消息手动确认机制

最近在系统学习RabbitMQ的实战课程,感觉很受用,总结一下以供参考及分享
搭建spring boot项目,引入rabbitmq依赖。这里不再赘述
一、application.properties配置

server.port=8730
server.servlet.context-path=/mq

#rabbitmq配置
spring.rabbitmq.virtual-host=/
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

spring.rabbitmq.listener.concurrency=10
spring.rabbitmq.listener.max-concurrency=20
spring.rabbitmq.listener.prefetch=5

mq.env=local

simple.mq.queue.name=${mq.env}.simple.mq.queue
simple.mq.exchange.name=${mq.env}.simple.mq.exchange
simple.mq.routing.key.name=${mq.env}.simple.mq.routing.key

二、创建RabbitMQConfig配置类

package com.jvli.project.config;

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import com.jvli.project.rabbitmqlistener.SimpleListener;

@Configuration
public class RabbitMqConfig {

	private static final Logger logger = LoggerFactory.getLogger(RabbitMqConfig.class);

	@Autowired
	private Environment env;

	@Autowired
	private CachingConnectionFactory connectionFactory;

	@Autowired
	private SimpleRabbitListenerContainerFactoryConfigurer factoryConfigurer;

	@Autowired
	private SimpleListener simpleListener;

	@Bean
	public RabbitTemplate rabbitTemplate() {
		connectionFactory.setPublisherConfirms(true);
		connectionFactory.setPublisherReturns(true);
		RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
		rabbitTemplate.setMandatory(true);
		rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

			@Override
			public void confirm(CorrelationData correlationData, boolean ack, String cause) {
				logger.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause);

			}
		});
		rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {

			@Override
			public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
					String routingKey) {
				logger.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey,
						replyCode, replyText, message);
			}
		});
		return rabbitTemplate;
	}

	// TODO: 并发配置-消息手动确认机制
	// 1. 创建消息模型
	@Bean(name = "simpleQueue")
	public Queue simpleQueue() {
		return new Queue(env.getProperty("simple.mq.queue.name"), true); // true表示持久化
	}

	@Bean
	public TopicExchange simpleExchange() {
		return new TopicExchange(env.getProperty("simple.mq.exchange.name"), true, false); // true表示持久化,false表示不自动删除
	}

	@Bean
	public Binding simpleBinding() {
		return BindingBuilder.bind(simpleQueue()).to(simpleExchange())
				.with(env.getProperty("simple.mq.routing.key.name"));
	}
	
	// 2.创建并发、手动确认监听容器
	@Bean(name = "simpleContainer")
	public SimpleMessageListenerContainer simpleContainer(@Qualifier("simpleQueue") Queue simpleQueue) {
		SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
		container.setConnectionFactory(connectionFactory);
		//container.setMessageConverter(new Jackson2JsonMessageConverter());
		// TODO:并发配置
		container.setConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.concurrency", Integer.class));
		container.setMaxConcurrentConsumers(env.getProperty("spring.rabbitmq.listener.max-concurrency", Integer.class));
		container.setPrefetchCount(env.getProperty("spring.rabbitmq.listener.prefetch", Integer.class));

		// TODO:消息确认-确认机制种类
		container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
		container.setQueues(simpleQueue);
		container.setMessageListener(simpleListener);
		return container;
	}

}

三、创建消费监听

package com.jvli.project.rabbitmqlistener;

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;

@Component("simpleListener")
public class SimpleListener implements ChannelAwareMessageListener, MessageListener{

	private static final Logger logger = LoggerFactory.getLogger(SimpleListener.class);
	
	@Autowired
	private ObjectMapper objectMapper;
	
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		long tag = message.getMessageProperties().getDeliveryTag();
		
		try {
			byte[] body = message.getBody();
	        //User user=objectMapper.readValue(body,User.class);
			String msg = new String(body,"UTF-8");
			logger.info("当前线程:{},简单消息监听确认机制监听到消息:{}",Thread.currentThread().getName(),msg);
			
			//int i=1/0;  测试抛出异常
			
			//手动确认
			channel.basicAck(tag, true);
		} catch (Exception e) {
			logger.error("简单消息监听确认机制发生异常:{}",e.fillInStackTrace());
			channel.basicReject(tag, false);
		}
		
	}

}

四、创建Controller,触发事件

package com.jvli.project.controller;

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jvli.project.dto.User;
import com.jvli.project.response.BaseResponse;
import com.jvli.project.response.StatusCode;


@RestController
@RequestMapping("/ack")
public class AcknowledgeController {

	private static final Logger logger = LoggerFactory.getLogger(AcknowledgeController.class);
	
	@Autowired
	private RabbitTemplate rabbitTemplate;
	
	@Autowired
	private ObjectMapper objectMapper;
	
	@Autowired
	private Environment env;
	
	@RequestMapping(value = "/user/info",method = RequestMethod.GET)
	public BaseResponse ackUser() {
	
		try {
			rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
			rabbitTemplate.setExchange(env.getProperty("simple.mq.exchange.name"));
			rabbitTemplate.setRoutingKey(env.getProperty("simple.mq.routing.key.name"));
			
			for (int i = 1; i <= 50; i++) {
				
				User user = new User(i, "army", "acknowledge");
				Message message = MessageBuilder.withBody(objectMapper.writeValueAsBytes(user)).setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
				rabbitTemplate.convertAndSend(message);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new BaseResponse<>(StatusCode.SUCCESS);
	}
	
}

测试效果:

  1. 启动项目之后,登录RabbitMQ 可视化客户端,可以看到我们创建的队列确实对应了10个消费者
    在这里插入图片描述
    在这里插入图片描述
    2.debug请求接口测试
    在SimpleListener消费端打断点
    在这里插入图片描述
    此时,我们再来观察RabbitMQ的客户端,待确认的消息有50条
    在这里插入图片描述
    然后,我们放行,观察RabbitMQ客户端和开发工具的控制台。确实有10个消费者在消费,并且每次拉取5条消息(container.setPrefetchCount()属性)
    在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值