RabbitMQ(一) - 基本结构、SpringBoot整合RabbitMQ、工作队列、发布订阅、直接、主题交换机模式

news2024/11/14 2:24:51

RabbitMQ结构

  • Publisher : 生产者

  • Queue: 存储消息的容器队列;

  • Consumer:消费者

  • Connection:消费者与消息服务的TCP连接

  • Channel:信道,是TCP里面的虚拟连接。例如:电缆相当于TCP,信道是一条独立光纤束,一条TCP连接上创建多少条信道是没有限制的。TCP一旦打开,就会出AMQP信道。无论是发布消息,接收消息,订阅队列,这些动作都是通过信道完成的。
    Broker: 一台消息服务就是一个Broker;

  • Exchange:交换机、负责接收生产者的消息,转发到队列中、交换机和队列通过路由键绑定、可以理解为每个队列都有自己的名称;
    在这里插入图片描述

SpringBoot整合RabbitMQ

  • Queue
    • 消息存放于队列中, 若是RabbitMQ挂了,则消息会丢失,因此要开启持久化, 将durable设置为true,
    • 若是没有消费者消费该队列,则该队列会自动删除, 因此需要将autoDelete参数设置为false;
    public Queue(String name) {
    	//  队列名称, 是否持久化,是否独占, 是否自动删除
        this(name, true, false, false);
    }
  • @RabbitListener
@RabbitListener(bindings=@QueueBinding(
		value= @Queue(value="${mq.config.queue.info}",autoDelete="true"),
		exchange=@Exchange(value="${mq.config.exchange}",type=ExchangeTypes.DIRECT),
		key="${mq.config.queue.info.routing.key}"
		)
		)

用来标记消费者;exchange表示交换器信息、类型;bindings表示监听器要绑定的队列、以及队列信息;
key:代表交换机和队列通过key绑定的;

  • AmqpTemplate / RabbitTempldate:
    生产者通过依赖此工具类发送消息;

先安装RabbitMQ,创建SpringBoot项目,修改配置

# 应用名称
spring.application.name=boolfilter

# 应用服务 WEB 访问端口
server.port=8080


spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

入门级别程序

发送hello world程序;
在这里插入图片描述
生产者:

public class Tut1Sender {

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private Queue queue;

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        String message = "Hello World!";
        this.template.convertAndSend(queue.getName(), message);
        System.out.println(" [x] Sent '" + message + "'");
    }
}

消费者:

@RabbitListener(queues = "hello")
public class Tut1Receiver {

    @RabbitHandler
    public void receive(String in) {
        System.out.println(" [x] Received '" + in + "'");
    }
}

将生产者、消费者注入容器;

@Configuration
@EnableScheduling
public class Tut1Config {

    @Bean
    public Queue hello() {
        return new Queue("hello");
    }

    @Bean
    public Tut1Receiver receiver() {
        return new Tut1Receiver();
    }

    @Bean
    public Tut1Sender sender() {
        return new Tut1Sender();
    }
}

运行结果:

[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’
[x] Received ‘Hello World!’
[x] Sent ‘Hello World!’

工作队列

主要思想是避免 立即执行资源密集型任务,必须等待它要完成。相反,我们将任务安排在以后完成。我们将任务封装为消息并将其发送到队列。正在运行的工作进程 在后台将弹出任务并最终执行工作
在这里插入图片描述
生产者:

public class Tut2Sender {

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private Queue queue;

    AtomicInteger dots = new AtomicInteger(0);

    AtomicInteger count = new AtomicInteger(0);

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {`在这里插入代码片`
        StringBuilder builder = new StringBuilder("Hello");
        if (dots.incrementAndGet() == 4) {
            dots.set(1);
        }
        for (int i = 0; i < dots.get(); i++) {
            builder.append('.');
        }
        builder.append(count.incrementAndGet());
        String message = builder.toString();
        template.convertAndSend(queue.getName(), message);
        System.out.println(" [x] Sent '" + message + "'");
    }

}

消费者:

@RabbitListener(queues = "hello")
public class Tut2Receiver {

    private final int instance;

    public Tut2Receiver(int i) {
        this.instance = i;
    }

    @RabbitHandler
    public void receive(String in) throws InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        System.out.println("instance " + this.instance +
                " [x] Received '" + in + "'");
        doWork(in);
        watch.stop();
        System.out.println("instance " + this.instance +
                " [x] Done in " + watch.getTotalTimeSeconds() + "s");
    }

    private void doWork(String in) throws InterruptedException {
        for (char ch : in.toCharArray()) {
            if (ch == '.') {
                Thread.sleep(1000);
            }
        }
    }
}

