rabbitmq高级特性(1):消息确认,持久性,发送方确认和重试机制

news2024/10/28 15:58:13

目录

1.消息确认机制

1.1.消息确认机制介绍

2.1.Spring-AMQP的三种消息确认(重点)

2.持久性

2.1.交换机持久性

2.2.队列持久性

2.3.消息持久性

3.发送方确认

3.1.confirm确认模式

3.2.return退回模式

4.重试机制

4.1.重试机制定义

4.2.重试机制代码的准备工作

4.3.重试机制演示


1.消息确认机制
1.1.消息确认机制介绍

这里的消息确认机制,指的是消费者对消息的确认,而不是生产者。

(1)背景缘由

当消费者把消息发送出去后,就会把消息删除。如果消费者这边处理消息成功,则相安无事;但是如果处理异常,消息也就会丢失。所以就需要设置消费者的消息确认模式

(2)消息确认的机制

消息确认机制分为两个大类:自动确认和手动确认

手动确认又分为三种模式:肯定确认、否定确认、否定批量确认

对于rabbitmq的操作有两种,我们主要介绍第二种

  1. 直接使用amqb提供的包(RabbitMQ Java Client库)
  2. 使用spring集成进来的

第一种介绍: 

对于第一种主要把autoAck参数设置成false即为手动确认,改成true即为自动确认。收到确认需要调用Channel.basicAck函数

//自动确认,第二个参数为true
channel.basicConsume(Constants.RPC_RESPONSE_QUEUE,true,consumer);
//收到确认,第二个参数为false
DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String request = new String(body,"UTF-8");
                System.out.println("接收到请求:"+ request);
                String response = "针对request:"+ request +", 响应成功";
                AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                channel.basicPublish("", Constants.RPC_RESPONSE_QUEUE, basicProperties, response.getBytes());//返回响应
                channel.basicAck(envelope.getDeliveryTag(), false);//手动确认
            }
        };
channel.basicConsume(Constants.RPC_REQUEST_QUEUE,false,consumer);//false
  • 自动确认:当autoAck为true时,RabbitMQ会自动把发出去的消息设置为确认,然后从内存(硬盘)删除,而不会去管消费者是否真正的消费到该消息。适用对消息可靠性要求不高的场景
  • 收到确认:当autoAck为false时,RabbiMQ会等待消费者显示地调用Basic.Ack命令,回复确认信号之后才会删除该消息。适用对消息可靠性要求比较高的场景

2.1.Spring-AMQP的三种消息确认(重点)

关于消息确认机制的问题:none自动确认(不管消息是否收到)、auto成功收到删除,异常收到不确认、manual手动确认

none:

(1)成功处理:不考虑

(2)消费异常:队列也会直接将消息删除

auto:

(1)成功处理--不考虑

(2)消费异常:队列不会把消息删除

  • 没有进行异常捕获 --- 会一直尝试消费消息并一直报异常,队列不会把该消息删除,也就是不会确认消息
  • 进行了异常捕获 --- 消费逻辑只会执行一次并,队列会删除消息

manual:手动确认模式(肯定确认和否定确认)

(1)成功处理

  • 进行了肯定确认,队列将消息进行删除
  • 没有进行肯定确认,队列会将消息从ready至为unacked

(2)消费异常

下面的情况是进行了异常的捕获 

  • 进行否定确认,但不重新入队 --- 队列会直接将消息删除
  • 进行了否定确认,并且重新入队 --- 消息会重新入队,也就是队列不会将消息删除

以上是消息确认机制的情况总结。

下面展示代码:

配置文件:除了manual模式,其他模式只需要配置了就会生效

mamual模式的确认机制:

