RabbitMQ常见场景问题

news2024/11/19 8:35:46

RabbitMQ常见场景问题

文章目录

  • RabbitMQ常见场景问题
    • 6种工作模式
      • 1.直连模式
      • 2.发布订阅模式
      • 3.Routing路由模式
      • 4.Topic通配符模式
      • 5.Header模式
      • 6.RPC
    • 消息不丢失
      • 消息发送到交换机失败
        • 1.配置文件开启发布确认
        • 2.配置回调函数
        • 3.测试
        • 4.如何处理失败消息
      • RabbitMQ服务器故障
        • 持久化
      • 消息发送到队列失败
        • 1.配置文件开启消费确认
        • 2.配置回调函数
        • 3.测试
        • 4.如何处理失败消息
      • 消息消费失败
    • 消息幂等性(重复消费)
      • 问题
      • 测试重复消费场景
      • 解决
    • 消息有序
      • 消息消费顺序错乱原因
      • 解决
    • 消息堆积
      • 消息堆积原因
      • 解决

6种工作模式

1.直连模式

没有交换机,根据routing key直连队列

在这里插入图片描述

application.properties

server.port=8081
spring.rabbitmq.host=39.99.141.194
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=123456
spring.mvc.pathmatch.matching-strategy=ant-path-matcher

RabbitmqConfig

@Configuration
public class RabbitMqConfig {
    //1.工作队列模式
    //声明队列,同时交给spring
    @Bean(name = "work-queue")
    public Queue queue0(){
        return new Queue("work-queue");
    }
  }

send

@SpringBootTest
class RabbitmqApplicationTests {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void direct() {
        rabbitTemplate.convertAndSend("direct", "这是直连模式");
    }

}

consumer

@Controller
public class Consumer1 {
    @RabbitListener(queues = "direct")
    public void workQueue(String str){
        System.out.println("当前监听到了:"+str);
    }
}

控制台

image-20230131140125353

2.发布订阅模式

在这里插入图片描述

发布订阅模式:

1、每个消费者监听自己的队列。

2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息

RabbitmqConfig

//2.发布订阅模式
    //声明了队列
    @Bean(name = "queue1")
    public Queue queue(){
        return new Queue("publish-queue1");
    }

    @Bean(name = "queue2")
    public Queue queue2(){
        return new Queue("publish-queue2");
    }
    //广播的交换机
    //声明交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("publish-exchange");
    }
    //将队列绑定到交换机
    @Bean
    Binding bindQueue1ToFanoutExchange(@Qualifier("queue1")Queue queue, FanoutExchange  fanoutExchange){
        return   BindingBuilder.bind(queue).to(fanoutExchange);
    }
    //将队列绑定到交换机
    @Bean
    Binding bindQueue2ToFanoutExchange(@Qualifier("queue2")Queue queue,FanoutExchange  fanoutExchange){
        return   BindingBuilder.bind(queue).to(fanoutExchange);
    }

send

@Test
public void testSendPublish(){
    Map map=new HashMap<>();
    map.put("name","张三");
    map.put("age",18);
    //1.交换机的名称  2.你的规则,发布订阅模式为空 3.消息的主题
    rabbitTemplate.convertAndSend("publish-exchange","",map);
}

consumer

@Controller
public class Consumer2 {
    @RabbitListener(queues = "publish-queue1")
    public void workQueue1(Map str1){
        System.out.println("publish-queue1当前监听到了:"+str1);
    }
    @RabbitListener(queues = "publish-queue2")
    public void workQueue2(Map str2){
        System.out.println("publish-queue2当前监听到了:"+str2);
    }
    @RabbitListener(queues = "publish-queue3")
    public void workQueue3(Map str3){
        System.out.println("publish-queue3当前监听到了:"+str3);
    }
}

控制台

image-20230131161219640

3.Routing路由模式

在这里插入图片描述

路由模式:

1、每个消费者监听自己的队列,并且设置routingkey。

2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。

