【RabbitMQ】基于RabbitMQ来实现基本的消息生产与消费

在本文中直接进行介绍消息的生产和消费过程;
工程的开发基于Springboot和RabbitMQ
RabbitMQ交换机的模式为:Direct Mode


在进行消息生产和消费之前需要在SpringBoot工程的pom.xml文件中,引入如下所示的依赖:

		<!-- rabbitmq依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

1、消息生产端

1.1、配置文件

在application.properties文件中进行相关的配置,具体配置内容如下:

server.port=8889

# RabbitMQ producer configuration
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
# set timeout
spring.rabbitmq.connection-timeout=15000

# RabbitMQ 生产端消息确认机制设置
# 确认消息已经发送到队列中
spring.rabbitmq.publisher-returns=true
# 确认消息已经发送到交换机中
spring.rabbitmq.publisher-confirm-type=correlated

# 热部署
spring.devtools.restart.enabled=true
1.2、交换机、消息队列配置文件

在RabbitMQConfig.java文件中进行交换机、队列的初始化配置,如下所示:

@Configuration
public class RabbitMQConfig {
	private static final String EXCHANGE_NAME = "exchange_demo";
	private static final String QUEUE_NAME = "queue_demo";
	private static final String ROUTE_KEY = "routeKey_demo";
	@Bean
	public DirectExchange createDirectExchange() {
		return new DirectExchange(EXCHANGE_NAME, true, false);
	}
	//name:交换机名称
	//durable: 是否持久化
	//autoDelete: 有过消费者,并且所有的消费者都取消订阅则自动删除
	//DirectExchange(String name, boolean durable, boolean autoDelete)

	@Bean
	public Queue createQueue() {
		return new Queue(QUEUE_NAME, true, false, false, null);
	}
	// name: 队列名称
	// durable: 是否持久化
	// exclusive: 是否只能在本次的连接中访问,一般为false
	// autoDelete: 没有消费者的时候是否自动删除
	// arguments: 参数配置
	//Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)

	@Bean
	public Binding createBinding() {
		return BindingBuilder.bind(createQueue()).to(createDirectExchange()).with(ROUTE_KEY);
	}
}
1.3、消息的发送

在service层中进行消息的发送操作:

@Service
public class MessageService implements IMessageService {
	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Override
	public void sendMessage(Message message) {
		String msgId = UUID.randomUUID().toString();
		// CorrelationData :data to correlate publisher confirms. (在进行消息确认时必须要有)
		CorrelationData correlationData = new CorrelationData(msgId);
		rabbitTemplate.convertAndSend("exchange_demo", "routeKey_demo", message, correlationData);
		System.out.println("Send Message       :消息已经发出!");
	}
}
1.4、消息实体类
@Data
public class Message implements Serializable {
	private static final long serialVersionUID = 1L;

	private String id;
	private String msgData;
}
1.5、生产端消息确认

实现生产端的消息确认需要添加如下配置文件,在application.properties中的配置入1.1所示;

@Configuration
public class MessageAckConfig {

	@Autowired
	private ConnectionFactory connectionFactory;

	@Bean
	public RabbitTemplate createRabbitTemplate() {
		RabbitTemplate rabbitTemplate = new RabbitTemplate();
		rabbitTemplate.setConnectionFactory(connectionFactory);
		/**
		 * 当mandatory标志位设置为true时 如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息
		 * 那么broker会调用basic.return方法将消息返还给生产者 当mandatory设置为false时,出现上述情况broker会直接将消息丢弃
		 * (必须进行配置,否则不能调用setReturnCallback方法)
		 */
		rabbitTemplate.setMandatory(true);
		// 消息确认,一般消息成功与否都会触发该方法
		rabbitTemplate.setConfirmCallback(new ConfirmCallback() {
			@Override
			public void confirm(CorrelationData correlationData, boolean ack, String cause) {
				System.err.println("ConfirmCallback     相关数据:" + correlationData);
				System.err.println("ConfirmCallback     确认情况:" + ack);
				System.err.println("ConfirmCallback     原因:" + cause);
			}
		});
		// 消息已经推送到交换机但是找不到队列,就会触发ReturnCallback,进行消息的回退
		rabbitTemplate.setReturnCallback(new ReturnCallback() {
			@Override
			public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
					String routingKey) {
				System.err.println("ReturnCallback:     " + "消息:" + message);
				System.err.println("ReturnCallback:     " + "回应码:" + replyCode);
				System.err.println("ReturnCallback:     " + "回应信息:" + replyText);
				System.err.println("ReturnCallback:     " + "交换机:" + exchange);
				System.err.println("ReturnCallback:     " + "路由键:" + routingKey);
			}
		});
		return rabbitTemplate;
	}
}

参考博客:https://blog.csdn.net/qq_35387940/article/details/100514134

消息成功发送到队列上:

ConfirmCallback     相关数据:CorrelationData [id=cf378f0b-2ad9-499d-bc21-a18e07e5db26]
ConfirmCallback     确认情况:true
ConfirmCallback     原因:null