@RabbitListener(queues = Constant.ACK_QUEUE)
public void ackQueue2(Message message, Channel channel) throws IOException {
    long deliveryTag = message.getMessageProperties().getDeliveryTag();
    System.out.println("消息是:"+new String(message.getBody()));
    System.out.println("deliveryTag下标是:"+deliveryTag);
    try {
        System.out.println("消费异常前");
        int a=10/0;
        System.out.println("消费异常后");
        channel.basicAck(deliveryTag, false);
    }catch (Exception e) {
        System.out.println("消费异常");
        channel.basicNack(deliveryTag, false, true);
    }
}

下面这一段写的无需观看,都是一些学习时写的混乱思路


Spring-AMQP提供了三种消息确认机制

public enum AcknowledgeMode {
    NONE ,
    MANUAL ,
    AUTO ;
}
  1. AcknowledgeMode.NONE:这种也就是对应自动确认模式。消息一旦投递给消费者,不关消费者是否成功处理,RabbitMQ都会自动确认消息,就会把消息删除。
  2. AcknowledgeMode.AUTO(默认):这种模式下,消费者处理成功会自动确认消息,但是如果处理失败就会抛出异常,不会确认消息。
  3. AcknowlegeMode.MANUAL:手动确认模式。消费者必须在成处理消息后显示的调用basicAck方法来确认消息。如果消息未被确认,RabbitMQ会认为消息尚未被成功处理,并且会重新投递消息。

(1)收到确认的两个方法

(2)先准备好下面的代码

1)常量类

public class Constant {

    //消息确认机制
    public static final String ACK_QUEUE = "ack_queue";
    public static final String ACK_EXCHANGE = "ack_exchange";
    
}

2)配置类和配置文件

@Configuration
public class AckConfig {

    //消息确认机制
    @Bean("ackQueue")
    public Queue ackQueue() {
        return QueueBuilder.durable(Constant.ACK_QUEUE).build();
    }

    @Bean("ackDirectExchange")
    public DirectExchange ackDirectExchange() {
        return ExchangeBuilder.directExchange(Constant.ACK_EXCHANGE).build();
    }

    @Bean("ackBinding")
    public Binding ackBinding(@Qualifier("ackDirectExchange") DirectExchange ackDirectExchange,
                              @Qualifier("ackQueue") Queue ackQueue) {
        return BindingBuilder.bind(ackQueue).to(ackDirectExchange).with("ack");
    }


}
spring:
  rabbitmq:
    addresses: amqp://study:study@8.138.121.41:5672/extension1
    listener:
      simple:
        acknowledge-mode: none #消息确认模式

3)生产者

@RequestMapping("/produce")
@RestController
public class ProduceController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @RequestMapping("/ack")
    public String ack() {
        rabbitTemplate.convertAndSend(Constant.ACK_EXCHANGE,"ack","这是一条ack消息");
        return "消息发送成功";
    }
}

4)消费者

@Component
public class AckListener {


    @RabbitListener(queues = Constant.ACK_QUEUE)
    public void ackQueue(Message message, Channel channel) {
        System.out.println("接受到消息:"+ new String(message.getBody()));
    }
}

程序正常运行:

(3)消息确认模式

1)第一种:none

正常运行:会直接确认消息并且从队列中删除

程序抛出异常:也会自动确认消息并且从队列中删除

小结:只要消费者把消息成功发送出去,不管消费者如何处理,都会自动确认并且删除消息

2)第二种:auto

正常运行:会直接确认消息并且从队列中删除

异常处理:消息不会确认,并且消息会不停地尝试重新入队

小结: 消费者成功处理消息就会确认消息,发生异常不会确认并尝试重新发送消息。

3)第三种:manual

正常情况并确认:需要手动确认消息

@Component
public class AckListener {


    @RabbitListener(queues = Constant.ACK_QUEUE)
    public void ackQueue(Message message, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();//消息的序号
        try {
            System.out.println("异常情况前");
            int a=10/0;
            System.out.println("异常情况后");
            System.out.println("接受到消息:"+ new String(message.getBody()));
            channel.basicAck(deliveryTag, false);//收到确认消息
        }catch (Exception e) {
            System.out.println("尝试重新入队列");
        }
    }
}