队列、生产者、消费者注入容器:

@Configuration
public class Tut2Config {

    @Bean
    public Queue hello() {
        return new Queue("hello");
    }

    private static class ReceiverConfig {

        @Bean
        public Tut2Receiver receiver1() {
            return new Tut2Receiver(1);
        }

        @Bean
        public Tut2Receiver receiver2() {
            return new Tut2Receiver(2);
        }
    }

    @Bean
    public Tut2Sender sender() {
        return new Tut2Sender();
    }
}

运行结果:

[x] Sent ‘Hello.1’
instance 1 [x] Received ‘Hello.1’
[x] Sent ‘Hello…2’
instance 2 [x] Received ‘Hello…2’
instance 1 [x] Done in 1.0062309s
[x] Sent ‘Hello…3’
instance 1 [x] Received ‘Hello…3’
instance 2 [x] Done in 2.0085791s
[x] Sent ‘Hello.4’
instance 2 [x] Received ‘Hello.4’

消息确认

  • SpringBoot整合RabbitMQ代码中,若消费者消费出现异常,则会重新进入队列, 一般生产环境中,是要有重试机制的;
  • 若是要关闭重试机制、则设置defaultRequeueRejected=false, 或者抛出AmqpRejectAndDontRequeueException异常,这样框架会帮我们自动提交确认channel.basicAck()
  • 重试机制也会存在问题、若是消费者服务关闭、则消息会不断重新入队、导致RabbitMQ内存最终爆满宕机;
  • 消息的ACK确认机制默认是打开的;如果忘记了ACK,后果很严重,当Consumer退出时,消息会一直重新分发,然后RabbitMq会占用越来越多的内存,由于RabbitMq会长时间运行,出现“内存泄露”是致命的

异常处理方案:

  • 使用try-catch捕捉
  • 使用重试机制、超过一定次数、则丢弃消息或放入死信队列;

spring.rabbitmq.listener.retry.max-attempts=5 //重试超过5次,消息丢弃;

公平调度与循环调度

  • 默认情况下,RabbitMQ 会将每条消息发送给下一个消费者。平均而言,每个消费者将获得相同数量的 消息。这种分发消息的方式称为轮询。 在这种模式下,调度不一定完全按照我们想要的方式工作。 若是存在两台机器,一台性能好、一台性能差, 而RabbitMQ对此一无所知,仍然会调度 消息均匀。发生这种情况是因为 RabbitMQ 只是在消息时调度消息 进入队列。它不看未确认的数量 面向消费者的消息。它只是盲目地发送每 n 条消息 给第 n 个消费者,这就导致了一台机器特别忙碌、一台机器空闲;

  • “公平调度”是Spring AMQP的默认配置。Consumer可以向服务器声明一个prefetchCount, 表示轮到自己时、自己可处理多少消息;这样RabbitMQ转发消息给消费者时、会先看Consumer正在处理的消息数量是否达到了prefetchCount, 若已达到该值,则发给其他的Consumer;

发布/订阅

在这里插入图片描述
特点:一条消息同时会被所有消费者消息;X是交换机(Exchange);交换机和队列进行绑定(Binding)
交换机负责接收生产者发送的消息,再转发消息到队列中;实现了生产者与队列的解耦;

RabbitMQ 中消息传递模型的核心思想是生产者 从不将任何消息直接发送到队列

示例1 : 广播匿名队列

发送者:

public class Tut3Sender {

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private FanoutExchange fanout;

    AtomicInteger dots = new AtomicInteger(0);

    AtomicInteger count = new AtomicInteger(0);

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        StringBuilder builder = new StringBuilder("Hello");
        if (dots.getAndIncrement() == 3) {
            dots.set(1);
        }
        for (int i = 0; i < dots.get(); i++) {
            builder.append('.');
        }
        builder.append(count.incrementAndGet());
        String message = builder.toString();
        template.convertAndSend(fanout.getName(), "", message);
        System.out.println(" [x] Sent '" + message + "'");
    }

}

消费者:

public class Tut3Receiver {

    @RabbitListener(queues = "#{autoDeleteQueue1.name}")
    public void receive1(String in) throws InterruptedException {
        receive(in, 1);
    }

    @RabbitListener(queues = "#{autoDeleteQueue2.name}")
    public void receive2(String in) throws InterruptedException {
        receive(in, 2);
    }

    public void receive(String in, int receiver) throws InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        System.out.println("instance " + receiver + " [x] Received '" + in + "'");
        doWork(in);
        watch.stop();
        System.out.println("instance " + receiver + " [x] Done in "
                + watch.getTotalTimeSeconds() + "s");
    }

    private void doWork(String in) throws InterruptedException {
        for (char ch : in.toCharArray()) {
            if (ch == '.') {
                Thread.sleep(1000);
            }
        }
    }

}

交换机、匿名队列、绑定,生产者、消费者注入容器;

public class Tut3Config {

    @Bean
    public FanoutExchange fanout() {
        return new FanoutExchange("tut.fanout");
    }

    private static class ReceiverConfig {

        @Bean
        public Queue autoDeleteQueue1() {
            return new AnonymousQueue();
        }

        @Bean
        public Queue autoDeleteQueue2() {
            return new AnonymousQueue();
        }

        @Bean
        public Binding binding1(FanoutExchange fanout,
                                Queue autoDeleteQueue1) {
            return BindingBuilder.bind(autoDeleteQueue1).to(fanout);
        }

        @Bean
        public Binding binding2(FanoutExchange fanout,
                                Queue autoDeleteQueue2) {
            return BindingBuilder.bind(autoDeleteQueue2).to(fanout);
        }
        @Bean
        public Tut3Receiver receiver() {
            return new Tut3Receiver();
        }
    }

    @Bean
    public Tut3Sender sender() {
        return new Tut3Sender();
    }
}

运行结果:

instance 1 [x] Received 'Hello.1'
instance 2 [x] Received 'Hello.1'
instance 2 [x] Done in 1.0057994s
instance 1 [x] Done in 1.0058073s
....

模拟Spring容器发布ContextRefreshedEvent事件

通常情况下,业务开发中,经常会监听该事件做扩展,例如初始化数据, 打印日志等等;
生产者:

public class AppContextSender {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void publishContextRefreshEvent() {
        rabbitTemplate.convertAndSend("contextRefreshedExchange", "", "publish refreshed event");
    }
}

消费者:

@RabbitListener(queues = {"initQueue"})
public class InitContextRefreshedConsumer {
    @RabbitHandler
    public void consum(String in) {
        System.out.println("init :"+in);
    }
}

@RabbitListener(queues = "logQueue")
public class LogContextRefreshedConsumer {
    @RabbitHandler
    public void consum(String in) {
        System.out.println("log : "+in);
    }
}

交换机、队列、绑定、生产者、消费者注入容器:

@Configuration
public class ContextRefreshedConfig {
    @Bean
    public FanoutExchange contextRefreshedExchange(){
        return new FanoutExchange("contextRefreshedExchange");
    }
    @Bean
    public AppContextSender appContextSender() {
        return new AppContextSender();
    }

    public static class ConsumerConfig {
        @Bean
        public Queue initQueue() {
            return new Queue("initQueue");
        }

        @Bean
        public Queue logQueue() {
            return new Queue("logQueue");
        }

