org.springframework.boot
spring-boot-starter-amqp
RabbitAutoConfiguration:
AmqpAdmin:
Exchange接口:从名字就可知是什么交换机(DirectExchange、FanoutExchange、CustomExchange、TopicExchange、HeadersExchange)
RabbitMQ基本使用_GoldenOakLibrarian的博客-CSDN博客关于DirectExchange、FanoutExchange、TopicExchange三种交换机的简要介绍可以转到RabbitMQ基本使用_GoldenOakLibrarian的博客-CSDN博客
DirectExchange的全参构造器
public DirectExchange(String name, //为交换机起的名称
boolean durable, //是否持久化(一般填yes)
boolean autoDelete, //是否自动删除(一般填no)
Map arguments) //携带参数
{
super(name, durable, autoDelete, arguments);
}
创建交换机
@Test
public void createExchange(){
DirectExchange directExchange = new DirectExchange("first-java-exchange",
true,false);
amqpAdmin.declareExchange(directExchange);
}
创建成功(在 http://虚拟机地址/#/exchanges 中查看)
Queue的全参构造器(与交换机类似)
public Queue(String name,
boolean durable,
boolean exclusive,
boolean autoDelete,
@Nullable Map arguments)
创建队列
public void createQueue(){
Queue queue = new Queue("first-java-queue",true,false,false);
amqpAdmin.declareQueue(queue);
}
创建成功
Binding 全参构造器
public Binding(String destination, //目的地(队列或交换机名称)
Binding.DestinationType destinationType, //目的地类型(队列或交换机)
String exchange, //交换机
String routingKey, //路由键
@Nullable Map arguments //参数
)
创建Binding
@Test
public void createBinding(){
Binding binding = new Binding("first-java-queue",
Binding.DestinationType.QUEUE,
"first-java-exchange",
"first.java",
null);
amqpAdmin.declareBinding(binding);
}
@Autowired RabbitTemplate
调用rabbitTemplate.convertAndSend(name,routingKey,string)
@Test
public void sendMessage(){
rabbitTemplate.convertAndSend("first-java-exchange",
"first.java","First Test.");
}
接收成功(在 http://虚拟机地址/#/queues中查看)
@ToString
@Data
public class TestEntity implements Serializable { //注意这里的序列化
Integer id;
String message;
String name;
}
调用rabbitTemplate.convertAndSend(name,routingKey,object)
@Test
public void sendMessage(){
TestEntity testEntity = new TestEntity();
testEntity.setMessage("Hello");
testEntity.setName("June");
testEntity.setId(1);
rabbitTemplate.convertAndSend("first-java-exchange","first.java",testEntity);
}
接收成功(在 http://虚拟机地址/#/queues中查看)
接收结果
Rabbit 配置消息转换器
@Configuration
public class MyRabbitConfig {
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter(); //配置消息转换器
}
}
1、只要收到消息,队列就删除消息,且只能有一个客户端收到消息。默认轮询接收消息。
2、只有一个消息完全处理完,才可以接受到下一个消息。
下图为标在方法上的情况:
//Message:原生消息详细信息
//T:发送消息的类型
//queues:需要监听的所有队列
@RabbitListener(queues = {"first-java-queue"})
public void recieveMessage(Message message, TestEntity content){
byte[] body = message.getBody();
MessageProperties properties = message.getMessageProperties();
System.out.println("接收到:"+message+"内容:"+content);
}
接收到:(Body:'{"id":1,"message":"Hello","name":"June"}' MessageProperties [headers=
{__TypeId__=com.atguigu.gulimall.order.entity.TestEntity}, contentType=application/json,
contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=first-java-exchange, receivedRoutingKey=first.java, deliveryTag=1, consumerTag=amq.ctag-PMVlLW3jcyurz4gpZF1gWA, consumerQueue=first-java-queue])
内容:TestEntity(id=1, message=Hello, name=June)
注意发送的路由键相同,但发送的类不同
@Test
public void sendMessage(){
TestEntity testEntity = new TestEntity();
TestEntity2 testEntity2 = new TestEntity2();
for(int i=0;i<10;i++){
if(i%2==0){
testEntity.setMessage("Hello"+i);
testEntity.setName("June");
testEntity.setId(1);
rabbitTemplate.convertAndSend("first-java-exchange","first.java",testEntity);
}else{
testEntity2.setType("num2"+i);
testEntity2.setName("June");
testEntity2.setId(1);
rabbitTemplate.convertAndSend("first-java-exchange","first.java",testEntity2);
}
}
}
注意接收的队列相同,但接收的类不同 (可以分别接收到对应类的消息)
@RabbitListener(queues = {"first-java-queue"})
public class recieveMessage {
@RabbitHandler
public void recieveMessage1(Message message,
TestEntity content,
Channel channel){
System.out.println("内容:"+content);
}
@RabbitHandler
public void recieveMessage2(TestEntity2 content){
System.out.println("内容:"+content);
}
}
在 MyRabbitConfig 中进行如下配置:
@PostConstruct (标志着会在MyRabbitConfig创建后调用)
initRabbitTemplate(){
rabbitTemplate.setConfirmCallback...}
@Configuration
public class MyRabbitConfig {
@Autowired
RabbitTemplate rabbitTemplate;
@PostConstruct //MyRabbitConfig创建完成后调用
public void initRabbitTemplate(){
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 消息唯一id
* @param b 成功或失败:只要成功发送了消息,不管是否接受,就为true
* @param s 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("correlationData:"+correlationData+" success:"+b+
" reason:"+s);
}
});
}
}
成功接收后 打印如下结果
correlationData:null success:true reason:null
在 application.properties 中进行如下配置:
# 消息抵达队列确认
spring.rabbitmq.publisher-returns=true
# 抵达队列后,异步的回调 return confirm
spring.rabbitmq.template.mandatory=true
initRabbitTemplate(){
rabbitTemplate.setReturnCallback...}
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
*
* @param message :失败的消息
* @param i:(replyCode)回复的状态码
* @param s:(replyText)回复的文本内容
* @param s1:(exchange)哪个交换机
* @param s2:(routingKey)哪个路由键
*/
@Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
System.out.println("message:"+message+
" replyCode:"+i+
" replyText:"+s+
" exchange:"+s1+
" routingKey:"+s2);
}
});
为了测试,将路由键改错,接受到下列错误消息
message:(Body:'{"id":1,"message":"Hello","name":"June"}' MessageProperties [headers={__TypeId__=com.atguigu.gulimall.order.entity.TestEntity}, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]) replyCode:312 replyText:NO_ROUTE exchange:first-java-exchange routingKey:first1.java
因为默认是自动确认,所以只要消息被消费端接收到,客户端就会自动确认,服务端就会移除这个消息。
产生问题:收到了很多消息,自动回复给服务器确认,却没有将全部消息处理成功就宕机了,此时消息丢失。 ==>解决方法:手动确认
在application.properties中进行如下配置后,只要未进行手动确认,消息就不会接收:
spring.rabbitmq.listener.simple.acknowledge-mode=manual
在接收方法中加入如下函数:
channel.basicAck(deliveryTag,false);业务成功的签收
channel.basicNack(deliveryTag,false,true);业务失败的拒签
@RabbitHandler
public void recieveMessage1(Message message, TestEntity content, Channel channel){
System.out.println("内容:"+content);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
channel.basicAck(deliveryTag,false); //手动签收
}catch (Exception e){
}
}
void basicNack(long deliveryTag,
boolean multiple,
boolean requeue) //requeue=false(丢弃) =true(发回服务器重新入队)
throws IOException;
@RabbitHandler
public void recieveMessage1(Message message, TestEntity content, Channel channel){
System.out.println("内容:"+content);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//var3:requeue =false(丢弃) =true(发回服务器重新入队)
channel.basicNack(deliveryTag,false,true);
}catch (Exception e){
}
}