一、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、配置服务
#配置rabbitmq的安装地址、端口以及账户信息
#注意port 15672是管理端的端口
spring.rabbitmq.host=192.168.184.134
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
三、编写生产者
package com.host.mq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ProductMessage implements RabbitTemplate.ConfirmCallback {
private static final Logger LOGGER = LoggerFactory.getLogger(ProductMessage.class);
@Autowired
private RabbitTemplate rabbitTemplate;
private static final String EXCHANG = "test_exchang";
private static final String ROUNTING_KEY = "meng_key";
public void send() {
//rabbitTemplate.convertAndSend(EXCHANG, ROUNTING_KEY, "hello world");
String str = "hellow woo";
MessageProperties properties = new MessageProperties();
Message message = new Message(str.getBytes(), properties);
rabbitTemplate.send(EXCHANG, ROUNTING_KEY, message);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
LOGGER.info("=====correlationData= " + correlationData);
if (ack) {
LOGGER.info("消息确认成功:");
} else {
LOGGER.info("消息确认失败:" + cause);
}
}
}
四、编写消费者
这里通过@RabbitListener注解在方法上来监听消息,并通过路由ke绑定exchange和queue。@RabbitListener可以在类和方法中使用,在类中注解@RabbitListener的话,方法中必须添加@RabbitHandler,才会生效。
如果消费者使用public void process(Message message)接收消息,那么@RabbitListener必须注解在方法上。
package com.host.mq;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class ConsumerMessage {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerMessage.class);
@RabbitListener(bindings = {@QueueBinding(value = @Queue("testqueue"),exchange = @Exchange(name = "test_exchang"),
key="meng_key")})
public void process(Message message) {
LOGGER.info("========ConsumerMessage=====" + new String(message.getBody()));
}
}
执行程序发送消息,下面日志显示接收成功
2020-03-01 21:01:37.608 INFO 2740 --- [io-10020-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2020-03-01 21:01:37.608 INFO 2740 --- [io-10020-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
2020-03-01 21:01:37.633 INFO 2740 --- [io-10020-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 25 ms
2020-03-01 21:01:37.743 INFO 2740 --- [cTaskExecutor-1] com.host.mq.ConsumerMessage : ========ConsumerMessage=====(Body:'hello world' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=test_exchang, receivedRoutingKey=meng_key, deliveryTag=1, consumerTag=amq.ctag-kw_1CoA72CT7z23K9oThCQ, consumerQueue=testqueue])
2020-03-01 21:01:37.743 INFO 2740 --- [cTaskExecutor-1] com.host.mq.ConsumerMessage : ========ConsumerMessage=====hello world
五、手动确认机制
手动确认在spring-boot中配置方法:
spring.rabbitmq.listener.simple.acknowledge-mode = manual
1、消费成功手动确认方法:
void basicAck(long deliveryTag, boolean multiple) throws IOException;
deliveryTag:该消息的index
multiple:是否批量确认。true:将一次性ack所有小于deliveryTag的消息。
消费者成功处理消息后,手动调用channel.basicAck(message.getMessageProperties().getDeliveryTag(), false)方法对消息进行消费确认。
@RabbitListener(bindings = {@QueueBinding(value = @Queue("testqueue"),exchange = @Exchange(name = "test_exchang"),
key="meng_key")})
public void process(Message message, Channel channel) {
LOGGER.info("========ConsumerMessage=====" + new String(message.getBody()));
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException;
deliveryTag:该消息的index。
multiple:是否批量. true:将一次性拒绝所有小于deliveryTag的消息。
requeue:被拒绝的是否重新入队列。
void basicReject(long deliveryTag, boolean requeue) throws IOException;
deliveryTag:该消息的index。
requeue:被拒绝的是否重新入队列。
channel.basicNack 方法与 channel.basicReject 方法区别在于basicNack可以批量拒绝多条消息,而basicReject一次只能拒绝一条消息。
如果消费端既没有设置basicAck,也没有设置basicNack 。这是消息会一直在Unacked中,如果消费者服务器断开后,消息回到Ready中等待重新消费。当消费端服务器重新启动后,会再次消费这条消息。