springboot 集成 rabbitmq 非常方便,只需引入一个依赖包,交换机队列等都可以直接通过注释完成。
我新建了两个项目,pom 文件是一样的,如下:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
我一开始没有引入 web 依赖,但是用测试用例的时候,我执行测试方法时,报错说连接已经关闭了。因此,我是启动了整个项目,写了 controller 测试成功。
配置文件 application.properties
spring.rabbitmq.addresses=192.168.0.125
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.template.mandatory=true
需一个配置类,现在没写什么配置,但是得加上扫描注解
@Configuration
@ComponentScan({"com.xiao.*"})
public class MainConfig {
}
消息发送类
import java.util.Map;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
import com.xiao.entity.LetterBody;
@Component
public class SendMessage {
@Autowired
private RabbitTemplate rabbitTemplate;
// 回调方法--确认机制
final ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("correlationData==" + correlationData);
System.out.println("ack==" + ack);
System.out.println(cause);
if(!ack) {
System.out.println("ack 失败!请重新操作。。。。");
}
}
};
// 回调方法--return 机制
final ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
String exchange, String routingKey) {
System.out.println("returnCallback: exchange==" + exchange + ", routingKey==" + routingKey
+ ", replyCode==" + replyCode + ", replyText=="+ replyText);
}
};
// 发送 Object 消息对象
public void send(Object message, Map<String, Object> properties) {
MessageHeaders headers = new MessageHeaders(properties);
Message msg = MessageBuilder.createMessage(message, headers);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
CorrelationData correlationData = new CorrelationData();
correlationData.setId("123asdfg321"); // 须全局唯一,为消息的实际id
rabbitTemplate.convertAndSend("exchange-boot", "boot.test", msg, correlationData);// 预先创建好的交换机和队列
}
//发送 java 类对象
public void sendLetter(LetterBody letter) {
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
CorrelationData correlationData = new CorrelationData();
correlationData.setId("1234asdfg4321"); // 须全局唯一
rabbitTemplate.convertAndSend("exchange-boot", "boot.test", letter, correlationData);// 预先创建好的交换机和队列
}
}
消息发送类中第二种消息类型是一个自定义 java 对象,须自定义一个实体对象,并实现序列化
public class LetterBody implements Serializable {
private String lid;
private String lcontent;
public LetterBody() {
}
public LetterBody(String lid, String lcontent) {
super();
this.lid = lid;
this.lcontent = lcontent;
}
public String getLid() {
return lid;
}
public void setLid(String lid) {
this.lid = lid;
}
public String getLcontent() {
return lcontent;
}
public void setLcontent(String lcontent) {
this.lcontent = lcontent;
}
}
测试类 or 控制类
@RestController
public class SendController {
@Autowired
private SendMessage sendMessage;
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@RequestMapping("/send")
public String testSendMessage() {
HashMap<String, Object> properties = new HashMap<>();
properties.put("mid", "123456789");
properties.put("createTime", sdf.format(new Date()));
sendMessage.send("boot send message!!", properties);
return "over";
}
@RequestMapping("/sendObj")
public String testSendLetter() {
LetterBody letterBody = new LetterBody("toremote001","给远方的一封信");
sendMessage.sendLetter(letterBody);
return "over";
}
}
写成测试类只要改相应的注解即可
执行测试方法 or 主入口类,如下图
下图是执行测试方法,在ack之前,连接关闭了,导致 ack 失败
下图是启动了项目,ack 成功
把交换机名称或队列名称或路由键改成不存在的,即可看到回调了 return 方法
配置文件 application.properties
spring.rabbitmq.addresses=192.168.0.125
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.connection-timeout=30000
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=5
spring.rabbitmq.listener.simple.max-concurrency=15
spring.rabbitmq.listener.letterBody.queue.name=queue-boot
spring.rabbitmq.listener.letterBody.queue.durable=true
spring.rabbitmq.listener.letterBody.exchange.name=exchange-boot
spring.rabbitmq.listener.letterBody.exchange.durable=true
spring.rabbitmq.listener.letterBody.exchange.type=topic
spring.rabbitmq.listener.letterBody.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.letterBody.key=boot.#
同样需要一个配置类,加上扫描包,同上。
接下来是消息接收类
import java.util.Map;
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.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.Message;
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.xiao.entity.LetterBody;
@Component
public class ReceiveMessage {
//以下队列和交换机如果不存在,会自动创建
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "queue-boot", durable = "true"),
exchange = @Exchange(value = "exchange-boot", type = "topic",
durable = "true", ignoreDeclarationExceptions = "true"),
key = "boot.#"
)
)
@RabbitHandler
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println("---------------Message---------------");
System.out.println("consumer payload===" + message.getPayload());
Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
// 手工 ack
channel.basicAck(deliveryTag, false);
}
/**
* spring.rabbitmq.listener.letterBody.queue.name=queue-boot
spring.rabbitmq.listener.letterBody.queue.durable=true
spring.rabbitmq.listener.letterBody.exchange.name=exchange-boot
spring.rabbitmq.listener.letterBody.exchange.durable=true
spring.rabbitmq.listener.letterBody.exchange.type=topic
spring.rabbitmq.listener.letterBody.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.letterBody.key=boot.#
* @param letter
* @param channel
* @param heads
* @throws Exception
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "${spring.rabbitmq.listener.letterBody.queue.name}",
durable = "${spring.rabbitmq.listener.letterBody.queue.durable}"),
exchange = @Exchange(value = "${spring.rabbitmq.listener.letterBody.exchange.name}",
type = "${spring.rabbitmq.listener.letterBody.exchange.type}",
durable = "${spring.rabbitmq.listener.letterBody.exchange.durable}",
ignoreDeclarationExceptions = "${spring.rabbitmq.listener.letterBody.exchange.ignoreDeclarationExceptions}"),
key = "${spring.rabbitmq.listener.letterBody.key}")
)
@RabbitHandler
public void onLetterMessage(@Payload LetterBody letter, Channel channel,
@Headers Map<String, Object> heads ) throws Exception {
System.out.println("---------------Letter Message---------------");
System.out.println("consumer Letter Message===" + letter.getLid() + "-->" + letter.getLcontent());
Long deliveryTag = (Long)heads.get(AmqpHeaders.DELIVERY_TAG);
// 手工 ack
channel.basicAck(deliveryTag, false);
}
}
测试发送的 java 对象消息,同样需要实体类,同上