RabbitMQConfig

    //3.routing模式 -路由模式
    //声明了3个队列
    @Bean(name = "queue4")
    public Queue queue4(){
        return new Queue("routing-queue1");
    }
    @Bean(name = "queue5")
    public Queue queue5(){
        return new Queue("routing-queue2");
    }
    @Bean(name = "queue6")
    public Queue queue6(){
        return new Queue("routing-queue3");
    }
    //声明交换机,路由模式 DirectExchange
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("routing-exchange");
    }
    //建立队列与交换机的关系
    @Bean
    public Binding bindQueue1ToDirectExchange(@Qualifier("queue4")Queue queue,DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("info");
    }
    @Bean
    public Binding bindQueue2ToDirectExchange(@Qualifier("queue5")Queue queue,DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("waring");
    }
    @Bean
    public Binding bindQueue3ToDirectExchange(@Qualifier("queue6")Queue queue,DirectExchange directExchange){
        return BindingBuilder.bind(queue).to(directExchange).with("error");
    }

send

@Test
public void testRouting() {
    //1.交换机的名称  2.你的规则,发布订阅模式为空 3.消息的主题
    rabbitTemplate.convertAndSend("routing-exchange", "info", "这是info");
    rabbitTemplate.convertAndSend("routing-exchange", "warning", "这是warning");
//        rabbitTemplate.convertAndSend("routing-exchange", "error", "这是error");
}

consumer

@Controller
public class Consumer3 {
    @RabbitListener(queues = "routing-queue1")
    public void routing1(String string) {
        System.out.println("routing-queue1接收到:" + string);
    }

    @RabbitListener(queues = "routing-queue2")
    public void routing2(String string) {
        System.out.println("routing-queue2接收到:" + string);
    }

    @RabbitListener(queues = "routing-queue3")
    public void routing3(String string) {
        System.out.println("routing-queue3接收到:" + string);
    }
}

控制台

只有info和warning收到了

image-20230131163212117

4.Topic通配符模式

在这里插入图片描述

路由模式:

1、每个消费者监听自己的队列,并且设置带统配符的routingkey。

2、生产者将消息发给broker,由交换机根据routingkey来转发消息到指定的队列。

RabbitMQConfig

    //4.topic模式 -主题模式
    //声明了3个队列
    @Bean(name = "queue7")
    public Queue queue7() {
        return new Queue("topic-queue1");
    }

    @Bean(name = "queue8")
    public Queue queue8() {
        return new Queue("topic-queue2");
    }

    @Bean(name = "queue9")
    public Queue queue9() {
        return new Queue("topic-queue3");
    }

    //声明交换机,路由模式 DirectExchange
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange("topic-exchange");
    }

    @Bean
    public Binding bindQueue1ToTopicExchange(@Qualifier("queue7") Queue queue, TopicExchange topicExchange) {
        return BindingBuilder.bind(queue).to(topicExchange).with("ex.123.123");
    }

    @Bean
    public Binding bindQueue2ToTopicExchange(@Qualifier("queue8") Queue queue, TopicExchange topicExchange) {
        return BindingBuilder.bind(queue).to(topicExchange).with("ex.*");
    }

    @Bean
    public Binding bindQueue3ToTopicExchange(@Qualifier("queue9") Queue queue, TopicExchange topicExchange) {
        return BindingBuilder.bind(queue).to(topicExchange).with("ex.#");
    }

send

@Test
public void testTopic() {
    //1.交换机的名称  2.你的规则,发布订阅模式为空 3.消息的主题
    rabbitTemplate.convertAndSend("topic-exchange", "ex.123.123", "这是ex.123.123");
}

consumer

@Controller
public class Consumer4 {
    @RabbitListener(queues = "topic-queue1")
    public void routing1(String string) {
        System.out.println("topic-queue1接收到:" + string);
    }

    @RabbitListener(queues = "topic-queue2")
    public void routing2(String string) {
        System.out.println("topic-queue2接收到:" + string);
    }

    @RabbitListener(queues = "topic-queue3")
    public void routing3(String string) {
        System.out.println("topic-queue3接收到:" + string);
    }
}

控制台

队列1,3可以收到

image-20230131170824007

5.Header模式

6.RPC

消息不丢失

消息发送到交换机失败

1.配置文件开启发布确认

