面试题:RabbitMQ 有哪几种消息模式?

文章目录

  • 前言
  • 核心组成
  • Rabbitmq 消息模式
    • 3.1 Simple 模式
      • Productor
      • Customer
    • 3.2 Fanout 模式
      • Productor
      • Customer
    • 3.3 Direct 模式
      • Productor
    • 3.4 Topic 模式
      • Productor
    • 3.5 Work 模式
      • 3.5.1 轮询分发
        • Productor
        • Worker1
      • 3.5.2 公平分发
        • Worker1
  • 防止消息丢失机制
    • 4.1 消息确认
    • 4.2 持久化
  • 使用场景
    • 解耦
    • 削峰
    • 异步


前言

Rabbitmq 是使用 Erlang 语言开发的开源消息队列系统,基于 AMQP 实现,是一种应用程序对应用程序的通信方法,应用程序通过读写出入队列的消息来通信,而无需专用连接来链接它们。消息传递指的是应用程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此通信,直接调用通常是指远程过程调用的技术。


核心组成

图片

  • Server:又称 Broker,接收客户端的连接,实现 AMQP 实体服务,安装 rabbitmq-server
  • Connection:连接,应用程序与Broker的网络连接TCP/IP/三次握手和四次挥手
  • Channel:网络信道,几乎所有操作都在 Channel 中进行,Channel 是进行消息读写的通道,客户端可以建立多个
    Channel,每个 Channel 代表一个会话任务。
  • Message:消息,服务与应用程序之间传送的数据,由 Properties 和 Body 组成,Properties
    可以对消息进行修饰,比如消息的优先级,延迟等高级特性,Body 则是消息体的内容。
  • Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机可以有若干个 exchange 和
    queue,同一个虚拟主机里面不能有相同名称的 exchange
  • Exchange:交换机,接收消息,根据路由键发送消息到绑定的队列(不具备消息存储能力)
  • Bindings:exchange 和 queue 之间的虚拟连接,binding 中可以保存多个 routing key
  • Routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息
  • Queue:队列,也称为 Message Queue,消息队列,保存消息并将它们转发给消费者

Rabbitmq 消息模式

3.1 Simple 模式

图片

Simple 模式是最简单的一个模式,由一个生产者,一个队列,一个消费者组成,生产者将消息通过交换机(此时,图中并没有交换机的概念,如不定义交换机,会使用默认的交换机)把消息存储到队列,消费者从队列中取出消息进行处理。

Productor