        @Bean
        public Binding initBinding(Queue initQueue, FanoutExchange contextRefreshedExchange) {
            return BindingBuilder.bind(initQueue).to(contextRefreshedExchange);
        }
        @Bean
        public Binding logBinding(Queue logQueue, FanoutExchange contextRefreshedExchange) {
            return BindingBuilder.bind(logQueue).to(contextRefreshedExchange);
        }

        @Bean
        public InitContextRefreshedConsumer initContextRefreshedConsumer() {
            return new InitContextRefreshedConsumer();
        }

        @Bean
        public LogContextRefreshedConsumer logContextRefreshedConsumer() {
            return new LogContextRefreshedConsumer();
        }
    }

}

log : publish refreshed event
init :publish refreshed event
log : publish refreshed event
init :publish refreshed event

Direct直接模式

  • 交换器绑定多个队列,每个绑定关系有自己的路由键;
  • 之前业务开发中、有一个交换机、绑定了两个队列,一个队列用来发送邮件,一个队列用来发送短信, 像广播模式下,如果只想发邮件,则没法t做到,使用direct模式和工作模式则可以做到, 最后使用了direct

在这里插入图片描述
生产者:

public class BaseServiceSender {
    @Autowired
    private RabbitTemplate template;

    @Autowired
    private DirectExchange messageExchange;

    AtomicInteger index = new AtomicInteger(0);

    AtomicInteger count = new AtomicInteger(0);

    private final String[] keys = {"sms", "mail"};

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        //短信
        String sms = "{userName: xxx; phone:xxx}";
        template.convertAndSend(messageExchange.getName(), "sms", sms);

        //邮件
        String mail = "{userName: xxx; mail:xxx}";
        template.convertAndSend(messageExchange.getName(), "mail", mail);
    }
}

消费者:

@RabbitListener(queues = "mailQueue")
public class MailConsumer {

    @RabbitHandler
    public void consum(String in) {
        System.out.println("send mail : " + in);
    }
}

@RabbitListener(queues = "smsQueue")
public class SmsConsumer {

    @RabbitHandler
    public void consum(String in) {
        System.out.println("send sms : " + in);
    }
}

交换机、队列,绑定、消费者,生产者注入容器:

@Configuration
public class DirectConfig {
    @Bean
    public DirectExchange messageExchange() {
        return new DirectExchange("messageExchange");
    }

    @Bean
    public BaseServiceSender baseServiceSender() {
        return new BaseServiceSender();
    }

    public static class ConsumerGroup {
        @Bean
        public MailConsumer mailConsumer() {
            return new MailConsumer();
        }

        @Bean
        public SmsConsumer smsConsumer() {
            return new SmsConsumer();
        }

        @Bean
        public Queue mailQueue() {
            return new Queue("mailQueue");
        }

        @Bean
        public Queue smsQueue() {
            return new Queue("smsQueue");
        }
        @Bean
        public Binding smsBinding(DirectExchange messageExchange, Queue smsQueue){
            return BindingBuilder.bind(smsQueue).to(messageExchange).with("sms");
        }

        @Bean
        public Binding mailBinding(DirectExchange messageExchange, Queue mailQueue){
            return BindingBuilder.bind(mailQueue).to(messageExchange).with("mail");
        }
    }
}

运行结果

send mail : {userName: xxx; mail:xxx}
send sms : {userName: xxx; phone:xxx}
send sms : {userName: xxx; phone:xxx}
send mail : {userName: xxx; mail:xxx}

Topic主题模式

  • 发送到主题交换的消息不能有任意routing_key
    • 它必须是单词列表,由点分隔。这 单词可以是任何东西,一些有效的路由密钥示例: “stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”。可以有 路由密钥中随心所欲地包含多个单词,最多可达 255 个 字节。
  • 绑定密钥也必须采用相同的形式。主题交换背后的逻辑类似于直接交换 - 发送的消息带有 特定的路由键将被传递到所有队列 绑定匹配的绑定键
  • *(星号)可以代替一个词。
  • #(哈希)可以替换零个或多个单词。