spring.rabbitmq.publisher-confirm-type=correlated

2.配置回调函数

RabbitMQConfig:

@Slf4j
@Configuration
public class RabbitMQConfig {


    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("触发confirm回调,交换机接收到了");
            } else {
                log.info("触发confirm回调函数,交换机收不到信息,原因:" + cause);
                log.info("消息对应的的CorrelationData id:" + correlationData.getId());
            }
        });
        return rabbitTemplate;
    }
}

3.测试

随便给交换机发送一条消息

image-20230131222932467

4.如何处理失败消息

在ConfirmCallback监听中,当消息发送失败,ack失败时,我们又能拿到消息的CorrelationData,所以通过CorrelationData与消息之间的关系,我们在回调函数中通过CorrelationData来获取发送失败的消息,进而对其进行下一步操作(记录或重发等)

我们可以在发消息之前,将CorrelationData作为key,消息作为value,持久化起来(例如用redis数据库),当消息成功发送到交换机,ack为true时,我们再把他从持久化层中删除,这样的话,当消息发送失败时,我们就可以通过CorrelationData,从持久层中拿到发送失败的消息了

代码改造如下:

(CacheService是封装的redis操作工具类)

RabbitMQConfig:

@Slf4j
@Configuration
public class RabbitMQConfig {
    @Autowired
    private CacheService cacheService;
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        rabbitTemplate.setReturnsCallback(returnedMessage -> {
            log.info("消息主体 message : " + returnedMessage.getMessage());
            log.info("消息主体 message : " + returnedMessage.getReplyCode());
            log.info("描述:" + returnedMessage.getReplyText());
            log.info("消息使用的交换器 exchange : " + returnedMessage.getExchange());
            log.info("消息使用的路由键 routing : " + returnedMessage.getRoutingKey());

        });
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("触发confirm回调,交换机接收到了");
                Long result = cacheService.hDelete("rabbitmq:" + correlationData.getId(), "exchange", "routingKey", "message");
                log.info("已清除redis消息备份:"+result);
            } else {
                log.info("触发confirm回调函数,交换机收不到信息,原因:" + cause);
                log.info("消息对应的的CorrelationData id:" + correlationData.getId());
                Map<Object, Object> map = cacheService.hGetAll("rabbitmq:" + correlationData.getId());
                String exchange = (String) map.get("exchange");
                String routingKey = (String) map.get("routingKey");
                String message = (String) map.get("message");
                rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
            }
        });
        return rabbitTemplate;
    }

send:

@Test
public void testConfirm() {
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    String exchange = "routing-exchange";
    String routingKey = "info";
    String message = "这是info";
    Map hashMap = new HashMap<>();
    hashMap.put("exchange", exchange);
    hashMap.put("routingKey", routingKey);
    hashMap.put("message", message);
    cacheService.hPutAll("rabbitmq:" + correlationData.getId(), hashMap);
    rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
}

模拟一个失败情况(向不存在的exchange发消息),就会自动进行重试,redis中会保存下所有的失败消息,我们定时做人工处理也是可以的

控制台

image-20230131224734521

image-20230131230312860

RabbitMQ服务器故障

持久化

开启交换机,队列,消息的持久化,可将其存储在磁盘上,可在服务重启后恢复

  1. 交换机持久化

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("publish-exchange",true,false);
    }
    
  2. 队列持久化

    @Bean("direct")
    public Queue queue0() {
        return new Queue("direct",true);
    }
    
  3. 消息持久化

    //spring的rabbitTemplate默认开启消息持久化
    rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    

消息发送到队列失败

1.配置文件开启消费确认

spring.rabbitmq.publisher-returns=true

2.配置回调函数

//消息消费确认
//mandatory:交换器无法根据自身类型和路由键找到一个符合条件的队列时的处理方式
//true:RabbitMQ会调用Basic.Return命令将消息返回给生产者
//false:RabbitMQ会把消息直接丢弃
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnsCallback(returnedMessage -> {
    log.info("消息主体 message : " + returnedMessage.getMessage());
    log.info("消息主体 message : " + returnedMessage.getReplyCode());
    log.info("描述:" + returnedMessage.getReplyText());
    log.info("消息使用的交换器 exchange : " + returnedMessage.getExchange());
    log.info("消息使用的路由键 routing : " + returnedMessage.getRoutingKey());

});

