MessageQueue --- RabbitMQ

news2024/11/25 4:47:25

MessageQueue --- RabbitMQ

  • RabbitMQ Intro
  • RabbitMQ 核心概念
  • RabbitMQ 分发类型
  • Dead letter (死信)
  • 保证消息的可靠传递

RabbitMQ Intro

  • 2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
  • 低延迟:RabbitMQ 提供了低延迟的消息传递,可以在毫秒级别内将消息从生产者传递到消费者。
  • 高吞吐量:RabbitMQ 能够处理大量的消息并实现高吞吐量。它使用多线程和预取机制来提高消息处理的效率。
  • 可扩展性:RabbitMQ 可以通过水平扩展来处理更多的消息流量。可以通过添加更多的节点、使用集群和队列分区等方式来扩展 RabbitMQ。
  • 持久化支持:RabbitMQ 支持将消息和队列持久化到磁盘,以确保消息的可靠性和持久性。这意味着即使在 RabbitMQ 重启后,消息仍然可以保留,不会丢失。
  • 多种消息传递模式:RabbitMQ 支持多种消息传递模式,如点对点、发布/订阅和请求/响应等。这使得 RabbitMQ 在各种场景下都能够灵活应用。
  • 负载均衡:RabbitMQ 提供了负载均衡机制,可以将消息均衡地分发给多个消费者,以实现更好的资源利用和处理能力。
  • 可靠性保证:通过使用确认机制、持久化和事务等特性,RabbitMQ 提供了可靠性保证,确保消息的可靠传递和处理。

RabbitMQ 核心概念

在这里插入图片描述
消息的路由过程如下:

  • 生产者发布消息时,将消息和指定的 Routing Key 一起发送到交换机。
  • 交换机根据 Binding Key 和 Routing Key 的匹配规则,将消息路由到一个或多个绑定的队列。
  • 绑定 Key 和 Routing Key 的匹配规则可以根据交换机的类型而有所不同。
  • 在直接交换机(Direct Exchange)中,Binding Key 必须与 Routing Key 完全匹配。
  • 在主题交换机(Topic Exchange)中,Binding Key 与 Routing Key 使用通配符进行模式匹配。
  • 在扇形交换机(Fanout Exchange)中,Binding Key 不起作用,消息会被广播到所有绑定的队列。
  • Routing Key(路由键):
  • Routing Key 是在消息发布时与消息一起发送的属性。
  • 在发布消息时,生产者可以指定一个 Routing Key,用于描述消息的特性或目标。
  • Routing Key 可以是任意的字符串,通常是由一些特定的标识符或关键词组成,以便用于消息的过滤和路由。
  • Binding Key(绑定键):
  • Binding Key 是用于绑定队列和交换机的属性。
  • 在 RabbitMQ 中,通过创建绑定(Binding)将队列和交换机关联起来,以便消息能够正确地路由到队列。
  • 绑定是基于 Binding Key 进行的,它定义了交换机将消息路由到哪些队列。
  • Binding Key 可以是一个或多个单词组成的字符串,也可以是符号“#”和“*”的组合,用于匹配 Routing Key。

名词解释:

  • 生产者(Producer):发送消息的应用。
  • 消费者(Consumer):接收消息的应用。
  • 队列(Queue):存储消息的缓存。
  • 消息(Message):由生产者通过RabbitMQ发送给消费者的信息。
  • 连接(Connection):连接RabbitMQ和应用服务器的TCP连接。
  • 信道(Channel):连接里的一个虚拟通道,通过消息队列发送或者接收消息时,都是通过信道进行的。
  • 交换机(Exchange):交换机负责从生产者那里接收消息,并根据交换类型分发到对应的消息队列里。
  • 代理(Broker):接收和分发消息的应用,RabbitMQ Server就是Message Broker。
  • 虚拟主机(Virtual host):出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue 等.
  • Example:每个环境配置一个virtual host

RabbitMQ 分发类型