在这里插入图片描述
若是消息指定的路由键为"xxx.orange.xxx", 则会匹配到Q1, 若是"lazy.xxx.xx"则是Q2;

生产者:

public class Tut5Sender {

    @Autowired
    private RabbitTemplate template;

    @Autowired
    private TopicExchange topic;

    AtomicInteger index = new AtomicInteger(0);

    AtomicInteger count = new AtomicInteger(0);

    private final String[] keys = {"quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox",
            "lazy.brown.fox", "lazy.pink.rabbit", "quick.brown.fox"};

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        StringBuilder builder = new StringBuilder("Hello to ");
        if (this.index.incrementAndGet() == keys.length) {
            this.index.set(0);
        }
        String key = keys[this.index.get()];
        builder.append(key).append(' ');
        builder.append(this.count.incrementAndGet());
        String message = builder.toString();
        template.convertAndSend(topic.getName(), key, message);
        System.out.println(" [x] Sent '" + message + "'");
    }

}

消费者:

public class Tut5Receiver {

    @RabbitListener(queues = "#{autoDeleteQueue1.name}")
    public void receive1(String in) throws InterruptedException {
        receive(in, 1);
    }

    @RabbitListener(queues = "#{autoDeleteQueue2.name}")
    public void receive2(String in) throws InterruptedException {
        receive(in, 2);
    }

    public void receive(String in, int receiver) throws
            InterruptedException {
        StopWatch watch = new StopWatch();
        watch.start();
        System.out.println("instance " + receiver + " [x] Received '"
                + in + "'");
        doWork(in);
        watch.stop();
        System.out.println("instance " + receiver + " [x] Done in "
                + watch.getTotalTimeSeconds() + "s");
    }

    private void doWork(String in) throws InterruptedException {
        for (char ch : in.toCharArray()) {
            if (ch == '.') {
                Thread.sleep(1000);
            }
        }
    }
}

交换器,队列,绑定、生产者,消费者注入容器:

@Configuration
public class Tut5Config {

    @Bean
    public TopicExchange topic() {
        return new TopicExchange("tut.topic");
    }

    private static class ReceiverConfig {

        @Bean
        public Tut5Receiver receiver() {
            return new Tut5Receiver();
        }

        @Bean
        public Queue autoDeleteQueue1() {
            return new AnonymousQueue();
        }

        @Bean
        public Queue autoDeleteQueue2() {
            return new AnonymousQueue();
        }

        @Bean
        public Binding binding1a(TopicExchange topic,
                                 Queue autoDeleteQueue1) {
            return BindingBuilder.bind(autoDeleteQueue1)
                    .to(topic)
                    .with("*.orange.*");
        }

        @Bean
        public Binding binding1b(TopicExchange topic,
                                 Queue autoDeleteQueue1) {
            return BindingBuilder.bind(autoDeleteQueue1)
                    .to(topic)
                    .with("*.*.rabbit");
        }

        @Bean
        public Binding binding2a(TopicExchange topic,
                                 Queue autoDeleteQueue2) {
            return BindingBuilder.bind(autoDeleteQueue2)
                    .to(topic)
                    .with("lazy.#");
        }

    }

    @Bean
    public Tut5Sender sender() {
        return new Tut5Sender();
    }

}

运行结果:

[x] Sent ‘Hello to lazy.orange.elephant 1’
instance 2 [x] Received ‘Hello to lazy.orange.elephant 1’
instance 1 [x] Received ‘Hello to lazy.orange.elephant 1’
[x] Sent ‘Hello to quick.orange.fox 2’
[x] Sent ‘Hello to lazy.brown.fox 3’
instance 1 [x] Done in 2.0110456s

RPC远程过程调用

RabbitMQ也实现了RPC的功能,但是业务开发中,根本没有使用场景,RPC要么使用Dubbo, 要么使用OpenFeign, 使用RabbitMQ做RPC的信息,目前都没有看到;