3.测试

发送一个不存在的routingKey

image-20230201181451259

4.如何处理失败消息

returnedMessage包含消息的所有信息,可以进行例如上面使用的redis进行持久化,再进行处理

消息消费失败

rabbitMQ有 ack 签收机制,简单来说就是三种模式:

AcknowledgeMode.NONE:默认推送的所有消息都已经消费成功,会不断地向消费端推送消息。所以推送出去的消息不会暂存在server端

AcknowledgeMode.AUTO: 由 spring-rabbit 依据消息处理逻辑是否抛出异常自动发送 ack(无异常)或 nack(异常)到 server 端。

AcknowledgeMode.MANUAL:模式需要人为地获取到 channel 之后调用方法向 server 发送 ack (或消费失败时的 nack )信息

消费结果结果批量操作
ack表示成功确认,使用此回执方法后,消息会被rabbitmq broker 删除
void basicAck(long deliveryTag, boolean multiple)
允许
nack表示失败确认,一般在消费消息业务异常时用到此方法,可以将消息重新投递入队列。
void basicNack(long deliveryTag, boolean multiple, boolean requeue)
允许
reject拒绝消息,与 basicNack 区别在于不能进行批量操作,其他用法很相似。
void basicReject(long deliveryTag, boolean requeue)
不允许
  • deliveryTag:表示消息投递序号,每次消费消息或者消息重新投递后,deliveryTag 都会递增。手动消息确认模式下,我们可以对指定deliveryTag的消息进行ack、nack、reject等操作。
  • multiple:为了减少网络流量,手动确认可以被批处理,值为 true 则会一次性 ack所有小于当前消息 deliveryTag 的消息。举个栗子: 假设我先发送三条消息deliveryTag分别是5、6、7,可它们都没有被确认,当我发第四条消息此时deliveryTag为8,multiple设置为 true,会将5、6、7、8的消息全部进行确认。
  1. 配置文件开启手动签收

    #多消费者轮询模式,每个消费者都能收到的未被消费的最大消息数量
    spring.rabbitmq.listener.simple.prefetch=1
    #设置消费端手动,返回分为:ack(无异常),nack(存在异常),reject(存在异常)
    spring.rabbitmq.listener.simple.acknowledge-mode=manual
    #开启重试
    spring.rabbitmq.listener.simple.retry.enabled=true
    
  2. 消费者开启手动签收,并手动抛出异常用于测试

    @RabbitListener(queues = "routing-queue1")
    public void routing1(String string, Channel channel, Message message) throws IOException {
        try {
            System.out.println("routing-queue1接收到:" + string);
            throw new RuntimeException("手动抛出异常,测试");
        } catch (Exception e) {
            log.error("消息消费出现异常,重新入队伍");
            /**
             * 出现异常,把消息重新投递回队列中,如一直有异常会一直循环投递
             * deliveryTag:表示消息投递序号。
             * multiple:是否批量确认。
             * requeue:值为 true 消息将重新入队列。
             */
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), true, true);
        }
        /**
         * 消息确认 ACK
         * deliveryTag:表示消息投递序号,每次消费消息或者消息重新投递后,deliveryTag都会增加
         * multiple:是否批量确认,值为 true 则会一次性 ack所有小于当前消息 deliveryTag 的消息。
         */
        log.info("ACK消息消费确认.....");
    
        // 消息确认 basicAck
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    
        // 消息拒绝 basicReject
        //channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
    
    }
    
  3. 测试

    send

    @Test
    public void testConfirm() {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String exchange = "routing-exchange";
        String routingKey = "info";
        String message = "这是info";
        Map hashMap = new HashMap<>();
        hashMap.put("exchange", exchange);
        hashMap.put("routingKey", routingKey);
        hashMap.put("message", message);
        cacheService.hPutAll("rabbitmq:" + correlationData.getId(), hashMap);
        rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    }
    

    会不停进行重新投递消费

    image-20230201191913345