Direct Exchange

  • Direct exchange使用完全匹配的方式进行消息路由
  • 当一个消息发送到Direct exchange时,它会将消息的路由键与绑定到交换机上的队列的绑定键(binding key)进行比较。如果路由键与某个队列的绑定键完全匹配,那么该消息将被路由到该队列

在这里插入图片描述

  • exchange :pdf_events
    Queue A:create_pdf_queue
    交换机(pdf_events)和队列 A(create_pdf_queue)之间的绑定键:pdf_create
  • exchange :pdf_events
    Queue B:pdf_log_queue
    交换机(pdf_events)和队列 B(pdf_log_queue)之间的绑定键:pdf_log
  • 示例:
  • 例如,一个带有routing key为 pdf_log 的消息被发送到交换机 pdf_events
  • 该消息会被路由到 pdf_log_queue,因为routing key(pdf_log)与binding key(pdf_log)匹配
  • 如果消息的路由键与任何绑定键都不匹配,那么该消息将被丢弃。

Topic Exchange

  • 在 Topic Exchange 中,消息的路由键和绑定键都使用通配符来进行匹配。路由键是消息的属性,而绑定键是在创建绑定时指定的。通配符可以帮助实现更灵活的消息路由,使得消息可以根据特定的模式进行匹配和分发。
  • 通配符符号:
  • *(星号):表示匹配一个单词(单词由点号分隔)
  • #(井号):表示匹配零个或多个单词(单词由点号分隔)

在这里插入图片描述

  • Consumer A is interested in all the agreements in Berlin.
  • Exchange: agreements
  • Queue A name: berlin_agreements
  • Binding key: agreements.eu.berlin.#
  • Example of message routing key that matches:
  • agreements.eu.berlin
  • agreements.eu.berlin.store
  • Consumer B is interested in all the agreements.
  • Exchange: agreements
  • Queue B name: all_agreements
  • Binding key: agreements.#
  • Example of message routing key that matches:
  • agreements.eu.berlin
  • agreements.us
  • Consumer C is interested in all agreements for European head stores.
  • Exchange: agreements
  • Queue C name: store_agreements
  • binding key: agreements.eu.*.store
  • Example of message routing keys that will match:
  • agreements.eu.berlin.store
  • agreements.eu.stockholm.store

Fanout exchange

  • Fanout Exchange(广播交换机)是 RabbitMQ 中的一种交换机类型。它的工作原理是将消息广播到与之绑定的所有队列,无论绑定键的匹配情况如何。
  • Fanout Exchange 不关心消息的路由键,它会简单地将收到的消息复制并发送到所有与之绑定的队列

在这里插入图片描述

Header exchange

  • Header Exchange(头交换机)是 RabbitMQ 中的一种交换机类型。它使用消息的头部属性(Header)来匹配与之绑定的队列,而不依赖于路由键或绑定键。
  • 在 Header Exchange 中,消息的头部属性是消息的一部分,它包含了一组键值对,用于描述消息的特征和属性。与其他类型的交换机不同,Header Exchange 不关心消息的路由键,而是根据消息头部属性的匹配情况来确定消息的路由。
  • 绑定到 Header Exchange 的队列可以定义一个或多个匹配规则,这些规则由键值对的匹配条件组成。当消息的头部属性与队列的匹配规则完全匹配时,消息会被路由到对应的队列。

在这里插入图片描述

  • Message 1 is published to the exchange with header arguments (key = value): “format = pdf”, “type = report”.
  • Message 1 is delivered to Queue A because all key/value pairs match, and Queue B since “format = pdf” is a match (binding rule set to “x-match =any”).
  • Message 2 is published to the exchange with header arguments of (key = value): “format = pdf”.
  • Message 2 is only delivered to Queue B. Because the binding of Queue A requires both “format = pdf” and “type = report” while Queue B is configured to match any key-value pair (x-match = any) as long as either “format = pdf” or “type = log” is present.
  • Message 3 is published to the exchange with header arguments of (key = value): “format = zip”, “type = log”.
  • Message 3 is delivered to Queue B since its binding indicates that it accepts messages with the key-value pair “type = log”, it doesn’t mind that “format = zip” since “x-match = any”.
  • Queue C doesn’t receive any of the messages since its binding is configured to match all of the headers (“x-match = all”) with “format = zip”, “type = pdf”. No message in this example lives up to these criterias.
  • It’s worth noting that in a header exchange, the actual order of the key-value pairs in the message is irrelevant.