public class Send {private final static String QUEUE_NAME = "queue1";public static void main(String[] args) {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 2、创建连接、通道connection = factory.newConnection();channel = connection.createChannel();// 3、声明队列channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 消息内容String message = "Hello world";// 4、发送消息到指定队列channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));System.out.println(" [x] Sent '" + message + "'");} catch (TimeoutException | IOException e) {e.printStackTrace();} finally {// 关闭通道if (channel != null && channel.isOpen()) {try {channel.close();} catch (Exception e) {e.printStackTrace();}}// 关闭连接if (connection != null && connection.isOpen()) {try {connection.close();} catch (Exception e) {e.printStackTrace();}}}}
}

Customer

public class Recv {private final static String QUEUE_NAME = "queue1";public static void main(String[] args) throws IOException, TimeoutException {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setVirtualHost("/");// 2、获取 Connection和 ChannelConnection connection = factory.newConnection();Channel channel = connection.createChannel();// 3、声明队列channel.queueDeclare(QUEUE_NAME, false, false, false, null);System.out.println(" [*] Waiting for messages. To exit press CTRL+C");DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [x] Received '" + message + "'");};channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});}
}

观察可视化界面,会看到消息先会被写入到队列中,随后又被消费者消费了。

3.2 Fanout 模式

图片

Fanout——发布订阅模式,是一种广播机制。

此模式包括:一个生产者、一个交换机 (exchange)、多个队列、多个消费者。生产者将消息发送到交换机,交换机不存储消息,将消息存储到队列,消费者从队列中取消息。如果生产者将消息发送到没有绑定队列的交换机上,消息将丢失。

用 Java demo 实现此模式

Productor

public class Productor {private static final String EXCHANGE_NAME = "fanout_exchange";public static void main(String[] args) {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 2、获取连接、通道connection = factory.newConnection();channel = connection.createChannel();// 消息内容String message = "hello fanout mode";// 指定路由keyString routeKey = "";String type = "fanout";// 3、声明交换机channel.exchangeDeclare(EXCHANGE_NAME, type);// 4、声明队列channel.queueDeclare("queue1", true, false, false, null);channel.queueDeclare("queue2", true, false, false, null);channel.queueDeclare("queue3", true, false, false, null);channel.queueDeclare("queue4", true, false, false, null);// 5、绑定 channel 与 queuechannel.queueBind("queue1", EXCHANGE_NAME, routeKey);channel.queueBind("queue2", EXCHANGE_NAME, routeKey);channel.queueBind("queue3", EXCHANGE_NAME, routeKey);channel.queueBind("queue4", EXCHANGE_NAME, routeKey);// 6、发布消息channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));System.out.println("消息发送成功!");} catch (IOException | TimeoutException e) {e.printStackTrace();System.out.println("消息发送异常");}finally {// 关闭通道和连接......}}
}

Customer

public class Customer {private static Runnable runnable = new Runnable() {@Overridepublic void run() {// 创建连接工厂ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");final String queueName = Thread.currentThread().getName();Connection connection = null;Channel channel = null;try {// 获取连接、通道connection = factory.newConnection();channel = connection.createChannel();Channel finalChannel = channel;finalChannel.basicConsume(queueName, true, new DeliverCallback() {@Overridepublic void handle(String consumerTag, Delivery delivery) throws IOException {System.out.println(delivery.getEnvelope().getDeliveryTag());System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));}}, new CancelCallback() {@Overridepublic void handle(String consumerTag) throws IOException {}});System.out.println(queueName + ":开始接收消息");} catch (IOException |TimeoutException e) {e.printStackTrace();} finally {// 关闭通道和连接......}}};public static void main(String[] args) throws IOException, TimeoutException {// 创建线程分别从四个队列中获取消息new Thread(runnable, "queue1").start();new Thread(runnable, "queue2").start();new Thread(runnable, "queue3").start();new Thread(runnable, "queue4").start();}
}

执行完 Productor 发现四个队列中分别增加了一条消息,而执行完 Customer 后四个队列中的消息都被消费者消费了。

3.3 Direct 模式

图片

Direct 模式是在 Fanout 模式基础上添加了 routing key,Fanout(发布/订阅)模式是交换机将消息存储到所有绑定的队列中,而 Direct 模式是在此基础上,添加了过滤条件,交换机只会将消息存储到满足 routing key 的队列中。

在上图中,我们可以看到交换机绑定了两个队列,其中队列 Q1绑定的 routing key 为 “orange” ,队列Q2绑定的routing key 为 “black” 和 “green”。在这样的设置中,发布 routing key 为 “orange” 的消息将被路由到 Q1,routing key 为 “black” 或 “green” 的消息将被路由到 Q2

在 rabbitmq 中给队列绑定 routing_key,routing_key 必须是单词列表。

用 Java demo 实现此模式

Productor

public class Productor {private static final String EXCHANGE_NAME = "direct_exchange";public static void main(String[] args) {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 2、获取连接、通道connection = factory.newConnection();channel = connection.createChannel();// 消息内容String message = "hello direct mode";// 指定路由keyString routeKey = "email";String type = "direct";// 3、声明交换机channel.exchangeDeclare(EXCHANGE_NAME, type);// 4、声明队列channel.queueDeclare("queue1", true, false, false, null);channel.queueDeclare("queue2", true, false, false, null);channel.queueDeclare("queue3", true, false, false, null);// 5、绑定 channel 与 queuechannel.queueBind("queue1", EXCHANGE_NAME, "email");channel.queueBind("queue2", EXCHANGE_NAME, "sms");channel.queueBind("queue3", EXCHANGE_NAME, "vx");// 6、发布消息channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));System.out.println("消息发送成功!");} catch (IOException | TimeoutException e) {e.printStackTrace();System.out.println("消息发送异常");} finally {// 关闭通道和连接......}}
}

可以通过可视化页面查看,各队列绑定的 routing_key

图片

由于设置的 routing_key为 “email”,所以,应该只有 queue1 存储了一条消息。

图片

Customer 与上述 fanout 示例一致。

3.4 Topic 模式

图片

Topic 模式是生产者通过交换机将消息存储到队列后,交换机根据绑定队列的 routing key 的值进行通配符匹配,如果匹配通过,消息将被存储到该队列,如果 routing key 的值匹配到了多个队列,消息将会被发送到多个队列;如果一个队列也没匹配上,该消息将丢失。

routing_key 必须是单词列表,用点分隔,其中 * 和 # 的含义为:

  • *:1个单词
  • #:0个或多个单词

用Java demo 实现此模式

Productor

public class Productor {private static final String EXCHANGE_NAME = "topic_exchange";public static void main(String[] args) {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 2、获取连接、通道connection = factory.newConnection();channel = connection.createChannel();// 消息内容String message = "hello topic mode";// 指定路由keyString routeKey = "com.order.test.xxx";String type = "topic";// 3、声明交换机channel.exchangeDeclare(EXCHANGE_NAME, type);// 4、声明队列channel.queueDeclare("queue5",true,false,false,null);channel.queueDeclare("queue6",true,false,false,null);// 5、绑定 channel 与 queuechannel.queueBind("queue5", EXCHANGE_NAME, "*.order.#");channel.queueBind("queue6", EXCHANGE_NAME, "#.test.*");// 6、发布消息channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));System.out.println("消息发送成功!");} catch (IOException | TimeoutException e) {e.printStackTrace();System.out.println("消息发送异常");} finally {// 关闭通道和连接......}}
}

执行完 Productor 后,通过可视化页面查看到,queue 绑定的 routing_key

图片

由于上述例子中,routing_key为:“com.order.test.xxx”,那么 queue5 和 queue6 都将接收到消息。

图片

Customer 与上述实例一样,执行完 Customer 后,再次查看队列信息,queue5 和 queue6 的消息都被消费了。

3.5 Work 模式

当有多个消费者时,如何均衡消息者消费消息的多少,主要有两种模式:

  • 轮询模式分发:按顺序轮询分发,每个消费者获得相同数量的消息
  • 公平分发:根据消费者消费能力公平分发,处理快的处理的多,处理慢的处理的少,按劳分配

3.5.1 轮询分发

在这种模式下,rabbitmq 采用轮询的方式将任务分配给多个消费者,但可能出现一种情况,当分配给某一个消费者的任务很复杂时,而有些消费者接收的任务较轻量,会出现有的消费者很忙,而有的消费者处于空闲的状态,而 rabbitmq 不会感知到这种情况的发生,rabbitmq 不考虑消费者未确认消息的数量,只是盲目的分配任务。

用 Java demo 实现此模式

Productor
public class Productor {public static void main(String[] args) {// 1、创建连接工程ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 2、获取连接、通道connection = factory.newConnection();channel = connection.createChannel();// 3、向 Queue1 发布20个消息for (int i = 0; i < 20; i++) {String msg = "feiyangyang: " + i;channel.basicPublish("", "queue1", null, msg.getBytes(StandardCharsets.UTF_8));}System.out.println("消息发送成功!");} catch (IOException | TimeoutException e) {e.printStackTrace();System.out.println("消息发送异常");} finally {// 关闭通道和连接......}}
}
Worker1
public class Worker1 {public static void main(String[] args) {// 1、创建连接工厂ConnectionFactory factory = new ConnectionFactory();factory.setHost("192.168.96.109");factory.setUsername("admin");factory.setPassword("admin");factory.setVirtualHost("/");Connection connection = null;Channel channel = null;try {// 获取连接、通道connection = factory.newConnection();channel = connection.createChannel();Channel finalChannel = channel;finalChannel.basicConsume("queue1", true, new DeliverCallback() {@Overridepublic void handle(String consumerTag, Delivery delivery) throws IOException {System.out.println("Worker1" + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}, new CancelCallback() {@Overridepublic void handle(String consumerTag) throws IOException {}});System.out.println("Worker1 开始接收消息");System.in.read();} catch (IOException |TimeoutException e) {e.printStackTrace();} finally {// 关闭通道和连接......}}
}

Worker2 与 Worker1 相同

我们看下消息分发结果:

Worker1 开始接收消息
Worker1:收到消息是:feiyangyang: 0
Worker1:收到消息是:feiyangyang: 2
Worker1:收到消息是:feiyangyang: 4
Worker1:收到消息是:feiyangyang: 6
Worker1:收到消息是:feiyangyang: 8
Worker1:收到消息是:feiyangyang: 10
Worker1:收到消息是:feiyangyang: 12
Worker1:收到消息是:feiyangyang: 14
Worker1:收到消息是:feiyangyang: 16
Worker1:收到消息是:feiyangyang: 18Worker2 开始接收消息
Worker2:收到消息是:feiyangyang: 1
Worker2:收到消息是:feiyangyang: 3
Worker2:收到消息是:feiyangyang: 5
Worker2:收到消息是:feiyangyang: 7
Worker2:收到消息是:feiyangyang: 9
Worker2:收到消息是:feiyangyang: 11
Worker2:收到消息是:feiyangyang: 13
Worker2:收到消息是:feiyangyang: 15
Worker2:收到消息是:feiyangyang: 17
Worker2:收到消息是:feiyangyang: 19

可以看出,轮询分发模式就是将消息均衡的分配所有消费者。

3.5.2 公平分发

图片

为了解决 Work 轮询分发模式 这个问题,rabbitmq 使用带有 perfetchCount = 1 设置的 basicQos 方法。当消费者接受处理并确认前一条消息前,不向此消费者发送新消息,会分配给其他空闲的消费者。

Productor 代码与上述轮询模式相同,而 Customer 中稍作修改

Worker1
// Channel 使用 Qos 机制
finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", false, new DeliverCallback() {@Overridepublic void handle(String consumerTag, Delivery delivery) throws IOException {System.out.println("Worker1" + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));try {Thread.sleep(1000);// 改成手动应答finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);} catch (InterruptedException e) {e.printStackTrace();}}
}, new CancelCallback() {@Overridepublic void handle(String consumerTag) throws IOException {}
});

上述实例相较于轮询分发模式,添加了 Qos 机制,设置值为1,代表消费者每次从队列中获取几条消息,将 Worker1 的 sleep 时间设置为 1s,将 Worker2 的 sleep 时间设置为 2s,查看消息分发结果

Worker1 开始接收消息
Worker1:收到消息是:feiyangyang: 0
Worker1:收到消息是:feiyangyang: 2
Worker1:收到消息是:feiyangyang: 4
Worker1:收到消息是:feiyangyang: 5
Worker1:收到消息是:feiyangyang: 7
Worker1:收到消息是:feiyangyang: 8
Worker1:收到消息是:feiyangyang: 10
Worker1:收到消息是:feiyangyang: 11
Worker1:收到消息是:feiyangyang: 13
Worker1:收到消息是:feiyangyang: 14
Worker1:收到消息是:feiyangyang: 16
Worker1:收到消息是:feiyangyang: 17
Worker1:收到消息是:feiyangyang: 19
Worker2 开始接收消息
Worker2:收到消息是:feiyangyang: 1
Worker2:收到消息是:feiyangyang: 3
Worker2:收到消息是:feiyangyang: 6
Worker2:收到消息是:feiyangyang: 9
Worker2:收到消息是:feiyangyang: 12
Worker2:收到消息是:feiyangyang: 15
Worker2:收到消息是:feiyangyang: 18

当使用 Work 公平分发模式时,要设置消费者为手动应答,并且开启 Qos 机制。

防止消息丢失机制

4.1 消息确认

消费者完成一项任务可能需要几秒钟,如果其中一个消费者开始了一项长期任务并且只完成了部分任务而死亡,如果将 autoAck 设置为 true ,一旦 RabbitMQ 将消息传递给消费者,它会立即将其标记为删除,在这种情况下,我们将丢失所有已分派给该特定消费者但尚未处理的消息。

如果其中一个消费者宕了,rabbitmq 可以将其消息分配给其他消费者。为了确保消息不会丢失,rabbitmq 采用消息确认,消费者发回确认消息,告诉 rabbitmq 消息已经被接收并处理,此时,rabbitmq 可以放心的删除这条消息。

如果消费者在没有发送 ack 的情况下宕了,rabbitmq 将理解为该条消息未被消费者处理完,如果有其他消费者在线,将迅速重新交付给其他消费者,这样就可以确保不会丢失消息了。

默认情况下rabbitmq 会启用手动消息确认,也就是 autoAck 默认为 false,一旦我们完成了一项任务,需要手动的进行消息确认,所以 autoAck 需要保持为默认值 false,并使用如下方法进行手动应答。

channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

4.2 持久化

rabbitmq 的消息确认机制可以保证消息不会丢失,但是如果 rabbitmq 服务器停止,我们的任务仍然会丢失。

当 rabbitmq 退出或崩溃时,如果不进行持久化,队列和消息都会消失。需要做两件事来确保消息不会丢失,将队列和消息都标记为持久的。

设置队列持久

boolean durable = true;
channel.queueDeclare("hello", durable, false, false, null);

设置消息持久

channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());

将消息标记为持久性并不能完全保证消息不会丢失,当 rabbitmq 接收到消息并且还没保存时,仍然有很短的时间窗口会使消息丢失,如果需要更强的保证,可以使用发布者确认机制。

使用场景

解耦、削峰、异步

解耦

在微服务架构体系中,微服务A需要与微服务B进行通信,传统的做法是A调用B的接口。但这样做如果系统B无法访问或连接超时,系统A需要等待,直到系统B做出响应,并且A与B存在严重的耦合现象。如果引入消息队列进行系统AB的通信,流程是这样的:

系统A将消息存储到消息队列中,返回成功信息
系统B从队列中获取消息,进行处理操作
系统A将消息放到队列中,就不用关心系统B是否可以获取等其他事情了,实现了两个系统间的解耦。

使用场景:

短信、邮件通知

削峰

系统A每秒请求100个,系统可以稳定运行,但如果在秒杀活动中,每秒并发达到1w个,但系统最大处理能力只能每秒处理 1000 个,所以,在秒杀活动中,系统服务器会出现宕机的现象。如果引入 MQ ,可以解决这个问题。每秒 1w个请求会导致系统崩溃,那我们让用户发送的请求都存储到队列中,由于系统最大处理能力是每秒1000个请求,让系统A每秒只从队列中拉取1000个请求,保证系统能稳定运行,在秒杀期间,请求大量进入到队列,积压到MQ中,而系统每秒只从队列中取1000个请求处理。这种短暂的高峰期积压是没问题的,因为高峰期一旦过去,每秒请求数迅速递减,而系统每秒还是从队列中取1000个请求进行处理,系统会快速将积压的消息消费掉。

使用场景:

  • 秒杀活动
  • 团抢活动

异步

用户注册,需要发送注册邮件和注册短信,传统的做法有两种:串行、并行。

串行方式:将注册信息写库后(50ms),发送邮件(50ms),再发送短信(50ms),任务完成后,返回客户端,共耗时(150ms)
并行方式:将注册信息写库后(50ms),开启子线程让发送邮件和发送短信同时进行(50ms),返回客户端,共耗时(100ms)
引入MQ,将注册信息写库(50ms),将发送邮件和短信的操作写入队列(5s),返回客户端,而消费者什么时候从队列中取消息进行处理,不用关心,共耗时(55ms)
使用场景:

将不是必须等待响应结果的业务逻辑进行异步处理

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

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

相关文章

在WIN从零开始在QMUE上添加一块自己的开发板(一)

文章目录 一、前言二、源码编译&#xff08;一&#xff09;安装Msys2&#xff08;二&#xff09;配置GCC工具链&#xff08;三&#xff09;安装QEMU构建依赖&#xff08;四&#xff09;下载编译QEMU源码 二、QUME编程基础&#xff08;一&#xff09;QOM机制&#xff08;二&…

LabVIEW振动筛螺栓松动故障诊断

LabVIEW振动筛螺栓松动故障诊断 概述&#xff1a;利用LabVIEW解决振动筛螺栓松动的故障诊断问题。通过集成的方法&#xff0c;不仅提高了故障检测的准确性&#xff0c;还优化了维护流程&#xff0c;为类似的机械设备故障提供了可靠的解决方案。 由于工作条件复杂&#xff0c;…

Linux系统安装NFS服务器

NFS是一种网络文件系统&#xff0c;英文全称Network File System&#xff0c;通过NFS可以让不同的主机系统之间共享文件或目录。通过NFS&#xff0c;用户可以直接在本地NFS客户端读写NFS服务端上的文件&#xff0c;是非常好的共享存储工具。本篇文章将介绍如何在CentOS7上安装N…

android 开发 W/TextToSpeech: speak failed: not bound to TTS engine

问题 笔者使用TTS(TextToSpeech)对于文本内容进行语音播报&#xff0c;控制台报错 android 开发 speak failed:not bound to TTS engine详细问题 笔者核心代码&#xff1a; import android.os.Bundle; import android.speech.tts.TextToSpeech; import android.speech.tts.…

[嵌入式软件][入门篇][仿真平台] STM32F103实现LED、按键

上一篇&#xff1a;[嵌入式软件][入门篇] 搭建在线仿真平台(STM32) 文章目录 一、点亮LED灯(1) 简介(2) 示例代码(3) 仿真效果&#xff08;闪烁&#xff09; 二、按键检测(1) 简介1. 按键原理2. 检测按键端口3. 消抖 (2) 示例代码1 &#xff08;按下点亮&#xff0c;松开熄灭&a…

一个简单的Web程序(详解创建一个Flask项目后自带的一个简单的Web程序)

程序代码截图如下&#xff1a; 1.应用初始化 在创建 Flask 程序时&#xff0c;通常需要先创建一个应用实例进行应用初始化。 from flask import Flask # 应用的初始化 app Flask(__name__) 上述代码中&#xff0c;使用 Flask 类创建了一个应用实例 app。 __name__ 参数用…

mp4文件可以转成mp3音频吗

现在是个非常流行刷短视频一个年代&#xff0c;刷短视似乎成了人们休闲娱乐的一种方式&#xff0c;在日常刷短视频过程中&#xff0c;肯定会有很多同学被短视频 bgm 神曲洗脑&#xff0c;比如很多被网红翻唱带火的歌曲&#xff0c;例如其中"不负人间”&#xff0c;就是其中…

Python 散点图的绘制(Seaborn篇-03)

Python 散点图的绘制(Seaborn篇-03)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹…

BuildRoot配置RTL8822CE WIFIBT模块(BT部分)

BuildRoot配置RTL8822CE WIFI&BT模块&#xff08;WIFI部分&#xff09;-CSDN博客 WIFI部分更新一下&#xff1a; ①、rkwifibt.mk 查看了output/rockchip_rk3399_tinkerboard2/build/的两个目录都有wifi相关的ko(后面make clean之后剩下linux-headers-custom路径的)&…

使用函数计算,数禾如何实现高效的数据处理?

作者&#xff1a;邱鑫鑫&#xff0c;王彬&#xff0c;牟柏旭 公司背景和业务 数禾科技以大数据和技术为驱动&#xff0c;为金融机构提供高效的智能零售金融解决方案&#xff0c;服务银行、信托、消费金融公司、保险、小贷公司等持牌金融机构&#xff0c;业务涵盖消费信贷、小…

菜鸟关于做前、后端的整理(html、js),以及疑问

涉及到后端的接口py&#xff0c;前端html和js 这三部分就按照如下格式放到server项目主路径下&#xff0c;这样后端机可以作为一个前端server main.pystaticmain.jsmain.htmlhtml 首先是html要设定网页的显示 <!DOCTYPE html> <html> <head><title>…

电力能源实景三维可视化合集,智慧电网数字孪生

电力能源是现代社会发展和运行的基石&#xff0c;渗透于工业、商业、农业、家庭生活等方方面面&#xff0c;它为经济、生活质量、环境保护和社会发展提供了巨大的机会和潜力。图扑软件应用自研 HT for Web 强大的渲染引擎&#xff0c;助力现代化的电力能源数字孪生场景&#xf…

【Vue】vue项目中Uncaught runtime errors:怎样关闭

vue项目中Uncaught runtime errors:怎样关闭 一、背景描述二、报错原因三、解决方案3.1 只显示错误信息不全屏覆盖3.2 取消全屏覆盖 四、参考资料 一、背景描述 项目本来运行的好好&#xff0c;换了个新的浏览器&#xff0c;新的Chrome浏览器版本号是116.0.5845.97&#xff08…

视频增强修复Topaz Video AI

Topaz Video AI是一款强大的视频增强软件&#xff0c;利用人工智能技术对数千个视频进行训练&#xff0c;结合多个输入视频的帧信息来提高素材的分辨率。该软件可将视频的分辨率提高到最高8K&#xff0c;并保持真实的细节和运动一致性。同时&#xff0c;它还能自动修复视频中的…

HCIA-HarmonyOS设备开发认证-序

序 最近涉及到HarmonyOS鸿蒙系统设备开发&#xff0c;在网络上已经有很多相关资料&#xff0c;视频教程&#xff0c;我也移植了公司的一个stm32G474板卡&#xff0c;运行LiteOS-m L0系统。 一面看资料一面移植&#xff0c;遇到不少坑&#xff0c;当看到运行的LOGO时&#xff0…

protobuf学习日记 | 认识protobuf中的类型

目录 前言 一、标量数据类型 二、protobuf中的 “数组” 三、特殊类型 1、枚举类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 2、Any类型 &#xff08;1&#xff09;类型讲解 &#xff08;2&#xff09;升级通讯录 3、oneof类型 …

LeetCode、2300. 咒语和药水的成功对数【中等,排序+二分】

文章目录 前言LeetCode、2300. 咒语和药水的成功对数【中等&#xff0c;排序二分】题目及类型思路及代码 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域…

elementUI+el-upload 上传、下载、删除文件以及文件展示列表自定义为表格展示

Upload 上传组件的使用 官方文档链接使用el-upload组件上传文件 具体参数说明&#xff0c;如何实现上传、下载、删除等功能获取文件列表进行file-list格式匹配代码 文件展示列表自定义为表格展示 使用的具体参数说明文件大小展示问题&#xff08;KB/MB&#xff09;文件下载代码…

【GitHub项目推荐--微软开源的可视化工具】【转载】

说到数据可视化&#xff0c;大家都很熟悉了&#xff0c;设计师、数据分析师、数据科学家等&#xff0c;都需要用各种方式各种途径做着数据可视化的工作.....当然许多程序员在工作中有时也需要用到一些数据可视化工具&#xff0c;如果工具用得好&#xff0c;就可以把原本枯燥凌乱…

svg矢量图标在wpf中的使用

在wpf应用程序开发中&#xff0c;为支持图标的矢量缩放&#xff0c;及在不同分辨率下界面中图标元素的矢量无损缩放&#xff0c;所以常常用到svg图标&#xff0c;那么如果完 美的将svg图标运用到wpf日常的项目开发中呢&#xff0c;这里分享一下我的个人使用经验和详细步骤。 步…