消息幂等性(重复消费)

问题

保证MQ消息不重复的情况下,消费者消费消息成功后,在给MQ发送消息确认的时候出现了网络异常(或者是服务中断),MQ没有接收到确认,此时MQ不会将发送的消息删除,
为了保证消息被消费,当消费者网络稳定后,MQ就会继续给消费者投递之前的消息。这时候消费者就接收到了两条一样的消息。

测试重复消费场景

发送5000条数据到queue,消费端自动应答

send

@Test
public void testAgain() {
    String exchange = "routing-exchange";
    String routingKey = "warning";
    for (int i = 1; i <= 5000; i++) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        String message = "这是warning:" + i;
        rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
    }
}

image-20230201205325114

启动consumer,随后中断重启

@RabbitListener(queues = "routing-queue2")
public void routing2(String string, Channel channel, Message message) throws IOException {
    System.out.println("routing-queue2接收到:" + string);
    // 消息确认 basicAck
    //channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}

image-20230201205724867

这个我测不出来,但理论上是可能出现问题的(QAQ)

解决

如何解决消息重复消费的问题:
为了保证消息不被重复消费,首先要保证每个消息是唯一的,所以可以给每一个消息携带一个全局唯一的id,流程如下:

  1. 消费者监听到消息后获取id,先去查询这个id是否存中

  2. 如果不存在,则正常消费消息,并把消息的id存入 数据库或者redis中(下面的编码示例使用redis)

  3. 如果存在则丢弃此消息

消费者改造,以消息id为key,消息内容为value存入setnx中,设置过期时间(可承受的redis服务器异常时间,比如设置过期时间为10分钟,如果redis服务器断了20分钟,那么未消费的数据都会丢了)

/**
 * setnx,如果redis中有记录,就会返回false,说明已经消费过了,无法写入
 * @param string
 * @param channel
 * @param message
 * @throws IOException
 */
@RabbitListener(queues = "routing-queue2")
public void routing2(String string, Channel channel, Message message) throws IOException, InterruptedException {
    boolean b = cacheService.setIfAbsent("RabbitmqConsumer:"+message.getMessageProperties().getMessageId(), string, 10L, TimeUnit.MINUTES);
    if (!b) {
        return;
    }
    System.out.println("routing-queue2接收到:" + string);

}

测试,已经全部存入了redis

image-20230201220102907

消息有序

消息消费顺序错乱原因

  1. 一个queue,有多个consumer去消费,这样就会造成顺序的错误,consumer从MQ里面读取数据是有序的,但是每个consumer的执行时间是不固定的,无法保证先读到消息的consumer一定先完成操作,这样就会出现消息并没有按照顺序执行,造成数据顺序错误。

  2. 一个queue对应一个consumer,但是consumer里面进行了多线程消费,这样也会造成消息消费顺序错误。

解决

在必须保证顺序消费的业务中,单个队列对应单个消费者,单线程消费

消息堆积

消息堆积原因

  • 消息堆积即消息没及时被消费,是生产者生产消息速度快于消费者消费的速度导致的。
  • 消费者消费慢可能是因为:本身逻辑耗费时间较长、阻塞了。

解决

  1. 增加消费者消费能力,消费者内开启多线程处理消息(注意无法保证顺序消费)

  2. 建立新的queue,消费者同时订阅新旧queue,采用订阅模式

  3. 默认情况下,rabbitmq消费者为单线程串行消费(org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer类的concurrentConsumers与txSize(对应prefetchCount)都是1),设置并发消费两个关键属性concurrentConsumers和prefetchCount。concurrentConsumers:设置的是对每个listener在初始化的时候设置的并发消费者的个数;prefetchCount:每次从broker里面取的待消费的消息的个数。
    配置方法:修改application.properties:

    spring.rabbitmq.listener.concurrency=m
    spring.rabbitmq.listener.prefetch=n
    

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

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

相关文章

存量房贷利率,一种简单估算其自然年利率调整的方法。

1.摘要2022年过去了&#xff0c;总所周知LPR被多次下调&#xff0c;目前有存量房贷的朋友&#xff0c;如果&#xff08;普遍&#xff09;设置的是根据自然年LPR动态调整利率&#xff0c;到2023年2月应该注意到了比较明显的房贷金额变动。这里主要给出一种根据这个变动&#xff…

Plecs电力电子仿真专业教程-第一季 第一节 Plecs简介

Plecs电力电子仿真专业教程-第一季 第一章 Plecs是什么&#xff1f; 第一节 Plecs简介 Plecs是瑞士Plexim GmbH公司开发的系统级电力电子仿真软件PLECS。PLECS是一个用于电路和控制结合的多功能仿真软件&#xff0c;尤其适用于电力电子和传动系统。不管您是工业领域中的开发…

[架构之路-96]:《软件架构设计:程序员向架构师转型必备》-6-需求与用户用例User Case/Senario建模

第6章 需求与用户用例User Case建模备注&#xff1a;严格意义上讲&#xff0c;用户用例属于需求分析领域&#xff0c;不属于架构设计。用户用例是架构设计最重要的输入参考之一。User Case和User Senario是非常重要的描述需求的重要手段6.1 常用的4种用例技术6.1.1 用例图6.1.2…

学习Java开发按此路线规划,从10K到40K全都有了,我就是这样过来的

如果有一天我醒来时&#xff0c;发现自己的几年Java开发经验被抹掉&#xff0c;重新回到了一个小白的状态。我想要重新自学Java&#xff0c;然后找到一份自己满意的Java工作&#xff0c;我想大概只需要6个月的时间就够了&#xff0c;如果顺利的话&#xff0c;4个月也差不多。如…

用光盘怎样重装电脑系统

用光盘怎样重装电脑系统&#xff1f;重装系统&#xff0c;听起来好像很难的样子。其实没那么难&#xff0c;用光盘装还是比较容易的。下面一起看看如何用光盘重装系统吧。 工具/原料&#xff1a; 系统版本&#xff1a;win7 品牌型号&#xff1a;联想yoga13 方法/步骤&#xf…

Vue使用axios发送get请求并携带参数

前言 其实关于Vue使用axios发送get请求并携带参数&#xff0c;我之前写过一篇&#xff0c;但是昨天又发现了另外一种方式&#xff0c;所以就单独写一篇进行总结。 之前写的那篇使用get请求并携带参数都是使用的字符串拼接的方式 感兴趣可以参考&#xff1a; Vue使用axios进行g…

基于Android的校园资产管理系统

需求信息&#xff1a; 管理员用户&#xff1a; 1&#xff1a;用户注册登录&#xff1a;通过手机号码、用户名称以及密码完成用户的注册和登录 2&#xff1a;添加资产&#xff1a;添加资产的编号、名称、归属部门之后生成资产二维码&#xff0c;以及查看添加过的资产信息 3&…

amCharts Javascript Web 5.3.0 Crack

添加新的 JSON 插件&#xff0c;允许您将序列化 (JSON) 配置解析为图表。 2023 年 1 月 31 日 - 16:00新版本 特征 添加了新JSON插件&#xff0c;允许将序列化 (JSON) 配置序列化和解析为图表。 crisp&#xff08;默认&#xff1a;&#xff09;false设置已添加到Sprite。如果设…

已经拿到IB成绩的学生,应该怎么为申请大学做准备呢?

2023年将会是过渡的一年&#xff0c;前几年的高分可能一去不复返了&#xff0c;大家心里也是要做好准备。对于今年已经拿到IB成绩的孩子们&#xff0c;应该怎么为申请大学做准备呢&#xff1f;老师也给了大家一些建议。1.如何递交IB成绩给申请的大学&#xff1f;今年1月出成绩的…

Shell + Datax 动态传递时间参数模式

Datax 数据同步模式Shell 脚本实现Datax 数据同步四种模式Datax 数据全量同步模式此脚本省略...Datax 数据实时增量&#xff08;T1&#xff09;模式功能&#xff1a;实现前一天日期 00:00:00 至前一天日期 23:59:59 数据同步#&#xff01;/bin/bash # 切换至增量脚本文件存储目…

[NOI Online #3 入门组] 最急救助

题目描述: 救助中心每天都要收到很多求救信号。收到求救信号后&#xff0c;救助中心会分析求救信号&#xff0c;找出最紧急的求救者给予救助。 求救信号是一个由小写英文字母组成的字符串&#xff0c;字符串中连续三个字符依次组成sos的情况越多&#xff08;即包含子串sos的数…

【蓝桥杯单片机】工厂灯光控制系统案例解析(小蜜蜂老师基础综合实训)

工厂灯光控制系统案例解析题目流程图关键点复盘参考代码&#xff08;IO模式&#xff09;题目 流程图 关键点复盘 设备检测——移位 L1~L8在板子上是从左至右&#xff0c;但是在对P0口赋值时是16进制从高位(L8)—>低位(L0) 根据原理图&#xff0c;LED赋值0亮1灭 为了方便赋值…

OpenShift 4 - 在单节点的 OpenShift 上用 NFS Operator 实现以 RWX 访问存储

《OpenShift / RHEL / DevSecOps 汇总目录》 文本已在 OpenShift Local 4.12 环境中进行验证。 文章目录OpenShift 支持的存储访问模式用 NFS Provisioner Operator 实现 RWX 访问存储安装 NFS Operator解决安装 Operator 过程无法访问谷歌 gcr.io 上的容器镜像配置 NFSProvisi…

《零基础学机器学习》读书笔记三之基本机器学习术语

《零基础学机器学习》读书笔记三之基本机器学习术语 一、机器学习快速上手路径&#xff08;续&#xff09; 1.3 基本机器学习术语 1.3.1 特征 特征是机器学习中的输入&#xff0c;原始的特征描述了数据的属性。特征的维度指的是特征的数目。 把向量、矩阵和其他张量的维度统…

React脚手架应用(二)

1、react脚手架 脚手架简介 用来帮助程序员快速创建一个基于xxx库的模板项目 1、包含了所有需要的配置&#xff08;语法检查、jsx编译、devServer…&#xff09;&#xff1b; 2、下载好了所有相关的依赖&#xff1b; 3、可以直接运行一个简单效果&#xff1b; create-react-a…

加速企业数字化进展,小程序容器来帮忙

近年来&#xff0c;由于新冠疫情&#xff0c;诸多企业面临经济挑战&#xff0c;高效办公常常无法正常保证。在此期间&#xff0c;不少企业纷纷加快了数字化进展。 2021年&#xff0c;在Gartner新型技术成熟度曲线中我们看到&#xff1a;组装式应用、实时事件中心即服务、生成式…

软考高级系统架构师背诵要点---系统安全与系统可靠性分析

系统安全与系统可靠性分析 系统安全&#xff1a; 信息摘要、数字签名、数字信封 被动攻击&#xff1a;收集信息为主&#xff0c;破坏保密性 窃听&#xff1a;用各种可能的合法手段和非法手段窃取系统中的信息资源和敏感信 业务流分析&#xff1a;通过对系统进行长期的监听&a…

Spark08: Spark Job的三种提交模式

一、三种任务提交方式 1. 第一种&#xff0c;standalone模式 基于Spark自己的standalone集群。指定–master spark://bigdata01:7077 2. 第二种&#xff0c;是基于YARN的client模式。 指定–master yarn --deploy-mode client 使用场景&#xff1a;这种方式主要用于测试&am…

Homekit智能家居DIY-智能通断开关

智能通断器&#xff0c;也叫开关模块&#xff0c;可以非常方便地接入家中原有开关、插座、灯具、电器的线路中&#xff0c;通过手机App或者语音即可控制电路通断&#xff0c;轻松实现原有家居设备的智能化改造。 随着智能家居概念的普及&#xff0c;越来越多的人想将自己的家改…

自定义archetype脚手架完整步骤与异常情况处理方案

自定义archetype脚手架完整步骤与异常情况处理方案一、创建模板项目二、生成骨架jar包三、骨架安装到本地仓库如果出现安装位置错误的情况&#xff0c;参考以下场景处理&#xff1a;四、选择骨架&#xff0c;创建新项目创建项目时&#xff0c;如果一直卡在Generating project i…