这种就是正常的情况,不演示

正确情况不确认:会在队列中一直存在,不确认就会一直存在,消费者也会一直拿到该消息。如果下一个消费者进行了确认才会删除

异常情况并确认:在捕获异常中进行否定确认(一样也会确认消息)

@Component
public class AckListener {


    @RabbitListener(queues = Constant.ACK_QUEUE)
    public void ackQueue(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();//消息的序号
        try {
            System.out.println("异常情况前");
            int a=10/0;
            System.out.println("异常情况后");
            System.out.println("接受到消息:"+ new String(message.getBody()));
            channel.basicAck(deliveryTag, false);//收到确认消息
        }catch (Exception e) {
            channel.basicNack(deliveryTag,false,false);//b:非批量确认,b1:不进行重新入队
            //System.out.println("尝试重新入队列");
        }
    }
}

异常情况确认并重新入队:消息重新入队,并且一直尝试重新消费

2.持久性

背景:RabbitMQ的持久性也是保证可靠性的一种机制。RabbitMQ持久性分为交换机持久性、队列持久性和消息持久性。

2.1.交换机持久性

(1)持久性定义

这里交换机的持久性是指在RabbitMQ重启前后,交换机是否还存在。如果设置为持久性,则会一直存在,反之重启后交换机就会被删除。

RabbitMQ重启的定义:在服务器上进行重启启动。比如在linux上使用systemctl restart rabbitmq-server.service进行重新启动

(2)Spring设置交换机持久化

spring集成进来的交换机在创建的时候是默认为持久化的

ExchangeBuilder. topicExchange (Constant. ACK_EXCHANGE_NAME ).durable(true).build()

如果要设置成非持久化,只需要设置成false即可。

ExchangeBuilder.directExchange(Constant.PRE_EXCHANGE).durable(false).build();

(3)持久化与非持久化演示

持久化:交换机仍然存在

非持久化:重启后框框中的队列已经不见了

2.2.队列持久性

(1)持久性定义

定义:队列持久化和交换机一样,指的都是RabbitMQ服务器重启前后是否还存在。持久化,服务器重启后仍然存在,队列中的消息也会存在;非持久化,当服务器重启后队列就会被删除,队列中的消息也会被删除。

(2)设置持久化

创建的队列默认是持久化的

设置的方法: 

QueueBuilder.durable(Constant.ACK_QUEUE).build();//持久化(默认)
QueueBuilder.nonDurable(Constant.PRE_QUEUE).build();//非持久化队列

(3)持久化与非持久化演示

持久化:重启前后消息队列存在,如果里面有消息,也仍然存在

非持久化:重启后队列消失,消息也消失

(4)可靠性的保证

如果只是队列持久性,但是消息不持久性,MQ重启之后,消息仍然会消失,也就是不可靠。所以要想保证消息可靠性,就需要同时设置消息持久性和队列持久化。

2.3.消息持久性

(1)定义

消息的持久化,放入队列中如果是持久化,那个mq服务器重启后消息也仍然会存在(队列也要是持久化);反之如果消息是非持久化,那么重启服务后消息将会被删除。

(2)设置消息持久性

对于消息的持久性属性,需要额外设置

消息默认是持久化的。如果队列是非持久化,消息也会被修改成非持久化。

rabbitmq-client设置消息持久化:设置第三个属性

//⾮持久化信息
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());

//持久化信息
channel.basicPublish("",QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN ,msg.getBytes());

spring内置:需要设置new MessageProperties()中的MessageProperties()属性

Message message = new Message("".getBytes(),new MessageProperties());

//1.消息非持久化
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
//2.消息持久化
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);

小结:想要消息做到持久化,必须要求队列和消息都为持久化才可以,其他情况都不可以。

3.发送方确认