配置RabbitMQ 示例代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class DirectExchangeExample {
    private static final String EXCHANGE_NAME = "direct_logs";
    private static final String QUEUE_NAME = "my_queue";
    private static final String ROUTING_KEY = "info";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 声明一个 Direct Exchange
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

            // 声明一个队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            // 绑定队列到 Direct Exchange,并指定绑定键
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

            // 定义消息处理函数
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
                System.out.println("Received message: " + message);
            };

            // 消费消息
            channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});

            System.out.println("Press any key to exit.");
            System.in.read();
        }
    }
}

Dead letter (死信)

  • 死信(Dead Letter)是指在消息队列中无法被正常消费和处理的消息。当消息满足一定的条件时,它们可以被标记为死信并被发送到专门的死信队列中,以便进一步处理或分析
  • 死信来源
  • 消息 TTL 过期
  • 队列达到最大长度(队列满了,无法再添加数据到 mq 中)
  • 消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false(不再重新入队)
  • 死信队列(Dead Letter Queue)是一个特殊的队列,用于接收死信消息。一旦消息被发送到死信队列,就可以根据需要进行进一步的处理,例如重新投递、持久化、记录日志或者进行分析。
  • 使用死信机制的好处包括:
  • 错误处理:当消息无法被正常处理时,可以将其发送到死信队列,以便进一步处理错误情况,例如记录日志或者通知管理员。
  • 重试机制:如果消息在一定时间内未能被消费成功,可以将其发送到死信队列,并设置重试策略,例如延时重试或者指数退避重试。
  • 延迟消息:通过结合延迟队列和死信队列,可以实现延迟消息的功能。当消息的延迟时间到达时,将其发送到死信队列,然后再从死信队列中重新投递到目标队列,实现延迟消息的效果。

在这里插入图片描述

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class DeadLetterExample {
    private static final String EXCHANGE_NAME = "normal_exchange";
    private static final String QUEUE_NAME = "normal_queue";
    private static final String DLX_EXCHANGE_NAME = "dlx_exchange";
    private static final String DLX_QUEUE_NAME = "dlx_queue";
    private static final String DLX_ROUTING_KEY = "dlx_routing_key";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {
            // 创建普通交换机和队列
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

            // 创建死信交换机和队列
            channel.exchangeDeclare(DLX_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            channel.queueDeclare(DLX_QUEUE_NAME, false, false, false, null);
            channel.queueBind(DLX_QUEUE_NAME, DLX_EXCHANGE_NAME, DLX_ROUTING_KEY);

            // 设置普通队列的死信参数
            Map<String, Object> arguments = new HashMap<>();
            arguments.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME);
            arguments.put("x-dead-letter-routing-key", DLX_ROUTING_KEY);
            channel.queueDeclare(QUEUE_NAME, false, false, false, arguments);

            // 定义消息处理函数
            DeliverCallback deliverCallback = (consumerTag, delivery) -> {
                String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
                System.out.println("Received message: " + message);
                // 手动确认消息
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
            };

            // 消费消息
            channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> {});

            System.out.println("Press any key to exit.");
            System.in.read();
        }
    }
}

保证消息的可靠传递

