当前位置: 首页 > news >正文

RabbitMQ三、springboot整合rabbitmq(消息可靠性、高级特性)

一、springboot整合RabbitMQ(jdk17)(创建两个项目,一个生产者项目,一个消费者项目)

  1. 上面使用原生JAVA操作RabbitMQ较为繁琐,很多的代码都是重复书写的,使用springboot可以简化代码的编写。

生产者项目

在这里插入图片描述

第一步:创建springboot工程,然后引入rabbitmq的依赖

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

第二步:编写配置文件

spring:rabbitmq:host: 192.168.70.130  # 虚拟机的地址port: 5672username: adminpassword: adminvirtual-host: /#日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'

第三步:编写RabbitMQ的配置类

@Configuration
public class RabbitmqConfig1 {private final String EXCHANGE_NAME = "boot_exchange";private final String QUEUE_NAME = "boot_queue";private final String ROUTE_NAME = "boot_route";//创建交换机@Bean(EXCHANGE_NAME)public Exchange getExchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//创建队列@Bean(QUEUE_NAME)public Queue getQueue(){return new Queue(QUEUE_NAME);}//交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTE_NAME).noargs();}
}

第四步:编写发送消息测试类

//编写发送消息测试类
@SpringBootTest
public class RabbitmqTest {// 注入RabbitTemplate工具类@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void testSendMessage(){/*** 发送消息* 参数1:交换机* 参数2:路由key* 参数3:要发送的消息*/rabbitTemplate.convertAndSend("boot_exchange","boot_route","你好我有一个毛衫");System.out.println("发送消息成功");}
}

消费者项目

在这里插入图片描述

第一步:创建springboot工程,然后引入rabbitmq的依赖

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

第二步:编写配置文件

spring:rabbitmq:host: 192.168.70.130  # 虚拟机的地址port: 5672username: adminpassword: adminvirtual-host: /#日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'

第三步:编写消费者,监听队列

@Component
public class Consumer1 {/*** 监听队列* @param message* queues表示监听的队列的名称*/@RabbitListener(queues = "boot_queue")public void listener(String message){System.out.println("接受到消息 = " + message);}
}

4、rabbitmq的消息可靠性

  1. RabbitMQ消息投递的路径为:
    生产者--->交换机--->队列--->消费者

  2. 在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?

      1. 确认模式(confirm):可以监听消息是否从生产者成功传递到交换机
      1. 退回模式(return):可以监听消息是否从交换机成功传递到队列
      1. 消费者消息确认(Consumer Ack):可以监听消费者是否成功处理消息。

【一】rabbitmq的消息可靠性——确认模式

  1. 确认模式(confirm):可以监听消息是否从生产者成功传递到交换机
  2. 创建一个新的生产者项目,导入mq(上面的第一步操作)依赖进行开发:(也可以在原来的基础上修改信息)
    • 代码组成和上面的生产者项目是一样的,也是三步内容。
第一步:修改配置文件

只是添加了一句代码
在这里插入图片描述

spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: / # 表示使用默认的virtual-host#开启确认模式publisher-confirm-type: correlated#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
第二步:在生产者的配置类创建交换机和队列(RabbitMQ的配置类)
@Configuration
public class RabbitmqConfig2Confirm {public final String EXCHANGE_NAME = "confirm_exchange";public final String QUEUE_NAME = "confirm_queue";public final String ROUTING_NAME = "confirm_routing";//    创建交换机@Bean(EXCHANGE_NAME)public Exchange exchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//    创建队列@Bean(QUEUE_NAME)public Queue queue(){return QueueBuilder.durable(QUEUE_NAME).build();}
//    创建交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTING_NAME).noargs();}
}
第三步:编写测试类发生消息:生产者定义确认模式的回调方法(springboot的测试类,能够加载到第二步的配置类)
 @Testvoid testConfirm() {//回调确认rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {/**** @param correlationData 配置信息* @param b 是否成功,true 是 ,false 否* @param s 失败原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean b, String s) {if (b){System.out.println("发送成功");}else{System.out.println("发送失败,原因:"+s);}}});//发送消息/*** 发送消息* 参数1:交换机* 参数2:路由key* 参数3:要发送的消息*/rabbitTemplate.convertAndSend("confirm_exchange","confirm_routing","send message...confirm");}

由于rabbitmq的confirm确认模式是确认消息是否从生产者成功传递到交换机的,所以就没必要写消费者进行信息的消费了

  • 当我们执行测试类的时候,先执行rabbitTemplate.convertAndSend(“confirm_exchange”,“confirm_routing”,“send message…confirm”);,无论消息是否成功发送都会调用 rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback()方法,如果发送成功则执行if语句的代码,如果发送失败则调用else语句的代码。
    • 根据执行的是if或者else的语句,就能判断消息是否成功传递到交换机了。

【二】rabbitmq的消息可靠性——退回模式

  1. 退回模式(return):可以监听消息是否从交换机成功传递到队列
  2. 创建一个新的生产者项目,导入mq(上面的第一步操作)依赖进行开发:(也可以在原来的基础上修改信息)
    • 代码组成和上面的生产者项目是一样的,也是三步内容。
第一步:修改配置文件

只是添加了一句
在这里插入图片描述

# rabbitmq???
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开启确认模式publisher-confirm-type: correlated#开始回退模式publisher-returns: true#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
第二步:编写配置类(RabbitMQ的配置类)
@Configuration
public class RabbitmqConfig3Return {public final String EXCHANGE_NAME = "return_exchange";public final String QUEUE_NAME = "return_queue";public final String ROUTING_NAME = "return_routing";
//    创建交换机@Bean(EXCHANGE_NAME)public Exchange exchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//    创建队列@Bean(QUEUE_NAME)public Queue queue(){return QueueBuilder.durable(QUEUE_NAME).build();}//    创建交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTING_NAME).noargs();}
}
第三步:编写测试类发生消息:生产者定义退回模式的回调方法(springboot的测试类,能够加载到第二步的配置类)
@Testvoid testReturnSendMessage(){
//        调用回退模式的回调方法,只有失败才会回调,成功不会回调哦
// 失败后将失败信息封装到参数中rabbitTemplate.setReturnsCallback(returned ->{Message message = returned.getMessage();System.out.println("消息对象:"+message);System.out.println("错误码:"+returned.getReplyCode());System.out.println("错误信息:"+returned.getReplyText());System.out.println("交换机:"+returned.getExchange());System.out.println("路由键:"+returned.getRoutingKey());});//        发送消息/*** 发送消息* 参数1:交换机* 参数2:路由key* 参数3:要发送的消息*/rabbitTemplate.convertAndSend("return_exchange","return_routing","send message...return");}

由于rabbitmq的return回退模式是确认消息是否从交换机成功传递到队列的,还没有传递到消费者,所以就没必要写消费者进行信息的消费了

  • 当我们执行测试类的时候,先执行rabbitTemplate.convertAndSend(“return_exchange”,“return_routing”,“send message…return”);,如果消息成功发送到队列上则不会调用 rabbitTemplate.setReturnsCallback方法,如果发送步成功则调用回调方法rabbitTemplate.setReturnsCallback,
    • 根据运行结果就可以知道在传递消息到队列上的时候哪里发生错误了

在这里插入图片描述

【三】rabbitmq的消息可靠性——消费者消息确认(Consumer Ack)

  1. 在RabbitMQ中,消费者接收到消息后会向队列发送确认签收的消息,只有确认签收的消息才会被移除队列。这种机制称为消费者消息确认(Consumer Acknowledge,简称Ack)
    • 类似快递员派送快递也需要我们签收,否则一直存在于快递公司的系统中。
  2. 消费者消息确认(Consumer Acknowledge,简称Ack)分为自动确认手动确认
    • 自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除
    • 但是在实际开发中,收到消息后可能业务处理出现异常,那么消息就会丢失。此时需要设置手动签收,即在业务处理成功后再通知签收消息,如果出现异常,则拒签消息,让消息依然保留在队列当中。

● 自动确认:spring.rabbitmq.listener.simple.acknowledge=“none”
● 手动确认:spring.rabbitmq.listener.simple.acknowledge=“manual”

  1. 创建一个新的生产者项目和新的消费者项目,导入mq(上面的第一步操作)依赖进行开发:(也可以在原来的基础上修改信息)
    • 代码组成和上面的生产者项目是一样的,也是三步内容。
生产者项目:第一步:修改配置文件

不用修改

# rabbitmq???
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开启确认模式publisher-confirm-type: correlated#开始回退模式publisher-returns: true#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
生产者项目:第二步:编写配置类(RabbitMQ的配置类)
@Configuration
public class RabbitmqConfig4ACK {public final String EXCHANGE_NAME = "ack_exchange";public final String QUEUE_NAME = "ack_queue";public final String ROUTING_NAME = "ack_routing";
//    创建交换机@Bean(EXCHANGE_NAME)public Exchange exchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//    创建队列@Bean(QUEUE_NAME)public Queue queue(){return QueueBuilder.durable(QUEUE_NAME).build();}//    创建交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTING_NAME).noargs();}
}
生产者项目:第三步:编写测试类发生消息:(springboot的测试类,能够加载到第二步的配置类)
 @Testvoid testAck(){//        发送消息rabbitTemplate.convertAndSend("ack_exchange","ack_routing","send message...ack");}
消费者项目(自动确认):第一步:修改配置文件
  • 消费者消息确认——自动确认的配置文件
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: none   # 默认就是自动确认
#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'

在这里插入图片描述

  • 自动签收模式就是:消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除。当我们拿到消息的时候,业务出现异常了,所以无法正确处理消息,导致消息丢失了。
消费者项目(自动确认):第二步:编写消费者类,监听队列
  • 自动确认的消费者类
@Component
public class AckConsumer {
//    自动签收@RabbitListener(queues = "ack_queue")public void listenMessage(Message message, Channel channel) throws IOException, InterruptedException, IOException {
//        获取消息String s = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println(s);
//        TODO,处理事务
//        故意出错int i= 1/0;}}
消费者项目(手动确认):第一步:修改配置文件
  • 消费者消息确认——手动确认的配置文件
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: manual  
#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
消费者项目(手动确认):第二步:编写消费者类,监听队列
  • 手动确认
@Component
public class AckConsumer {//    手动签收@RabbitListener(queues = "ack_queue")public void listenMessage(Message message, Channel channel) throws IOException, InterruptedException, IOException {
// 消息投递序号,消息每次投递该值都会+1long deliveryTag = message.getMessageProperties().getDeliveryTag();try {
//            int i = 1/0; //模拟处理消息出现bugSystem.out.println("成功接受到消息:"+message);// 签收消息/*** 参数1:消息投递序号* 参数2:是否一次可以签收多条消息*/channel.basicAck(deliveryTag,true);}catch (Exception e){System.out.println("消息消费失败!");Thread.sleep(2000);// 拒签消息/*** 参数1:消息投递序号* 参数2:是否一次可以拒签多条消息* 参数3:拒签后消息是否重回队列*/channel.basicNack(deliveryTag,true,true);}}
}

在这里插入图片描述

在这里插入图片描述

  • 手动签收模式就是:如果出现异常,则拒签消息,让消息依然保留在队列当中。方便下次请求能够请求到这次因为异常而没有接收到的消息。

【四】RabbitMQ高级特性——消费端限流

在这里插入图片描述

  • 前面说过MQ可以对请求进行“削峰填谷”,即通过消费端限流的方式限制消息的拉取速度,达到保护消费端的目的。
  • 使用【三】rabbitmq的消息可靠性——消费者消息确认(Consumer Ack)的项目,消费者使用手动确认模式的代码即可(但是要修改配置文件)
第一步:先在生产者项目中,发送多个消息
@Testpublic void testLimitSendBatch() {// 发送十条消息for (int i = 0; i < 10; i++) {rabbitTemplate.convertAndSend("ack_exchange", "ack_routing", "这是第"+i+"条消息");}}
第二步:修改消费者项目的配置文件

最主要就是配置文件的修改:
在这里插入图片描述

spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: manual  #none是默认的prefetch: 5  # 每次消费者从队列拉取的消息数量(限制)#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
第三步:重新编写消费者类
@Component
public class ConsumerLimit {
//    手动签收@RabbitListener(queues = "limit_queue")public void listenMessage(Message message, Channel channel) throws IOException, InterruptedException, IOException {
//        获取消息String s = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println(s);//        模拟业务处理Thread.sleep(3000);long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 手动签收channel.basicAck(deliveryTag,true);}}
  • 其实就是修改了消费者项目的配置文件,添加一条配置信息,限制消费者消息的拉取速度。
    在这里插入图片描述

【五】RabbitMQ高级特性——利用限流实现不公平分发

  1. 在RabbitMQ中,多个消费者监听同一条队列,则队列默认采用的轮询分发。但是在某种场景下这种策略并不是很好,例如消费者1处理任务的速度非常快,而其他消费者处理速度却很慢。此时如果采用公平分发,则消费者1有很大一部分时间处于空闲状态。此时可以采用不公平分发,即谁处理的快,谁处理的消息多。
  • 在【四】RabbitMQ高级特性——消费端限流的基础上,修改一消费者项目的配置文件,然后在消费者类中多写几个监听消息的方法(或者多写几个消费者类)。
第一步:修改消费者项目的配置文件

最主要就是配置文件的修改:
在这里插入图片描述

spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: manual  #none是默认的prefetch: 1  #  消费端最多拉取1条消息消费,这样谁处理的快谁拉取下一条消息,实现了不公平分发#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
第二步:修改消费者类,编写多个监听方法
@Component
public class ConsumerUnfair {
//  消费者1@RabbitListener(queues = "ack_queue")public void listenMessage1(Message message, Channel channel) throws IOException, InterruptedException, IOException {
//        获取消息String s = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println("消费者1"+s);Thread.sleep(3000);long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 手动签收channel.basicAck(deliveryTag,true);}//    消费者2@RabbitListener(queues = "ack_queue")public void listenMessage2(Message message, Channel channel) throws IOException, InterruptedException, IOException {
//        获取消息String s = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println("消费者2"+s);Thread.sleep(1000);long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 手动签收channel.basicAck(deliveryTag,true);}// .......监听方法
}
  • 最主要的就是消费者项目的配置文件的修改: 配置消费端最多拉取1条消息消费,这样谁处理的快谁拉取下一条消息,实现了不公平分发。

【六】RabbitMQ高级特性——消息存活时间

  1. RabbitMQ可以设置消息的存活时间(Time To Live,简称TTL),当消息到达存活时间后还没有被消费,会被移出队列。RabbitMQ可以对队列的所有消息设置存活时间,也可以对某条消息设置存活时间。
  • 使用【三】rabbitmq的消息可靠性——消费者消息确认(Consumer Ack)的项目,消费者使用手动确认模式的代码
第一步:修改生产者项目的配置类

在这里插入图片描述

@Configuration
public class RabbitmqConfig7ttl {public final String EXCHANGE_NAME = "ack_exchange";public final String QUEUE_NAME = "ack_queue";public final String ROUTING_NAME = "ack_routing";
//    创建交换机@Bean(EXCHANGE_NAME)public Exchange exchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//    创建队列@Bean(QUEUE_NAME)public Queue queue(){return QueueBuilder.durable(QUEUE_NAME)
//                设置队列的超时的时间,单位是毫秒.ttl(10000).build();}//    创建交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTING_NAME).noargs();}
}
第二步:修改生产者项目的测试类

设置单条消息存活时间
在这里插入图片描述

 @Testpublic void testTtlSendBatch() {// 发送十条消息for (int i = 0; i < 100; i++) {if (i%5 == 0) {//设置消息属性MessageProperties messageProperties = new MessageProperties();//设置存活时间messageProperties.setExpiration("10000");// 创建消息对象(可以配置消息的一些配置)Message message = new Message(("这是第" + i + "条消息").getBytes(StandardCharsets.UTF_8), messageProperties);// 发送消息rabbitTemplate.convertAndSend("ack_exchange", "ack_routing", message);}else {rabbitTemplate.convertAndSend("ack_exchange", "ack_routing", "这是第" + i + "条消息");}}}
    1. 如果设置了单条消息的存活时间,也设置了队列的存活时间,以时间的为准。
    1. 消息过期后,并不会马上移除消息,只有消息消费到队列顶端时,才会移除该消息

【七】RabbitMQ高级特性——优先级队列

  1. 假设在电商系统中有一个订单催付的场景,即客户在一段时间内未付款会给用户推送一条短信提醒,但是系统中分为大型商家和小型商家。比如像苹果,小米这样大商家一年能给我们创造很大的利润,所以在订单量大时,他们的订单必须得到优先处理,此时就需要为不同的消息设置不同的优先级,此时我们要使用优先级队列。
  • 使用【三】rabbitmq的消息可靠性——消费者消息确认(Consumer Ack)的项目,消费者使用手动确认模式的代码
第一步:修改生产者项目的配置类

在这里插入图片描述

@Configuration
public class RabbitmqConfig8Priority {public final String EXCHANGE_NAME = "priority_exchange";public final String QUEUE_NAME = "priority_queue";public final String ROUTING_NAME = "priority_routing";
//    创建交换机@Bean(EXCHANGE_NAME)public Exchange exchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//    创建队列@Bean(QUEUE_NAME)public Queue queue(){return QueueBuilder.durable(QUEUE_NAME)
//                设置队列的优先级,值越大优先级越高,一般不超过10.maxPriority(10).build();}//    创建交换机和队列绑定@Beanpublic Binding exchangeBindQueue(@Qualifier(QUEUE_NAME) Queue queue, @Qualifier(EXCHANGE_NAME) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(ROUTING_NAME).noargs();}
}
第二步:修改生产者项目的测试
 @Testpublic void testPrioritySendBatch() {// 发送十条消息for (int i = 0; i < 100; i++) {if (i%5 == 0) {//设置消息属性MessageProperties messageProperties = new MessageProperties();
//             设置优先级messageProperties.setPriority(9);// 创建消息对象(可以配置消息的一些配置)Message message = new Message(("这是第" + i + "条消息").getBytes(StandardCharsets.UTF_8), messageProperties);// 发送消息rabbitTemplate.convertAndSend("priority_exchange", "priority_routing", message);}else {rabbitTemplate.convertAndSend("priority_exchange", "priority_routing", "这是第" + i + "条消息");}}}
  • 设置了消息的优先级,那么消费者项目在消费消息的时候就会优先消费等级高的消息。

【八】RabbitMQ高级特性——死信队列

  1. 在MQ中,当消息成为死信(Dead message)后,消息中间件可以将其从当前队列发送到另一个队列中,当前队列就是死信队列。而在RabbitMQ中,由于有交换机的概念,实际是将死信发送给了死信交换机(Dead Letter Exchange,简称DLX)。死信交换机和死信队列和普通的没有区别。
    在这里插入图片描述
  2. 消息成为死信的情况:
      1. 队列消息长度到达限制。
      1. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
      1. 消息到达存活时间未被消费。
生产者项目:第一步:修改配置文件
# rabbitmq???
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开启确认模式publisher-confirm-type: correlated#开始回退模式publisher-returns: true#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
生产者项目:第二步:编写配置类(RabbitMQ的配置类)
@Configuration
public class RabbitmqConfig9Dead {//    死信private final String DEAD_EXCHANGE = "dead_exchange";private final String DEAD_QUEUE = "dead_queue";private final String DEAD_ROUTING = "dead_routing";// 死信交换机@Bean(DEAD_EXCHANGE)public Exchange deadExchange(){return ExchangeBuilder.topicExchange(DEAD_EXCHANGE).durable(true).build();}// 死信队列@Bean(DEAD_QUEUE)public Queue deadQueue(){return QueueBuilder.durable(DEAD_QUEUE).build();}// 死信交换机绑定死信队列@Beanpublic Binding bindDeadQueue(@Qualifier(DEAD_EXCHANGE) Exchange exchange,@Qualifier(DEAD_QUEUE)Queue queue){return BindingBuilder.bind(queue).to(exchange).with(DEAD_ROUTING).noargs();}// 普通private final String NORMAL_EXCHANGE = "normal_exchange";private final String NORMAL_QUEUE = "normal_queue";private final String NORMAL_ROUTING = "normal_routing";// 普通交换机@Bean(NORMAL_EXCHANGE)public Exchange normalExchange(){return ExchangeBuilder.topicExchange(NORMAL_EXCHANGE).durable(true).build();}// 普通队列@Bean(NORMAL_QUEUE)public Queue normalQueue(){return QueueBuilder.durable(NORMAL_QUEUE).deadLetterExchange(DEAD_EXCHANGE) // 绑定死信交换机.deadLetterRoutingKey(DEAD_ROUTING) // 死信队列路由关键字.ttl(10000) // 消息存活10s.maxLength(10) // 队列最大长度为10.build();}// 普通交换机绑定普通队列@Beanpublic Binding bindNormalQueue(@Qualifier(NORMAL_EXCHANGE) Exchange exchange,@Qualifier(NORMAL_QUEUE)Queue queue){return BindingBuilder.bind(queue).to(exchange).with(NORMAL_ROUTING).noargs();}
}
生产者项目:第三步:编写测试类发生消息:(springboot的测试类,能够加载到第二步的配置类)
@Test
public void testDlx(){// 存活时间过期后变成死信//     rabbitTemplate.convertAndSend("normal_exchange","normal_routing","测试死信");// 超过队列长度后变成死信//     for (int i = 0; i < 20; i++) {//       rabbitTemplate.convertAndSend("normal_exchange","normal_routing","测试死信");//     }// 消息拒签但不返回原队列后变成死信rabbitTemplate.convertAndSend("normal_exchange","normal_routing","测试死信");
}
消费者项目(手动确认):第一步:修改配置文件
  • 消费者消息确认——手动确认的配置文件
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: manual  
#????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
消费者项目(手动确认):第二步:编写消费者类,监听队列
  • 手动确认
@Component
public class ConsumerDead {@RabbitListener(queues = "normal_queue")public void listenMessage1(Message message, Channel channel) throws IOException, InterruptedException, IOException {
//        获取消息String s = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println("消费者1"+s);Thread.sleep(500);long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 拒绝签收channel.basicNack(deliveryTag,true,false);}
  • 死信队列小结
      1. 死信交换机和死信队列和普通的没有区别
      1. 当消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
      1. 消息成为死信的三种情况:
        1. 队列消息长度到达限制;
        1. 消费者拒接消费消息,并且不重回队列;
        1. 原队列存在消息过期设置,消息到达超时时间未被消费;

【九】RabbitMQ高级特性——延迟队列

  1. 延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
    • 例如:
        1. 下单后,30分钟未支付,取消订单,回滚库存。
        1. 新用户注册成功7天后,发送短信问候。
        • 实现方式:
            1. 定时器
            1. 延迟队列
              在这里插入图片描述
  • RabbitMQ中并未提供延迟队列功能,我们可以使用死信队列实现延迟队列的效果。
    在这里插入图片描述
    1. 延迟队列 指消息进入队列后,可以被延迟一定时间,再进行消费。
    1. RabbitMQ没有提供延迟队列功能,但是可以使用 : TTL + DLX 来实现延迟队列效果。
      在这里插入图片描述
第一步:创建springboot项目并添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
第二步:编写配置文件
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动手动签收listener:simple:acknowledge-mode: manual
# ????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
第三步:编写配置类
@Configuration
public class RabbitMQConfig {private final String DEAD_EXCHANGE = "order_expire_exchange";private final String DEAD_QUEUE = "order_expire_queue";private final String DEAD_ROUTING = "order_expire_routing";private final String ORDER_EXCHANGE = "order_exchange";private final String ORDER_QUEUE = "order_queue";private final String ORDER_ROUTING = "order_routing";// 死信交换机@Bean(DEAD_EXCHANGE)public Exchange deadExchange(){return ExchangeBuilder.topicExchange(DEAD_EXCHANGE).durable(true).build();}// 死信队列@Bean(DEAD_QUEUE)public Queue deadQueue(){return QueueBuilder.durable(DEAD_QUEUE).build();}// 死信交换机绑定死信队列@Beanpublic Binding bindDeadQueue(@Qualifier(DEAD_EXCHANGE) Exchange exchange, @Qualifier(DEAD_QUEUE)Queue queue){return BindingBuilder.bind(queue).to(exchange).with(DEAD_ROUTING).noargs();}// 普通交换机@Bean(ORDER_EXCHANGE)public Exchange normalExchange(){return ExchangeBuilder.topicExchange(ORDER_EXCHANGE).durable(true).build();}// 普通队列@Bean(ORDER_QUEUE)public Queue normalQueue(){return QueueBuilder.durable(ORDER_QUEUE).deadLetterExchange(DEAD_EXCHANGE) // 绑定死信交换机.deadLetterRoutingKey(DEAD_ROUTING) // 死信队列路由关键字.ttl(10000) // 消息存活10s(模拟30min超时).build();}// 普通交换机绑定普通队列@Beanpublic Binding bindNormalQueue(@Qualifier(ORDER_EXCHANGE) Exchange exchange,@Qualifier(ORDER_QUEUE)Queue queue){return BindingBuilder.bind(queue).to(exchange).with(ORDER_ROUTING).noargs();}
}
第四步:创建控制器,完成下单功能
@RestController
public class OrderController {//注入MQ@Autowiredprivate RabbitTemplate rabbitTemplate;@RequestMapping("/addOrder")public String addOrder(){//生成订单号String orderNumber = "2030061812251234";//在service层完成订单逻辑//将订单号发送到订单mq,30分钟过期进入死信队列,死信队列消费查询订单支付状态,做对应处理rabbitTemplate.convertAndSend("order_exchange","order_routing",orderNumber);return "下单成功! 您的订单号为 :"+orderNumber;}
}
第五步:创建消费者,监听消息
@Component
public class ListenerOrder {//监听订单过期队列@RabbitListener(queues = "order_expire_queue")public void orderListener(String orderId){System.out.println("orderId = " + orderId);//根据订单id查询订单状态是否支付/*** 监听死信队列的类,回去30min超时订单号,根据订单号查询订单的支付状态* 支付:走下一步流程* 未支付:关闭订单,库存回滚*/}
}
手动签收模式的结果
  • 在手动签收模式的时候,当我们启动项目,访问订单功能时,立刻生成了一个队列消息
    在这里插入图片描述
  • 然后因为是手动签收模式,所以在消息的存活时间过去了之后,成为了死信消息,所以被消息被拒收了,但是还存在队列中。
    在这里插入图片描述
自动签收模式的结果
spring:rabbitmq:host: 192.168.70.130port: 5672username: adminpassword: adminvirtual-host: /#开动自动签收listener:simple:acknowledge-mode: none   # 默认的
# ????
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'
  • 在自动签收模式的时候,当我们启动项目,访问订单功能时,立刻生成了一个队列消息
    在这里插入图片描述
  • 因为是自动签收的,所以消息过了存活时间之后就没了(自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除)
    在这里插入图片描述

RabbitMQ一、RabbitMQ的介绍与安装(docker)

RabbitMQ二、RabbitMQ的六种模式

相关文章:

RabbitMQ三、springboot整合rabbitmq(消息可靠性、高级特性)

一、springboot整合RabbitMQ&#xff08;jdk17&#xff09;&#xff08;创建两个项目&#xff0c;一个生产者项目&#xff0c;一个消费者项目&#xff09; 上面使用原生JAVA操作RabbitMQ较为繁琐&#xff0c;很多的代码都是重复书写的&#xff0c;使用springboot可以简化代码的…...

第八十九周周报

学习目标&#xff1a; 论文 学习时间&#xff1a; 2024.05.25-2024.05.31 学习产出&#xff1a; 一、论文 SAN: INDUCING METRIZABILITY OF GAN WITH DISCRIMINATIVE NORMALIZED LINEAR LAYER 将GAN与切片最优输运联系起来&#xff0c;提出满足方向最优性、可分离性和单射…...

Centos升级Openssh版本至openssh-9.3p2

一、启动Telnet服务 为防止升级Openssh失败导致无法连接ssh&#xff0c;所以先安装Telnet服务备用 1.安装telnet-server及telnet服务 yum install -y telnet-server* telnet 2.安装xinetd服务 yum install -y xinetd 3.启动xinetd及telnet并做开机自启动 systemctl enable…...

茉莉香飘,奶茶丝滑——周末悠闲时光的绝佳伴侣

周末的时光总是格外珍贵&#xff0c;忙碌了一周的我们&#xff0c;终于迎来了难得的闲暇。这时&#xff0c;打开喜欢的综艺&#xff0c;窝在舒适的沙发里&#xff0c;再冲泡一杯香飘飘茉莉味奶茶&#xff0c;一边沉浸在剧情的海洋中&#xff0c;一边品味着香浓丝滑的奶茶&#…...

揭秘:Java字符串对象的内存分布原理

先来看看下面寄到关于String的真实面试题&#xff0c;看看你废不废&#xff1f; String str1 "Hello"; String str2 "Hello"; String str3 new String("Hello"); String str4 new String("Hello");System.out.println(str1 str2)…...

Vue.js - 生命周期与工程化开发【0基础向 Vue 基础学习】

文章目录 Vue 的生命周期Vue 生命周期的四个阶段Vue 生命周期函数&#xff08;钩子函数 工程化开发 & 脚手架 Vue CLI**开发 Vue 的两种方式&#xff1a;**脚手架目录文件介绍项目运行流程组件化开发 & 根组件App.vue 文件&#xff08;单文件组件&#xff09;的三个组成…...

Element-UI 快速入门指南

Element-UI 快速入门指南 Element-UI 是一套基于 Vue.js 的桌面端组件库,由饿了么前端团队开发和维护。它提供了丰富的 UI 组件,帮助开发者快速构建美观、响应式的用户界面。本篇文章将详细介绍 Element-UI 的安装、配置和常用组件的使用方法,帮助你快速上手并应用于实际项…...

2024华为OD机试真题-整型数组按个位值排序-C++(C卷D卷)

题目描述 给定一个非空数组(列表),其元素数据类型为整型,请按照数组元素十进制最低位从小到大进行排序, 十进制最低位相同的元素,相对位置保持不变。 当数组元素为负值时,十进制最低位等同于去除符号位后对应十进制值最低位。 输入描述 给定一个非空数组,其元素数据类型…...

善听提醒遵循易经原则。世界大同只此一路。

如果说前路是一个大深坑&#xff0c;那必然是你之前做的事情做的不太好&#xff0c;当坏的时候&#xff0c;坏的结果来的时候&#xff0c;是因为你之前的行为&#xff0c;你也就不会再纠结了&#xff0c;会如何走出这个困境&#xff0c;是好的来了&#xff0c;不骄不躁&#xf…...

CrossOver有些软件安装不了 用CrossOver安装软件后如何运行

CrossOver为用户提供了三种下载软件的方式分别是&#xff1a;搜索、查找分类、导入。如果【搜索】和【查找分类】提供的安装资源不能成功安装软件&#xff0c;那么我们可以通过多种渠道下载安装包&#xff0c;并将安装包以导入的方式进行安装。这里我们以QQ游戏为例&#xff0c…...

在vue中如何使用leaflet图层展示地图

在vue中如何使用leaflet <template><div id"map" class"map"></div> </template><script> export default {data () {return {};},mounted(){this.initMaps()},methods: {initMaps () {const map L.map(map, {zoomControl…...

mybatisplus 字段存的是json 在查询的时候怎么映射成对象

数据库交互对象 TableName(value "表名", autoResultMap true)TableField(typeHandler JacksonTypeHandler.class, value "user_info")private User user;autoResultMap 是一个 MyBatis-Plus 中的注解属性&#xff0c;用于控制是否自动生成结果映射。…...

Python 学习笔记【1】

此笔记仅适用于有任一编程语言基础&#xff0c;且对面向对象有一定了解者观看 文章目录 数据类型字面量数字类型数据容器字符串列表元组 type()方法数据类型强转 注释单行注释多行注释 输出基本输出连续输出&#xff0c;中间用“,”分隔更复杂的输出格式 变量定义del方法 标识符…...

Git系列:rev-parse 使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

【Java数据结构】详解LinkedList与链表(一)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2.ArrayList的缺陷 3.链表的概念及结构 4.无头单向非循环链表的实现 4.1成员属性 4.2成员方法 createList display——打印链表 addFirst——头插 addLast…...

PDF高效编辑器革新:一键智能转换PDF至HTML,轻松开启文件处理全新时代!

信息爆炸的时代&#xff0c;PDF文件因其跨平台、不易修改的特性&#xff0c;成为了商务、教育、出版等领域不可或缺的文件格式。然而&#xff0c;PDF文件的固定性也带来了诸多不便&#xff0c;特别是在需要对其内容进行编辑或格式转换时。这时&#xff0c;一款高效、易用的PDF编…...

JDBC知识

JDBC是什么? 这工作中我们针对数据库的操作,实际上很少会用到SQL语句,通过命令行/图形化来操作数据库,更多的是通过主流的编程语言来对数据库进行操作,即使通过代码来操作数据,我们还是会使用到SQL语句,所以掌握SQL语句也是很重要的. 如何通过代码操作数据库? 通过代码操作…...

C++操纵符用法

C中的操纵符&#xff08;Manipulators&#xff09;是用于格式化输入输出的特殊工具。它们可以在输出流中控制各种格式&#xff0c;如设置字段宽度、精度、填充字符等。以下是一些常用的操纵符及其用法&#xff1a; setw(int width): 设置字段宽度为width个字符。 cout <<…...

【一步一步了解Java系列】:子类继承以及代码块的初始化

看到这句话的时候证明&#xff1a;此刻你我都在努力 加油陌生人 个人主页&#xff1a;Gu Gu Study专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff1a;小闭 …...

探索Expect Python用法:深入解析与实战挑战

探索Expect Python用法&#xff1a;深入解析与实战挑战 在自动化和脚本编写领域&#xff0c;Expect Python已经成为了一种强大的工具组合。它结合了Expect的交互式会话处理能力和Python的编程灵活性&#xff0c;为开发者提供了一种全新的方式来处理复杂的自动化任务。然而&…...

【PostgreSQL17新特性之-explain命令新增选项】

EXPLAIN是一个用于显示语句执行计划的命令&#xff0c;可用于显示以下语句类型之一的执行计划&#xff1a; - SELECT - INSERT - UPDATE - DELETE - VALUES - EXECUTE - DECLARE - CREATE TABLE AS - CREATE MATERIALIZED VIEWPostgreSQL17-beta1版本近日发布了&#xff0c;新…...

JAVA实现人工智能,采用框架SpringAI

文章目录 JAVA实现人工智能,采用框架SpringAISpring AI介绍使用介绍项目前提项目结构第一种方式采用openai1. pom文件&#xff1a; 2. application.yml 配置3.controller 实现层 项目测试 JAVA实现人工智能,采用框架SpringAI Spring AI介绍 Spring AI是AI工程师的一个应用框架…...

基础—SQL—DQL(数据查询语言)分组查询

一、引言 分组查询的关键字是&#xff1a;GROUP BY。 二、DQL—分组查询 1、语法 SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后过滤条件 ]; 注意&#xff1a; 1、[ ] 里的内容可以有可以没有。 2、这条SQL语句有两块指定条件的地方&#…...

从CSV到数据库(简易)

需求&#xff1a;客户上传CSV文档&#xff0c;要求CSV文档内容查重/插入/更新相关数据。 框架&#xff1a;jdbcTemplate、commons-io、 DB&#xff1a;oracle 相关依赖&#xff1a; 这里本来打算用的2.11.0&#xff0c;无奈正式项目那边用老版本1.3.1&#xff0c;新版本对类型…...

K210视觉识别模块学习笔记3:内存卡写入拍摄图片_LED三色灯的操作_按键操作_定时器的配置使用

今日开始学习K210视觉识别模块: LED三色灯的操作_按键操作_定时器的配置使用_内存卡写入拍摄图片 亚博智能的K210视觉识别模块...... 固件库版本: canmv_yahboom_v2.1.1.bin 本文最终目的是编写一个按键拍照的例程序&#xff1a; 为以后的专用场景的模型训练做准备&#xf…...

如何定义“智慧校园”这个概念

在信息爆炸的时代&#xff0c;教育面临着前所未有的挑战&#xff1a;如何让每个学生在海量知识中找到属于自己的路径&#xff1f;如何让教师的智慧与科技的力量相得益彰&#xff1f;如何让校园成为培养创新思维的摇篮&#xff1f;智慧校园&#xff0c;这一概念的提出&#xff0…...

OpenSSL自签名证书

文章目录 生成1. 生成根证书的私钥&#xff08;root_private_key.pem&#xff09;2. 创建根证书的CSR和自签名证书&#xff08;root_csr.pem&#xff09;3. 生成服务器证书的私钥&#xff08;server_private_key.pem&#xff09;4. 创建服务器证书的CSR&#xff08;server_priv…...

QtCreator调试运行工程报错,无法找到相关库的的解决方案

最新在使用国产化平台做qt应用开发时&#xff0c;总是遇到qtcreator内调试运行 找不到动态库的问题&#xff0c;为什么会出现这种问题呢&#xff1f;明明编译的时候能够正常通过&#xff0c;运行或者调试的时候找不到相关的库呢&#xff1f;先说结论&#xff0c;排除库本身的问…...

【Python系列】Python 元组(Tuple)详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

特征融合篇 | YOLOv8 引入动态上采样模块 | 超过了其他上采样器

1. 介绍 本篇介绍了一种将动态上采样模块引入 YOLOv8 目标检测算法的新方法&#xff0c;该方法在 COCO 数据集上获得了 55.7% 的 mAP&#xff0c;超越了其他上采样器。该方法将动态上采样模块引入到 YOLOv8 的特征融合阶段&#xff0c;能够根据输入图像的特征分辨率动态调整上…...

​​​​​​​Beyond Compare 3密钥被撤销的解决办法

首先&#xff0c;BCompare3的链接如下 链接&#xff1a;https://pan.baidu.com/s/1vuSxY0cVQCt0-8CpFzUhvg 提取码&#xff1a;8888 --来自百度网盘超级会员V7的分享 1.问题现象 激活之后在使用过程中有时候会出现密钥被撤销的警告&#xff0c;而且该工具无法使用&#xff…...

知识见闻 - 人和动物的主要区别

人类和动物的主要区别之一确实在于理性&#xff0c;但这只是众多区别中的一个方面。以下是一些更全面的比较&#xff0c;突出人类和动物之间的主要区别&#xff1a; 理性和抽象思维&#xff1a; 人类&#xff1a;人类具有高度发展的理性能力&#xff0c;可以进行抽象思维、逻辑…...

Javaweb基础之工程路径

大家好&#xff0c;这里是教授.F 引入&#xff1a; 工程路径有一个知识点需要注意&#xff1a;就是相对路径。所谓相对路径就是依赖当前位置&#xff1a; 相对路径的定位依赖于当前位置或参考位置。 使用相对路径来解决&#xff0c; 一个非常重要的规则&#xff1a;页面所有的…...

国际荐酒师(香港)协会受邀出席广州意大利国庆晚宴

2024年5月30日&#xff0c;意大利驻广州总领事馆举办的2024年意大利国庆招待会及晚宴&#xff0c;庆祝意大利共和国成立。此次晚宴旨在促进中意两国之间的文化交流与合作。国际荐酒师&#xff08;香港&#xff09;协会受主办方邀请参与了这一重要活动。 国际荐酒师&#xff08;…...

让驰骋BPM系统插上AI的翅膀

让驰骋BPM系统插上AI的翅膀 在当今日益复杂多变的商业环境中&#xff0c;业务流程管理&#xff08;BPM&#xff09;系统的应用愈发广泛&#xff0c;成为企业提高效率、优化流程、降低成本的重要工具。驰骋BPM系统凭借其出色的性能和丰富的功能&#xff0c;赢得了众多企业的青睐…...

排队论 | 基于排队机制实现智能仓储机器人巡逻及避碰

研究背景: 智能仓储机器人在现代物流行业中扮演着重要的角色,能够提高仓库的运作效率和准确性。然而,仓储机器人在巡逻过程中可能会遇到其他机器人或障碍物,这就需要解决排队和避碰问题,以确保安全和高效的运作。 研究路线: 背景调研:了解智能仓储机器人的发展和应用…...

Node.js和npm常用命令

一、Node.js简介 Node.js是一个免费、开源、跨平台的JavaScript运行时环境&#xff0c;允许开发人员创建服务器、web应用程序、命令行工具和脚本。 点击查看node.js中文官网 点击查看node.js英文官网 二、npm简介 npm(Node Package Manager)是Node.js的软件包管理器&#xff0…...

pytest +allure在测试中的应用

一、allure配置&#xff1a; 1、安装allure库 pip install allure-pytest2、代码中导入 import allure3、常用命令&#xff1a; 1)、 pytest --alluredir报告目录 测试脚本.py比如&#xff1a;pytest --alluredir./allure_report &#xff08;未指定执行所有&#xff09; 2&…...

004 CentOS 7.9 mongodb7.0.11安装及配置

https://www.mongodb.com/try/download/shell https://www.mongodb.com/try/download/community 文章目录 /etc/mongod.conf在 /etc/systemd/system/ 目录下创建一个名为 mongod.service 的文件重新加载 systemd 配置&#xff1a;启用服务&#xff1a;现在&#xff0c;可以手动…...

Docker安装Redis(云服务器)

准备&#xff1a; 在云服务器中开启6370端口号 docker run -d --name redis -p 6379:6379 redis 这条命令使用docker运行一个名为"redis"的容器&#xff0c;映射容器的6379端口到主机的6379端口&#xff0c;并且使用redis镜像来运行容器。REDIS是一个开源的内存数据…...

springboot中抽象类无法注入到ioc容器

1、背景 在写代码时&#xff0c;发现service接口有两个实现类&#xff0c;并且两个实现类中没有对类名重命名&#xff0c;属性注入的时候也没有使用byName或Qualifier&#xff0c;正确情况下会发生多实现报错的问题&#xff0c;以前对这个问题进行解析过。 2、调试过程 我想…...

Java关键字大冒险:深入浅出地理解Java的精髓

Java编程语言中的关键字就像是魔法咒语&#xff0c;每一个都有自己独特的作用。在这篇博客中&#xff0c;我们将探讨Java中最常见的关键字&#xff0c;并通过有趣的例子和形象的比喻&#xff0c;让你轻松掌握它们的用法。 1. public: 万能钥匙 public关键字是Java中的“万能钥…...

Android Kotlin 打开相册选择图片(多选)

1. 核心代码 打开系统相册功能&#xff0c;本代码使用两种方式打开本地相册&#xff0c;startActivityForResult 已经废弃&#xff0c;可以使用新的方式。 package com.example.facedetectordemoimport android.content.pm.PackageManager import androidx.appcompat.app.App…...

java学习路径

ProcessOn Mindmap...

[线程与网络] 网络编程与通信原理(四):深入理解传输层UDP与TCP协议

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …...

IEEE编程语言排行榜:深度解析编程语言的四大维度、五大趋势、六大热门与七大挑战

IEEE编程语言排行榜&#xff1a;深度解析编程语言的四大维度、五大趋势、六大热门与七大挑战 在信息技术领域&#xff0c;编程语言排行榜一直是衡量各种编程语言流行度和影响力的重要指标。IEEE&#xff08;电气电子工程师协会&#xff09;作为全球最具影响力的科技专业组织之…...

【C++面试50题】

以下是针对C程序员面试可能遇到的一些问题&#xff0c;涵盖了从基础语法、面向对象、STL、内存管理、模板、异常处理、并发编程等多个方面。 ### 基础概念与语法 1. C与C的主要区别是什么&#xff1f; 2. 什么是构造函数和析构函数&#xff1f;它们何时被调用&#xff1f; 3. 什…...

外汇天眼:ESMA发布针对在投资服务中使用人工智能的公司的指导意见

欧洲证券和市场管理局&#xff08;ESMA&#xff09;&#xff0c;欧盟的金融市场监管机构和监督机构&#xff0c;发布了一份声明&#xff0c;为在向零售客户提供投资服务时使用人工智能技术&#xff08;AI&#xff09;的公司提供初步指导。 尽管人工智能的普及仍处于初期阶段&am…...

【前缀和 记忆化搜索】LeetCode1444. 切披萨的方案数

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 动态规划 记忆化搜索 LeetCode1444. 切披萨的方案数 给你一个 rows x cols 大小的矩形披萨和一个整数 k &#xff0c;矩形包含两种字符&#xff1a; ‘A’ &#xff…...

异常处理1--5.31

try--catch--finally格式&#xff1a; try{ }catch(错误类型){ }finally{ } 执行情况&#xff1a; package javatest5;public class test {public static void main(String[] args) {int num115;int num20;try{System.out.println("商等于&#xff1a;"num1/num…...