要想保证消息持久化,前提是消息已经到达了服务器,那如果保证消息可以发送到服务器呢?RabbitMQ就给我们提供了两种方式:事务机制和发送方确认机制,我们介绍后者。

发送方确认机制也就是:publisher confirm 机制,该机制也分为两种:confirm确认模式和return退回模式

confirm确认模式运用在生产者和交换机之间;而return退回模式运用在交换机和队列之间。

3.1.confirm确认模式

(1)介绍confirm

1)工作的地方

2)定义

生产者发送消息后,会对发送端设置一个监听,无论是否到达Exchange,这个监听都会被执行。如果Exchange成功收到,ACK为true,如果没收到,为false

(2)使用confirm模式

1)第一步:配置确认模式

spring:
  rabbitmq:
    addresses: amqp://study:study@8.138.121.41:5672/extension1
    listener:
      simple:
        #acknowledge-mode: none #消息确认模式
        #acknowledge-mode: auto #消息确认模式
        acknowledge-mode: manual #消息确认模式
    publisher-confirm-type: correlated #消息发送确认

2)编写准备代码

已准备好的队列和交换机:

//发送方确认机制
@Bean("confirmQueue")
public Queue confirmQueue() {
    return QueueBuilder.durable(Constant.CONFIRM_QUEUE).build();
}
@Bean("confirmDirectExchange")
public DirectExchange confirmDirectExchange() {
    return ExchangeBuilder.directExchange(Constant.CONFIRM_EXCHANGE).build();
}

@Bean("confirmBinding")
public Binding confirmBinding(@Qualifier("confirmDirectExchange")Exchange exchange,@Qualifier("confirmQueue")Queue queue ) {
    return BindingBuilder.bind(queue).to(exchange).with("comfrim").noargs();
}

3)编写生产者代码

因为需要在交换机收到消息时回复确认,所以就需要回调方法,这个需要在rabbitmq客户端对象上进行设置。

设置客户端的属性:

@Configuration
public class RabbitTemplateConfig {

    //1.返回原装的rabbitmqTemplate
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        return rabbitTemplate;
    }

    //2.返回confirm模式的
    @Bean
    public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //设置confirm回调方法
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                /**
                 *  b:是否收到ack
                 *  correlationData: 消息id
                 *  s: 原因
                 */
                if(b) {
                    System.out.printf("接收到消息,消息id: %s \n",correlationData==null?"null":correlationData.getId());
                }else {
                    System.out.printf("未接收到消息,消息id: %s,原因: %s \n",correlationData==null?"null":correlationData.getId(),s);
                    //下面可以做相应的消息重发
                }
            }
        });
        return rabbitTemplate;
    }

}

一个客户端修改了属性,后续的所有操作都会生效,并且只能设置一次,多次设置会报错。 

在生产者代码中注入属性并发送消息:

@Resource(name = "confirmRabbitTemplate")
private RabbitTemplate confirmRabbitTemplate;

 //confirm确认模式
    @RequestMapping("/confirm")
    public String confirm() {
        CorrelationData correlationData  =new CorrelationData("1");//设置消息的id
        confirmRabbitTemplate.convertAndSend(Constant.CONFIRM_EXCHANGE,"confirm11","我是一条confirm test",correlationData);
        return "confirm";
    }

如果消息没有成功到达指定的交换机,ack就为false。这里不管理会消息是否到达队列。

(3)confirm模式小结

  1. 需要配置为确认模式
  2. 设置rabbitmq客户端的属性
  3. 消息只要成功发送到指定的交换机,ack就为true,也就是说明成功收到消息。
3.2.return退回模式

confirm模式和return模式可以一起使用并不互斥

(1)模式定义

定义:消息到达Exchange后,就会根据路由规则将消息放入指定的队列中。在这个过程中开启了return模式,只要存在一条消息无法到达队列(比如路由规则不匹配或者队列不存在),就可以选择把消息退回给发送者。选择是否发送回给发送者,就需要设置一个回调方法(和confirm模式一样)