要确保消息的可靠传递,可以采取以下几个步骤:

  • 持久化消息:将消息和队列都设置为持久化。这样,在 RabbitMQ 重启后,持久化的队列和消息会被恢复,避免消息丢失。可以在消息的发布端设置消息的持久化属性,以及在队列声明时设置队列的持久化属性。
  • 使用确认机制(Acknowledgement):在消费者处理消息后,发送确认消息给 RabbitMQ,告知消息已经成功处理。RabbitMQ 收到确认后才会将消息从队列中删除,确保消息不会丢失。确认机制可以通过在消费者端手动发送确认消息(basicAck)或使用自动确认模式(autoAck)来实现。
  • 使用发布者确认(Publisher Confirms):在消息的发布端启用发布者确认模式。通过将 confirm.select 设置为 true,可以让发布者等待 RabbitMQ 发送确认消息,表示消息已经成功到达交换机。如果没有收到确认消息,发布者可以选择重新发送消息,确保消息的可靠传递。
  • 设置合适的消息 TTL(Time-to-Live):可以为消息设置 TTL,即消息的存活时间。如果消息在指定的时间内没有被消费,RabbitMQ 可以将其标记为过期并进行相应的处理,例如发送到死信队列或丢弃。
  • 使用事务(Transactions):可以通过开启事务来确保消息的可靠传递。在事务中,可以将消息的发布和确认操作包裹在一个事务中,如果事务提交成功,表示消息已经成功到达 RabbitMQ,否则可以进行回滚。
  • 备份交换机(Alternate Exchange):可以配置备份交换机,当消息无法路由到指定的交换机时,它将被发送到备份交换机,从而避免消息丢失。
  • 监控和错误处理:建立监控机制,定期检查消息队列的状态,以及消费者的消费情况。在出现错误或异常情况时,根据具体情况进行错误处理,例如重试发送消息、记录日志、发送警报等。

发布者确认机制Example

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReliableMessagingExample {
    private static final String QUEUE_NAME = "my_queue";
    private static final String EXCHANGE_NAME = "my_exchange";
    private static final String ROUTING_KEY = "my_routing_key";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接和信道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        // 声明队列和交换机
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

        // 启用发布者确认模式
        channel.confirmSelect();

        // 添加发布者确认监听器
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("Message confirmed, delivery tag: " + deliveryTag);
            }

            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("Message not confirmed, delivery tag: " + deliveryTag);
                // 可以在这里进行相应的处理,例如重新发送消息
            }
        });

        // 发布消息
        String message = "Hello, RabbitMQ!";
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());

        try {
            // 等待发布者确认
            channel.waitForConfirmsOrDie();
        } catch (InterruptedException e) {
            // 可以在这里进行相应的处理,例如重新发送消息
            e.printStackTrace();
        }

        // 关闭信道和连接
        channel.close();
        connection.close();
    }
}

事务保证消息可靠性Example

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class ReliableMessagingExample {
    private static final String QUEUE_NAME = "my_queue";
    private static final String EXCHANGE_NAME = "my_exchange";
    private static final String ROUTING_KEY = "my_routing_key";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建连接和信道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        try {
            // 开启事务
            channel.txSelect();

            // 声明队列和交换机
            channel.queueDeclare(QUEUE_NAME, true, false, false, null);
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

            // 发布消息
            String message = "Hello, RabbitMQ!";
            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());

            // 提交事务
            channel.txCommit();
            System.out.println("Transaction committed successfully.");
        } catch (IOException e) {
            // 发生异常,回滚事务
            channel.txRollback();
            System.out.println("Transaction rolled back due to an exception.");
            e.printStackTrace();
        } finally {
            // 关闭信道和连接
            channel.close();
            connection.close();
        }
    }
}

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

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

相关文章

Pandas:DataFrame的完整指南【第82篇—DataFrame】

Pandas&#xff1a;DataFrame的完整指南 Pandas是Python中最流行的数据处理库之一&#xff0c;而其中的DataFrame对象是数据处理的核心。DataFrame为我们提供了一个强大而灵活的数据结构&#xff0c;使得数据的清洗、分析和可视化变得更加简便。在本文中&#xff0c;我们将深入…