总结

  • 就目前来说、工作队列、发布订阅两个模式,业务开发中会使用到,其他的消息场景很少见。
  • 底层是基于RabbitMQ-client做的封装出RabbitTempldate使用;除非远古项目,否则不推荐使用RabbitMQ-Client原生API写,太费时间了。我写了一会就放弃了

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

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

相关文章

动手学深度学习(二)线性神经网络

推荐课程&#xff1a;跟李沐学AI的个人空间-跟李沐学AI个人主页-哔哩哔哩视频 目录 一、线性回归 1. 线性模型 2. 损失函数&#xff08;衡量预估质量&#xff09; 3.梯度下降算法&#xff08;优化算法&#xff09; 3.1 梯度下降公式 3.2 选择学习率 3.3 小批量随机梯度下…

2023华数杯数学建模C题完整5问代码思路分析

目前已经写出2023华数杯C题母亲身心健康对婴儿成长的影响全部5问的完整代码和42页论文&#xff08;正文30页&#xff0c;论文部分摘要如下&#xff1a; 本文共解决了五个问题&#xff0c;涉及婴儿行为特征、睡眠质量与母亲的身体指标和心理指标的关系&#xff0c;以及如何优化…

YOLOv5模型压缩方法:综述

文章目录 YOLOv5模型压缩方法&#xff1a;综述摘要1、介绍2、剪枝2.1、修剪的显著性标准2.1.1、 ℓn-norm2.1.2、 特征图激活 YOLOv5模型压缩方法&#xff1a;综述 摘要 Model Compression Methods for YOLOv5: A Review (arxiv.org) 在过去的几年里&#xff0c;广泛的研究致…

vue3引入video.js

一.引入video.js yarn add video.js videojs-player/vue --save 或者 npm install video.js videojs-player/vue --save 二.vue3项目main.js引入 import VueVideoPlayer from "videojs-player/vue" import "video.js/dist/video-js.css" const app cr…

DP(各种模型)

数字三角形模型 摘花生 Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图)&#xff0c;从西北角进去&#xff0c;东南角出来。 地里每个道路的交叉点上都有种着一株花生苗&#xff0c;上面有若干颗花生&#xff0c;经过一株花生苗就…

Doris(三)-集群部署3个FE+3个BE

前置 1&#xff09;配置java环境 1st 解压jdk包 unzip jdk1.8.0_171-amd64.zip 2nd 配置环境变量 vim /etc/profile#文末添加JAVA_HOME/data/jdk1.8.0_171-amd64 PATH$JAVA_HOME/bin:$PATHexport PATH JAVA_HOME3rd 启用配置 source /etc/profile 4th 验证 java -versi…

离散 Hopfield 神经网络的分类与matlab实现

1 案例背景 1.1离散 Hopfield 神经网络学习规则 离散型 Hopfield神经网络的结构、工作方式,稳定性等问题在第9章中已经进行了详细的介绍,此处不再赘述。本节将详细介绍离散Hopfield神经网络权系数矩阵的设计方法。设计权系数矩阵的目的是: ①保证系统在异步工作时的稳…

Consul实战

Consul实战 什么是Consul Consul是一种为分布式系统提供服务发现、配置共享和健康检查的开源工具&#xff1b; 可以用来做微服务架构里的注册中心和配置中心。Consul的特定和功能有&#xff1a; 1.服务发现 consul允许微服务注册自己的实例到Consul, 并查询consul来获取可用的…

flex 弹性布局