(2)return退回模式代码编写

1)配置文件

这里上述的confirm已经配置好,这里无需再进行配置。

spring:
  rabbitmq:
    addresses: amqp://study:study@8.138.121.41:5672/extension1
    listener:
      simple:
        #acknowledge-mode: none #消息确认模式
        #acknowledge-mode: auto #消息确认模式
        acknowledge-mode: manual #消息确认模式
    publisher-confirm-type: correlated #消息发送确认(包含confirm和return)

2)编写准备代码

这里的队列和交换机使用上面的。

3)设置return回调方法

//2.返回confirm模式的
    @Bean
    public RabbitTemplate confirmRabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //1.设置confirm回调方法
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                /**
                 *  b:是否收到ack
                 *  correlationData: 消息id
                 *  s: 原因
                 */
                if(b) {
                    System.out.printf("接收到消息,消息id: %s \n",correlationData==null?"null":correlationData.getId());
                }else {
                    System.out.printf("未接收到消息,消息id: %s,原因: %s \n",correlationData==null?"null":correlationData.getId(),s);
                    //下面可以做相应的消息重发
                }
            }
        });
        //2.设置return回调
        rabbitTemplate.setMandatory(true);//开启
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {

            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                System.out.println("消息已被退回:"+returnedMessage);
                //具体退回逻辑省略
            }
        });
        return rabbitTemplate;
    }

4.重试机制

4.1.重试机制定义

重试机制指的是在消费消息时发送异常情况,进行对消息重新入队的机制。如果入队成功,就相当于没有消费;如果入队失败,队列就把消息删除。

(1)在消息发送时,可能会存在很多的问题导致消息发送失败,RabbitMQ的重试机制就运行消息可以重新发送

(2)一些网络问题比如:网络故障、服务器不可以、资源等问题,是有可能重新发送成功的;但是如果是由于代码的问题导致,就无法重新发送成功

(3)为了防止无休止的重试,就可以设置重试的次数

(4)重试机制需要配合消息确认机制。如果消息为自动确认,消息到达就会被自动删除,这个时候重试机制也就没有了用武之地。

4.2.重试机制代码的准备工作

(1)配置重试机制

spring:
  rabbitmq:
    listener:
      simple:
        retry:
          enabled: true #开启消费者失败重试
          initial-interval: 5000ms #初始失败等待时长为5秒
          max-attempts: 5 # 最⼤重试次数(包括⾃⾝消费的⼀次)   

(2)队列和交换机的编写

生产者:

//retry模式
@RequestMapping("/retry")
public String retry() {
    //System.out.println("我进来了");
    rabbitTemplate.convertAndSend(Constant.RETRY_EXCHANGE,"retry","我是retry ……");
    return "retry";
}

队列和交换机: 

//重试机制
@Bean("retryQueue")
public Queue retryQueue() {
    return QueueBuilder.durable(Constant.RETRY_QUEUE).build();
}
@Bean("retryDirectExchange")
public DirectExchange retryDirectExchange() {
    return ExchangeBuilder.directExchange(Constant.RETRY_EXCHANGE).build();
}

public Binding retryBinding(@Qualifier("retryDirectExchange")Exchange exchange,@Qualifier("retryQueue")Queue queue) {
    return BindingBuilder.bind(queue).to(exchange).with("retry").noargs();
}

 (3)消费者代码编写

@Configuration
public class RetryListener {
    
    //自动确认的消费者
    //@RabbitListener(queues = Constant.RETRY_QUEUE)
    public void handleMessage(Message message) throws Exception {
        System.out.println("消费消息:"+new String(message.getBody()));
        System.out.println("异常前");
        int a=9/0;
        System.out.println("异常后");
    }
    