旅游出门千万别忘带这些!花的不多,享受翻倍!随身wifi看这篇,高性价比高口碑随身wifi推荐

春节长假&#xff0c;大家都去哪儿玩了呢&#xff1f;我反正带着我的小背包&#xff0c;走遍了祖国的大好河山&#xff01; 得益于之前几次长假出行的经验&#xff0c;这次出行体验十分完美。除了详细完备的出行攻略&#xff0c;还有就是一些出行好物&#xff0c;虽然不起眼&am…

采购平台架构设计和实现的实战总结

当代企业日益重视采购管理的有效性和高效性&#xff0c;而采购平台的架构设计和实现则成为实现这一目标的关键。本文将探讨采购平台架构设计的重要性、关键原则以及实施过程中需要考虑的要点&#xff0c;帮助企业构建强大的采购平台&#xff0c;提升采购管理效率和效果。 ### 1…

在PyTorch中,如何查看深度学习模型的每一层结构?

这里写目录标题 1. 使用print(model)2. 使用torchsummary库3.其余方法&#xff08;可以参考&#xff09; 在PyTorch中&#xff0c;如果想查看深度学习模型的每一层结构&#xff0c;可以使用print(model)或者model.summary()&#xff08;如果你使用的是torchsummary库&#xff0…

Arrays工具类的常见方法总结

一、Arrays.asList( ) 1、作用 Arrays.asList( )可以将一个数组以集合的形式传入一个集合对象。通常用来将一组元素全部添加到集合中。 2、参数及返回值 参数&#xff1a;一组动态参数 返回值&#xff1a;List<T>集合 3、应用举例 List<String> boyListArra…

什么是智慧公厕,智慧公厕有哪些功能

1.什么是智慧公厕&#xff1f; 随着智慧城市的快速发展&#xff0c;公共厕所作为城市基础设施的一部分&#xff0c;也在逐步升级转型。那么&#xff0c;什么是智慧公厕&#xff1f;智慧公厕作为智慧城市的重要组成部分&#xff0c;将公共厕所的建设、设计、使用、运营和管理等…

深入解析鸿蒙系统的页面路由(Router)机制

鸿蒙系统以其独特的分布式架构和跨设备的统一体验而备受瞩目。在这个系统中&#xff0c;页面路由&#xff08;Router&#xff09;机制是连接应用各页面的关键组成部分。本文将深入探讨鸿蒙系统的页面路由&#xff0c;揭示其工作原理、特点以及在应用开发中的实际应用。 1. 实现…

同比跌超39%!春节楼市进一步冷却

楼市偏冷的基调延续。今年春节假期楼市热度进一步冷却。从各线城市的销售面积来看&#xff0c;正月初一至初六&#xff0c;30城楼市商品房平均成交面积继续下滑至2019年以来新低&#xff0c;较去年下滑39.2%&#xff0c;其中一线、三线均呈现大幅下滑&#xff0c;而二线城市成交…

Python算法题集_验证二叉搜索树

Python算法题集_验证二叉搜索树 题98&#xff1a;验证二叉搜索树1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【DFS递归】2) 改进版一【DFS递归终止检测】3) 改进版二【BFS迭代终止检测】 4. 最优算法 本文为Python算法题集之一的代码示例 题…

vue-ESlint (六)

代码规范 代码规范&#xff1a;一套写代码的约定规则。例如&#xff1a;"赋值符号的左右是否需要空格" "一句结束是否是要加;" . 老话说&#xff1a;"没有规矩不成方圆" → 正规的团队 需要 统一的编码风格 JavaScript Standard Style 规范说…

Linux文件操作类命令 touch | stat | cp | scp | alias | mv | dd | cat | head | tail