Flex 布局的使用 任何一个容器都可以指定为 Flex 布局。 .box{ display: flex; //flex作为display的一个属性使用 } 行内元素也可以使用 Flex 布局。 .box{ display: inline-flex; } 注意&#xff1a;设为 Flex 布局以后&#xff0c;子元素的float、clear和vertical-align…

jmeter使用步骤

jmeter 使用步骤 1&#xff0c;进入jmeter目录中的bin目录&#xff0c;双击jmeter.bat 打开 2&#xff0c;右键test plan 创建线程组 3&#xff0c;配置线程组参数 4&#xff0c;右键刚刚创建的线程组&#xff0c;创建请求&#xff0c;填写请求地址 5&#xff0c;需要携带to…

【力扣刷题 | 第二十四天】

目录 前言&#xff1a; 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 总结 前言&#xff1a; 今晚我们爆刷动态规划类型的题目。 416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这…

MPP架构和Hadoop架构的区别

1. 架构的介绍 mpp架构是将许多数据库通过网络连接起来&#xff0c;相当于将一个个垂直系统横向连接&#xff0c;形成一个统一对外的服务的分布式数据库系统。每个节点由一个单机数据库系统独立管理和操作该物理机上的的所有资源&#xff08;CPU&#xff0c;内存等&#xff09…

flask------消息闪现 flash

1介绍 flask提供了一个非常有用的flash()函数&#xff0c;它可以用来“闪现”需要提示给用户的消息&#xff0c;比如当用户登录成功后显示“欢迎回来&#xff01;”。在视图函数调用flash()函数&#xff0c;传入消息内容&#xff0c;flash&#xff08;&#xff09;函数把消息存…

【网络基础进阶之路】设计网络划分的实战详解

PS&#xff1a;本要求基于华为的eNSP模拟软件进行 具体要求&#xff1a; 完成步骤&#xff1a; 1、对192.168.1.0/24进行子网划分 2、对每一个路由器进行IP的配置 3、开始静态路由的书写&#xff0c;在写之前&#xff0c;我们可以先对每一个路由器写一条通向右边的缺省路由&…

【C++入门到精通】C++入门 —— 内存管理(new函数的讲解)

目录 一、C/C内存分布 1. 栈&#xff08;Stack&#xff09; 2. 堆&#xff08;Heap&#xff09; 3. 全局区/静态区&#xff08;Global Area/Static Area&#xff09; 4. 常量区&#xff08;Constant Area&#xff09; 5. 代码区&#xff08;Code Area&#xff09; 二、C…

【HAL库】STM32CubeMX开发----STM32F407----LAN8720A----移植FreeModbus实现ModbusTCP

前言 本次实验以 STM32F407VET6 芯片为MCU&#xff0c;使用 25MHz 外部时钟源。 以太网PHY层芯片为 LAN8720A&#xff0c;移植FreeModbus实现ModbusTCP网口通信。 具体内容参考文章&#xff1a;【HAL库】STM32CubeMX开发----STM32F407----ETHLAN8720ALWIP----ping通 本次移植…

基于开源模型搭建实时人脸识别系统(三):人脸关键点、对齐模型概览与模型选型

续 基于开源模型搭建实时人脸识别系统&#xff08;二&#xff09;&#xff1a;人脸检测概览与模型选型_CodingInCV的博客-CSDN博客 摘要 人脸对齐&#xff08;face alignment&#xff09;或者人脸关键点&#xff08;face alignment&#xff09;是定位人脸上的关键点&#xff…

chatGLM 本地部署(windows+linux)

chatGLM算是个相对友好的模型&#xff0c;支持中英文双语的对话交流&#xff0c;清华出的 我的教程无需特别的网络设置&#xff0c;不过部分情况因为国内网络速度慢&#xff0c;需要反复重复 chatGLM github地址 一、硬件需求 N卡8G显存以上&#xff0c;最好16G以上&#xff…

redis 集群 1:李代桃僵 —— Sentinel

目前我们讲的 Redis 还只是主从方案&#xff0c;最终一致性。读者们可思考过&#xff0c;如果主节点凌晨 3 点突发宕机怎么办&#xff1f;就坐等运维从床上爬起来&#xff0c;然后手工进行从主切换&#xff0c;再通知所有的程序把地址统统改一遍重新上线么&#xff1f;毫无疑问…

电路暂态过程

本文仅提取了课程的部分内容&#xff0c;原视频课程如下&#xff1a;姜三勇《电工学》暂态过程 电路暂态过程——产生的原因&#xff1a; 1、内部原因&#xff1a;电路内部含有储能元件&#xff08;如&#xff1a;电容、电感&#xff09;&#xff0c;其中存储的能量不能发生改…