消息发送到broker时,找到exchange,而没有找到queue:

ReturnCallback:     消息:(Body:'[B@1b6abdf6(byte[111])' MessageProperties [headers={spring_returned_message_correlation=fe5a3286-0a31-41b2-9dc2-5c4ba9bd2d6e}, contentType=application/x-java-serialized-object, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
ReturnCallback:     回应码:312
ReturnCallback:     回应信息:NO_ROUTE
ReturnCallback:     交换机:new_exchange
ReturnCallback:     路由键:routeKey_demo

ConfirmCallback     相关数据:CorrelationData [id=fe5a3286-0a31-41b2-9dc2-5c4ba9bd2d6e]
ConfirmCallback     确认情况:true
ConfirmCallback     原因:null
1.6、死信队列

实现死信队列只需在队列初始化时,在参数中添加一些配置,然后初始化一个死信队列即可;对1.2中的配置文件进行更改:

@Configuration
public class RabbitMQConfig {
	private static final String EXCHANGE_NAME = "exchange_demo";
	private static final String QUEUE_NAME = "queue_demo";
	private static final String ROUTE_KEY = "routeKey_demo";
	// Dead Letter
	private static final String EXCHANGE_DL_NAME = "exchange_dl_timeout";
	private static final String QUEUE_DL_NAME = "queue_dl_timeout";
	private static final String ROUTE_KEY_DL = "routeKey_dl_timeout";
	@Bean
	public DirectExchange createDirectExchange() {
		return new DirectExchange(EXCHANGE_NAME, true, false);
	}

	@Bean
	public Queue createQueue() {
		Map<String, Object> map = new HashMap<String, Object>();
		// Set max-message-length of queue.
		map.put("x-max-length", 30);
		// Set time-to-live of all of messages in queue.(unit:ms)
		map.put("x-message-ttl", 6000);
		// Set dead-letter-exchange of queue.
		map.put("x-dead-letter-exchange", EXCHANGE_DL_NAME);
		// 必须配置路由,否则消息不会切换到死信交换器
		// Set dead-letter-routing-key
		map.put("x-dead-letter-routing-key", ROUTE_KEY_DL);
		return new Queue(QUEUE_NAME, true, false, false, map);
	}

	@Bean
	public Binding createBinding() {
		return BindingBuilder.bind(createQueue()).to(createDirectExchange()).with(ROUTE_KEY);
	}

	/*********** Dead letter **************/
	@Bean
	public DirectExchange createDLDirectExchange() {
		return new DirectExchange(EXCHANGE_DL_NAME, true, false);
	}

	// Create dead letter queue.
	@Bean
	public Queue createDLQueue() {
		return new Queue(QUEUE_DL_NAME, true, false, false, null);
	}

	@Bean
	public Binding createDLBinding() {
		return BindingBuilder.bind(createDLQueue()).to(createDLDirectExchange()).with(ROUTE_KEY_DL);
	}
}

死信队列的消费端与一般的没什么区别,本文就不赘述了。

2、消息消费端

2.1、消费端配置文件
server.port=8888

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/

# 初始化的消费者数量
spring.rabbitmq.listener.simple.concurrency=5
# 最大的消费者数量
spring.rabbitmq.listener.simple.max-concurrency=10
# 每个消息费者每次处理的信息个数
spring.rabbitmq.listener.simple.prefetch=1

# 如果是属性配置的形式需要将以下两个都设置为manual模式,否则,就会出现unknown delivery tag 1的错误
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual

# 热部署
spring.devtools.restart.enabled=true
2.2、配置文件及实现消费及消息确认

实现消费;

@Configuration
@Slf4j
public class MQListenerConfig {

	/**
	 * @param message 消息内容
	 * @param channel
	 * @param headers
	 * @throws IOException
	 */
	@RabbitListener(bindings = @QueueBinding(//
			exchange = @Exchange("exchange_demo"), //
			value = @Queue("queue_demo"), //
			key = "routeKey_demo"//
	))
	@RabbitHandler
	public void receiveMessage(@Payload Message message, Channel channel, @Headers Map<String, Object> headers)
			throws IOException {
		System.err.println(message);
		long deliveryTag = (long) headers.get(AmqpHeaders.DELIVERY_TAG);

		/********** 消费端的消息确认 ************/
		// 消息确认,false:只是对当前信息的确认
		try {
			channel.basicAck(deliveryTag, false);
		} catch (IOException e) {
			log.error("MQListenerConfig exception message" + e.toString());
			channel.basicNack(deliveryTag, false, true);
		}
		// 重新将消息放回到队列中;true:重新将消息放回到消息队列中
		// channel.basicNack(deliveryTag, false, true);
		// 拒绝消息; true:重新将消息放回到消息队列中
		// channel.basicReject(deliveryTag, true);
	}
}
发布了66 篇原创文章 · 获赞 6 · 访问量 9415

猜你喜欢

转载自blog.csdn.net/qgnczmnmn/article/details/103368873
今日推荐