    //自动确认的消费者
    @RabbitListener(queues = Constant.RETRY_QUEUE)
    public void handlerMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        System.out.printf("["+Constant.RETRY_QUEUE+"]接收到消息: %s, deliveryTag: %s \n", new String(message.getBody(), "UTF-8"), deliveryTag);
        try {
            int num = 3/0;
            System.out.println("业务处理完成");
            channel.basicAck(deliveryTag, false);
        }catch (Exception e){
            channel.basicNack(deliveryTag, false, true);
        }
    }
    
}

(4)回忆消息确认机制

1)NONE:消息一旦投递给消费者,不关消费者是否成功处理,RabbitMQ都会自动确认消息,就会把消息删除。

2)MANUAL:消费者必须在成处理消息后显示的调用basicAck方法来确认消息。如果消息未被确认,RabbitMQ会认为消息尚未被成功处理,并且会重新投递消息。

3)AUTO:消费者处理成功会自动确认消息,但是如果处理失败就会抛出异常,不会确认消息。

 

4.3.重试机制演示

演示三种消息确认模式下的重试机制(正常消费情况无法触发重试,所以都是在异常情况下)

(1)none模式下重试

代码发送异常并进行重试:

重试次数完成还是无法入队,就进行了报错。

(2)auto模式下重试

重试次数完成还是无法入队,就进行了报错。

以上两种统称为自动确认,都是可以搭配重试机制

(3)manual模式下重试

  • 发送异常否定确认并重新入队

使用的是否定确认的重复入队机制,并不会采取重试机制,最后队列不会把消息删除

  • 发送异常否定确认,不进行重新入队

队列删除消息,不会进行重试


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2225522.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Padavan开启IPV6

开户IPV6 如果运营商支持IPV6,在网络地图中可以看到获取到的ipv6地址 开启SSH 输入如下命令让访火墙放行IPV6 ip6tables -F ip6tables -X ip6tables -P INPUT ACCEPT ip6tables -P OUTPUT ACCEPT ip6tables -P FRWARD ACCEPT 可以写在自启动脚本里 关闭路由器的防…

CH592进一步降功耗问题

目录 问题描述: 自制模块电流测试: 成熟产品电流测试: 优化程序: 总结: 关注我,躺不平就一起卷吧 问题描述: 之前有记录过,CH592与app连接时电流过大问题&#…

群控系统服务端开发模式-应用开发-业务架构逻辑开发BaseAPI

一、加密工具开发 首先在根目录下extend文件中创建Encipher文件夹&#xff0c;用于专门开发加解密工具&#xff0c;新建RSA算法控制器并命名为Encrypt.php。然后在根目录下config文件夹中创建rsa.php文件&#xff0c;用于配置RSA算法参数。 1、秘钥生成算法 <?php /*** RS…

聚类分析算法——K-means聚类 详解

K-means 聚类是一种常用的基于距离的聚类算法&#xff0c;旨在将数据集划分为 个簇。算法的目标是最小化簇内的点到簇中心的距离总和。下面&#xff0c;我们将从 K-means 的底层原理、算法步骤、数学基础、距离度量方法、参数选择、优缺点 和 源代码实现 等角度进行详细解析。…

web3.0 开发实践

优质博文&#xff1a;IT-BLOG-CN 一、简介 Web3.0也称为去中心化网络&#xff0c;是对互联网未来演进的一种概念性描述。它代表着对现有互联网的下一代版本的设想和期望。Web3.0的目标是通过整合区块链技术、分布式系统和加密技术等新兴技术&#xff0c;构建一个更加去中心化…

[C++11] 右值引⽤与移动语义

文章目录 左值和右值左值&#xff08;Lvalue&#xff09;右值&#xff08;Rvalue&#xff09;区别 左值引⽤和右值引⽤左值引用&#xff08;Lvalue Reference&#xff09;右值引用&#xff08;Rvalue Reference&#xff09;右值引用的特点 右值引用延长生命周期右值引⽤和移动语…

