systemctl start docker
docker ps -a
docker restart c086e473e4f9
1、SpringBoot整合RabbitMQ
1、所需要的依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
2、生产者发送步骤
2-1:配置application.yml
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast" //mq的虚拟机 可以自己创建
2-2:配置mq的交换机和队列
package com.itheima.rabbitMq.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author shkstart
* @create 2023-02-27 10:57
*/
@Configuration
public class RabbitMqConfig {
public static final String EXCHANGE_TOPIC_NAME = "TOPIC_boot";
public static final String QUEUE_TOPIC_NAME1 = "QUEUE_boot1";
//配置交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_TOPIC_NAME).
durable(true).build();
}
//配置队列
@Bean("bootQueue")
public Queue bootQueue(){
return QueueBuilder.durable(QUEUE_TOPIC_NAME1).build();
}
//队列和交换机绑定
/*
1、知道队列是谁
2、知道那个交换机
3、routing key
*/
@Bean("bingQueueExchange")
public Binding bingQueueExchange(@Autowired Exchange bootExchange,
@Autowired Queue bootQueue){
return BindingBuilder.bind(bootQueue).
to(bootExchange).
with("boot.#").noargs();
}
}
2-3:生产者发送消息
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
@Resource
public RabbitTemplate rabbitTemplateL;
@Test
public void testPro(){
String body = "宠他喵的我真尼玛服了";
rabbitTemplateL.convertAndSend(RabbitMqConfig.EXCHANGE_TOPIC_NAME,"boot.xixi",body);
}
}
3、消费者接收消息
3-1:配置application.yml
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast"
3-2:消费者接收消费信息
@Component
public class ConsumerTest {
@RabbitListener(queues = RabbitMqConfig.QUEUE_TOPIC_NAME1)
public void listenQueue(Message message){
System.out.println(message);
}
}
2、mq的高级特性
3:消息的可靠性
2-2:配置yml文件,开启 publisher-confirms和publisher-returns
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast"
publisher-confirms: true
publisher-returns: true
2-3:开启交换机接收信息的确认模式
如果消息到了交换机,那么会执行setConfirmCallback的这个回调方法
@Test
public void testconfirm(){
rabbitTemplateL.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("confirm方法被执行了");
}
});
//发送消息
rabbitTemplateL.convertAndSend(RabbitMqConfig.EXCHANGE_TOPIC_NAME,"aaa.boot.wokao","java退腿腿");
}
2-4:开启队列接收信息的确认模式
当消息没到队列,那么就执行setReturnCallback的方法,进行下一步的处理
/*
回退模式
当消息发送给exchange时,exchange会路由到queue,会自动确认消息,如果消息没有被确认,就会执行ReturnCallBack
步骤:
1、开启回退模式
2、设置回调ReturnCallBack
3、设置exchange处理消息的模式
1、如果消息没有路由到queue,则丢弃消息
2、如果消息没有路由到了queue,则返回给消息发送方ReturnCallBack
*/
@Test
public void testReturn(){
//设置交换机处理失败消息的模式
rabbitTemplateL.setMandatory(true);
//设置会回调returnCallBack
rabbitTemplateL.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
*
* @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("routingKey=" + routingKey);
System.out.println("exchange=" + exchange);
System.out.println("message" + message);
System.out.println("消息没到队列");
}
});
//发送消息
rabbitTemplateL.convertAndSend(RabbitMqConfig.EXCHANGE_TOPIC_NAME,"aa.boot.wokao","java退腿腿我真真的哭死123");
}
4 Consumer Ack
4-1:设置消费者中的yml文件
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast"
# publisher-confirms: true
# publisher-returns: true
listener:
simple:
acknowledge-mode: manual
direct:
acknowledge-mode: manual
# acknowledge-mode: manual
4-2:消费者的代码如下
@Component
@RabbitListener(queues = RabbitMqConfig.QUEUE_TOPIC_NAME1)
public class AckListener implements ChannelAwareMessageListener {
@RabbitHandler
public void process(String hello, Channel channel, Message message) throws IOException {
System.out.println("HelloReceiver收到 : " + hello +"收到时间"+new Date());
try {
//告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
//int i = 3/0;
System.out.println("receiver success");
} catch (Exception e) {
e.printStackTrace();
//将失败的信息重新返回给队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(), true,true);
System.out.println("receiver fail");
}
}
5、消费端限流
5-1:前提
/** * Consumer限流机制 * 1、确保ack机制为手动确认 * 2、在yml文件中设置消费端从mq拉取的最大消费消息数量prefetch * */
5-2:消费端中的yml配置文件的设置
prefetch就是代表着每次消费端可以从mq里面拿到消息数量
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast"
listener:
simple:
acknowledge-mode: manual
prefetch: 1
direct:
acknowledge-mode: manual
prefetch: 1
5-3:消费端消费消息代码
@Component
public class QosListener {
@RabbitListener(queues = RabbitMqConfig.QUEUE_TOPIC_NAME1)
@RabbitHandler
public void process(String hello, Channel channel, Message message) throws IOException, InterruptedException {
//获取消息
System.out.println(new String(message.getBody()));
//处理业务逻辑
Thread.sleep(1000);
//签收
channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
}
6、TTL
6-1:第一种可以给队列设置过期时间
withArgument("x-message-ttl",10000)设置了队列的过期时间,到指定时间后,该队列的所有消息都会过期
@Bean("ttlQueue1")
public Queue ttlQueue1(){
return QueueBuilder.durable(QUEUE_TOPIC_TTL).withArgument("x-message-ttl",10000).build();
}
6-2:第二种可以给消息本身设置过期时间
@Test
public void testTTL(){
/**
*
*/
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("10000");
return message;
}
};
//发送消息
rabbitTemplateL.convertAndSend(RabbitMqConfig.TTL_EXCHANGE,
"order.wokao",
"订单消息",messagePostProcessor);
}
7、死信队列
8、延迟队列
8-1:设置生产者的yml
spring:
rabbitmq:
host: 192.168.153.130
port: 5672
username: itcast2
password: itcast2
virtual-host: "/itcast"
publisher-confirms: true
publisher-returns: true
8-2:配置相关的正常队列与交换机以及死信的队列和交换机
package com.example.bootproducer.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author shkstart
* @create 2023-02-27 18:34
*/
@Configuration
public class producerConf {
//正常交换机
public static final String ORDER_EXCHANGE = "order_exchange";
//正常队列
public static final String ORDER_QUEUE = "order_queue";
//死心交换机
public static final String ORDER_EXCHANGE_DLX = "order_exchange_dlx";
//死心队列
public static final String ORDER_QUEUE_DLX = "order_queue_dlx";
/**
* 延迟队列
* 1、创建正常的交换机(order_exchange)和正常的队列(order_queue)
* 2、创建死信的交换机(order_exchange_dlx)和死信的队列(order_queue_dlx)
* 3、将正常的队列与死信交换机绑定关系
*/
//正常的队列(order_queue)
@Bean("getOrderQueue")
public Queue getOrderQueue() {
return QueueBuilder.durable(ORDER_QUEUE)
.withArgument("x-message-ttl",20000)
.withArgument("x-dead-letter-exchange",ORDER_EXCHANGE_DLX)
.withArgument("x-dead-letter-routing-key","orderdlx.error")
.build();
}
//创建正常的交换机(order_exchange)
@Bean("getOrderExchange")
public Exchange getOrderExchange() {
return ExchangeBuilder.topicExchange(ORDER_EXCHANGE).durable(true).build();
}
//绑定正常的队列(order_queue)和创建正常的交换机(order_exchange)
@Bean
public Binding getOrderQueueAndexchange(@Qualifier("getOrderQueue") Queue queue,@Qualifier("getOrderExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("order.#").noargs();
}
//死心的队列(order_queue_dlx)
@Bean("getOrderQueueDlx")
public Queue getOrderQueueDlx() {
return QueueBuilder.durable(ORDER_QUEUE_DLX).build();
}
//创建死心的交换机(order_exchange_dlx)
@Bean("getOrderExchangeDlx")
public Exchange getOrderExchangeDlx() {
return ExchangeBuilder.topicExchange(ORDER_EXCHANGE_DLX).durable(true).build();
}
//绑定死心的队列(order_queue_dlx)和创建死心的交换机(order_exchange_dlx)
@Bean
public Binding getOrderQueueAndexchangeDlx(@Qualifier("getOrderQueueDlx") Queue queue,@Qualifier("getOrderExchangeDlx") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("orderdlx.#").noargs();
}
}
8-3:生产者发送消息
@Test
void contextLoads() {
rabbitTemplate.convertAndSend(producerConf.ORDER_EXCHANGE,"order.xixi","我是一条死心消息");
}
8-4:消费者接收消息(这个消息是从死信队列过来的)
package com.example.bootconsumer.Consumers;
import com.example.bootproducer.config.producerConf;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
/**
* @author shkstart
* @create 2023-02-27 19:11
*/
@Component
public class consumerTest1 {
@Resource
public RabbitTemplate rabbitTemplate;
@RabbitListener(queues = producerConf.ORDER_QUEUE_DLX)
@RabbitHandler
public void process(String hello, Channel channel, Message message) throws IOException {
try {
System.out.println("hello=" + hello );
System.out.println(new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
} catch (Exception e) {
//
channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
}
}
}
9、rabbitmq应用问题
9-1:消息可靠性保障
9-2:消息幂等性保障