touch 1.创建空文件&#xff08;只有元数据信息&#xff0c;没有数据段内容&#xff09;touch 1.txt 2.对创建或已经存在的文件改变元数据信息的时间记录值-d 改变创建时间touch -d 20240101 1.txt -a改变访问时间 -m改变修改时间stat 查看元数据信息 Blocks是512字…

【CV论文精读】【BEV感知】BEVFormer:通过时空Transformer学习多摄像机图像的鸟瞰图表示

【CV论文精读】BEVFormer Learning Bird’s-Eye-View Representation from Multi-Camera Images via Spatiotemporal Transformers BEVFormer&#xff1a;通过时空Transformer学习多摄像机图像的鸟瞰图表示 图1&#xff1a;我们提出了BEVFormer&#xff0c;这是一种自动驾驶的…

2024年最新onlyfans虚拟信用卡订阅教程

一、Onlyfans是什么&#xff1f; OnlyFans是一个允许创作者分享自己的独家内容的平台&#xff0c;简称o站。这个平台允许创作者创建一个订阅服务&#xff0c;粉丝需要支付费用才能访问其独家内容。 本文将教你如何使用虚拟卡在OnlyFans上进行充值。 二、如何使用虚拟卡支付 O…

nba2k23 中国梦之队面补名单

nba2k23 中国梦之队面补名单 提示&#xff1a;本面补为名单形式&#xff0c;内含中国国家队2000、2008、2015、2019面补名单&#xff0c;安装后多队同时存在。 下载地址&#xff1a; https://www.changyouzuhao.cn/12759.html

OpenCV-41 使用掩膜的直方图

一、掩膜 掩膜即为与原图大小一致的黑底白框图。 如何生成掩膜&#xff1f; 先生成一个全黑的和原始图片大小一样大的图片。mask np.zeros(img.shape, np.uint8)将想要的区域通过索引方式设置为255.mask[100:200, 200:300] 示例代码如下&#xff1a; import cv2 import ma…

WINCC如何新增下单菜单,切换显示页面

杭州工控赖工 首先我们先看一下&#xff0c;显示的效果&#xff0c;通过下拉菜单&#xff0c;切换主显示页面。如图一&#xff1a; 图1 显示效果 第一步&#xff1a; 通过元件新增一个组合框&#xff0c;见图2&#xff1b; 组合框的设置&#xff0c;设置下拉框的长宽及组合数…

- 项目落地 - 《选择项目工具的方法论》

本文属于专栏《构建工业级QPS百万级服务》 提纲&#xff1a; 选择大概率能完成业务目标的工具选择最适合的工具制作最适合的工具 本文所说的项目工具&#xff0c;泛指业务软件开发&#xff0c;所依赖的第三方提供的成熟的资源。包括但不限于开发语言、编辑工具、编译工具、三方…

深入解析ESP32C3(3)- bootloader启动流程

ESP32C3启动流程可以分为如下3 个步骤&#xff1a; 一级引导程序(PBL)&#xff1a;被固化在了ESP32-C3 内部的ROM 中&#xff0c;它会从flash 的0x0 偏移地址处加载二级引导程序至RAM (IRAM & DRAM) 中。二级引导程序(SBL)&#xff1a;从flash 中加载分区表和主程序镜像至…

2.13:C语言测试题

21.(b) 6 22.(b) cd 23.b) 5 4 1 3 2 栈&#xff1a;先进后出 24. b,c,d:10,12,120 25.2,5 26.越界访问&#xff0c;可能正常输出&#xff0c;可能段错误 27. 0&#xff0c;41 28. a&#xff09;11 b) 320 29. aab; ba-b; aa-b; 30. p150x801005; p250x810…

mysql 2-16

安全等于<> 最大最小LEAST,GREATEST BETWEEN AND 条件一是下限 IN LIKE关键字 转移字符 逻辑运算符 位运算符 排序数据 升序降序&#xff0c;默认升序 二级排序 8.0新特性 小拓展 多表查询 多表查询 别名 多表查询的分类 非等值连接 自连接 内连接与外连接 sql92实现外连…