【开源免费】基于SpringBoot+Vue.JS在线文档管理系统(JAVA毕业设计)

本文项目编号 T 038 &#xff0c;文末自助获取源码 \color{red}{T038&#xff0c;文末自助获取源码} T038&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

Redis 线程控制 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 线程控制 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 线程控制 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis &a…

如何指定 Maven 的 JDK 版本?

maven 路径&#xff1a;/data/maven/ jdk 路径&#xff1a;/data/jdk_1.8 1、修改 mvn 可执行文件并指定 JDK 版本 vim /data/maven/bin/mvn # 在开头新增即可... # zhurs add JAVA_HOME PATH JAVA_HOME/data/jdk_1.8 ...保存退出即可&#xff01; 为什么在此处新增&#x…

AIGC时代的数据盛宴:R语言引领数据分析新风尚

文章目录 一、AIGC时代的挑战与R语言的机遇二、R语言在AIGC时代的数据预处理与清洗三、R语言在AIGC时代的统计分析四、R语言在AIGC时代的数据可视化五、R语言在AIGC时代的自动化报告生成六、R语言在AIGC时代的优势与未来发展《R语言统计分析与可视化从入门到精通》亮点内容简介…

软工毕设开题建议

文章目录 &#x1f6a9; 1 前言1.1 选题注意事项1.1.1 难度怎么把控&#xff1f;1.1.2 题目名称怎么取&#xff1f; 1.2 开题选题推荐1.2.1 起因1.2.2 核心- 如何避坑(重中之重)1.2.3 怎么办呢&#xff1f; &#x1f6a9;2 选题概览&#x1f6a9; 3 项目概览题目1 : 深度学习社…

spring-boot(4)

1.VueRouter安装与使用 2.状态管理VueX 3.Mock 如果后端没写好&#xff0c;可以通过这个来随机返回前端需要的后端数据&#xff0c;只不过是随机的。 vue: mounted:function (){console.log("-------------------------------------Hello");axios.get("http://…

华为原生鸿蒙操作系统的发布有何重大意义和影响:

#1024程序员节 | 征文# 一、华为原生鸿蒙操作系统的发布对中国的意义可以从多个层面进行分析&#xff1a; 1. 技术自主创新 鸿蒙操作系统的推出标志着中国在操作系统领域的自主创新能力的提升。过去&#xff0c;中国在高端操作系统方面依赖于外国技术&#xff0c;鸿蒙的发布…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24目录1. Optimizing Preference Alignment with Differentiable NDCG Ranking摘要研究背景问题与挑战如何解决创新点算法模型算…

Flutter登录界面使用主题

Now, let’s use the theme we initially created in our main function for a simple login screen: 现在&#xff0c;让我们使用最初在主函数中创建的主题来制作一个简单的登录屏幕&#xff1a; Create a Login Screen Widget: Inside the main.dartfile, create a new wid…

【Qt】常用控件:按钮类控件

思维导图&#xff1a; 一、Push Button 我们可以使用 QPushButton 表示一个按钮&#xff0c;这也是当前我们最熟悉的一个控件。QPushButton继承于QAbstractButton。这个类是一个抽象类&#xff0c;是按钮的父类。 1.1 常用的属性 属性说明text按钮中的文本icon按钮中的图标ic…

「C/C++」C++ STL容器库 之 std::list 双向链表容器

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

【STM32】单片机ADC原理详解及应用编程

本篇文章主要详细讲述单片机的ADC原理和编程应用&#xff0c;希望我的分享对你有所帮助&#xff01; 目录 一、STM32ADC概述 1、ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09; 2、STM32工作原理 二、STM32ADC编程实战 &#xff08;一&am…

小程序中设置可拖动区域

官方说明文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/component/movable-area.htmlhttps://developers.weixin.qq.com/miniprogram/dev/component/movable-view.html demo&#xff1a;浮动控件上下移动交互 .wxmx <movable-area><!-- y"…