【RabbitMQ教程】第四章 —— RabbitMQ - 交换机

news2024/11/26 21:46:26

在这里插入图片描述

                                                                  💧 【 R a b b i t M Q 教程】第四章—— R a b b i t M Q − 交换机 \color{#FF1493}{【RabbitMQ教程】第四章 —— RabbitMQ - 交换机} RabbitMQ教程】第四章——RabbitMQ交换机💧          


🌷 仰望天空,妳我亦是行人.✨
🦄 个人主页——微风撞见云的博客🎐
🐳 《数据结构与算法》专栏的文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
💧 《Java学习笔记》专栏的文章是本人在Java学习中总结的一些知识点~ 💐
🥣 《每天一点小知识》专栏的文章可以丰富你的知识库,滴水成河~ 🌊
🥕 《RabbitMQ》专栏的文章是在学习尚硅谷课程时整理的笔记,方便复习巩固~ 🍑
🪁 希望本文能够给读者带来一定的帮助~🌸文章粗浅,敬请批评指正!🐥


文章目录

  • 🌊RabbitMQ - 交换机
    • Exchanges
    • 临时队列
    • 绑定 bindings
    • Fanout exchange
      • Fanout 介绍
      • Fanout 实战
    • Direct exchange
      • Direct 介绍
      • 多重绑定
      • Direct 实战
    • Topics exchange
      • Topic 的介绍
      • Topic 匹配案例
      • Topic 实战
  • 🐳结语


🌊RabbitMQ - 交换机

在上一节中,我们创建了一个工作队列。我们假设的是工作队列背后,每个任务都恰好交付给一个消费者(工作进程)。在这一部分中,我们将做一些完全不同的事情-我们将消息传达给多个消费者。这种模式 称为 ”发布/订阅”。


Exchanges

RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。

相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面将它们推入队列。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。

在这里插入图片描述
Exchanges 的类型:

​ 直接(direct), 主题(topic) ,标题(headers) , 扇出(fanout)

无名exchange:

​ 在前面部分我们对 exchange 一无所知,但仍然能够将消息发送到队列。之前能实现的 原因是因为我们使用的是默认交换,我们通过空字符串(“”)进行标识。

在这里插入图片描述
第一个参数是交换机的名称。空字符串表示默认或无名称交换机:消息能路由发送到队列中其实是由 routingKey(bindingkey)绑定 key 指定的,如果它存在的话

临时队列

之前的章节我们使用的是具有特定名称的队列(还记得 hello 和 ack_queue 吗?)。队列的名称我们来说至关重要,我们需要指定我们的消费者去消费哪个队列的消息。

每当我们连接到 Rabbit 时,我们都需要一个全新的空队列,为此我们可以创建一个具有随机名称的队列,或者能让服务器为我们选择一个随机队列名称那就更好了。其次一旦我们断开了消费者的连接,队列将被自动删除。

创建临时队列的方式如下:

String queueName = channel.queueDeclare().getQueue();

在这里插入图片描述

绑定 bindings

什么是 bingding 呢,binding 其实是 exchange 和 queue 之间的桥梁,它告诉我们 exchange 和那个队列进行了绑定关系。比如说下面这张图告诉我们的就是 X 与 Q1 和 Q2 进行了绑定
在这里插入图片描述
在这里插入图片描述

Fanout exchange

Fanout 介绍

Fanout 这种类型非常简单。正如从名称中猜到的那样,它是将接收到的所有消息广播到它知道的 所有队列中。系统中默认有些 exchange 类型
在这里插入图片描述

Fanout 实战

在这里插入图片描述
Logs 和临时队列的绑定关系如下图
在这里插入图片描述
为了说明这种模式,我们将构建一个简单的日志系统。它将由两个程序组成:第一个程序将发出日志消 息,第二个程序是消费者。其中我们会启动两个消费者,其中一个消费者接收到消息后把日志存储在磁盘,

ReceiveLogs01 将接收到的消息打印在控制台

import com.oddfar.utils.RabbitMqUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class ReceiveLogs01 {
    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();
        //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
        channel.queueBind(queueName, EXCHANGE_NAME, "");
        System.out.println("等待接收消息,把接收到的消息打印在屏幕........... ");

        //发送回调
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("控制台打印接收到的消息" + message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});

    }
}

ReceiveLogs02 把消息写出到文件

public class ReceiveLogs02 {
    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws Exception {

        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();
        //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
        channel.queueBind(queueName, EXCHANGE_NAME, "");
        System.out.println("等待接收消息,把接收到的消息写到文件........... ");

        //发送回调
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            File file = new File("D:\\test\\rabbitmq_info.txt");
            FileUtils.writeStringToFile(file,message,"UTF-8");
            System.out.println("数据写入文件成功");
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});

    }
}

EmitLog 发送消息给两个消费者接收:

public class EmitLog {
    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();

        /**
         * 声明一个 exchange
         * 1.exchange 的名称
         * 2.exchange 的类型
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入信息");
        while (sc.hasNext()) {
            String message = sc.nextLine();
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
            System.out.println("生产者发出消息" + message);
        }
    }
    
}

Direct exchange

在上一节中,我们构建了一个简单的日志记录系统。我们能够向许多接收者广播日志消息。在本节我们将向其中添加一些特别的功能——让某个消费者订阅发布的部分消息。例如我们只把严重错误消息定向存储到日志文件(以节省磁盘空间),同时仍然能够在控制台上打印所有日志消息。

我们再次来回顾一下什么是 bindings,绑定是交换机和队列之间的桥梁关系。也可以这么理解: 队列只对它绑定的交换机的消息感兴趣。绑定用参数:routingKey 来表示也可称该参数为 binding key, 创建绑定我们用代码:channel.queueBind(queueName, EXCHANGE_NAME, “routingKey”);

绑定之后的意义由其交换类型决定。

Direct 介绍

上一节中的我们的日志系统将所有消息广播给所有消费者,对此我们想做一些改变,例如我们希 望将日志消息写入磁盘的程序仅接收严重错误(errros),而不存储哪些警告(warning)或信息(info)日志 消息避免浪费磁盘空间。Fanout 这种交换类型并不能给我们带来很大的灵活性-它只能进行无意识的 广播,在这里我们将使用 direct 这种类型来进行替换,这种类型的工作方式是,消息只去到它绑定的 routingKey 队列中去。
在这里插入图片描述
在上面这张图中,我们可以看到 X 绑定了两个队列,绑定类型是 direct。队列Q1 绑定键为 orange, 队列 Q2 绑定键有两个:一个绑定键为 black,另一个绑定键为 green.

在这种绑定情况下,生产者发布消息到 exchange 上,绑定键为 orange 的消息会被发布到队列 Q1。绑定键为 blackgreen 和的消息会被发布到队列 Q2,其他消息类型的消息将被丢弃。

多重绑定

在这里插入图片描述
当然如果 exchange 的绑定类型是direct,但是它绑定的多个队列的 key 如果都相同,在这种情况下虽然绑定类型是 direct 但是它表现的就和 fanout 有点类似了,就跟广播差不多,如上图所示。

Direct 实战

关系:
在这里插入图片描述
交换机:
在这里插入图片描述
c2:绑定disk,routingKey为error

c1:绑定console,routingKey为info、warning

1、

import com.oddfar.utils.RabbitMqUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class ReceiveLogsDirect01 {
    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        String queueName = "disk";
        //队列声明
        channel.queueDeclare(queueName, false, false, false, null);
        //队列绑定
        channel.queueBind(queueName, EXCHANGE_NAME, "error");
        System.out.println("等待接收消息...");

        //发送回调
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            message = "接收绑定键:" + delivery.getEnvelope().getRoutingKey() + ",消息:" + message;
            System.out.println("error 消息已经接收:\n" + message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

2、

public class ReceiveLogsDirect02 {
    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        String queueName = "console";
        //队列声明
        channel.queueDeclare(queueName, false, false, false, null);
        //队列绑定
        channel.queueBind(queueName, EXCHANGE_NAME, "info");
        channel.queueBind(queueName, EXCHANGE_NAME, "warning");

        System.out.println("等待接收消息...");

        //发送回调
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            message = "接收绑定键:" + delivery.getEnvelope().getRoutingKey() + ",消息:" + message;
            System.out.println("info和warning 消息已经接收:\n" + message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

3、

public class EmitLogDirect {
    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        //创建多个 bindingKey
        Map<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("info", "普通 info 信息");
        bindingKeyMap.put("warning", "警告 warning 信息");
        bindingKeyMap.put("error", "错误 error 信息");
        //debug 没有消费这接收这个消息 所有就丢失了
        bindingKeyMap.put("debug", "调试 debug 信息");

        for (Map.Entry<String, String> bindingKeyEntry : bindingKeyMap.entrySet()) {
            //获取 key value
            String bindingKey = bindingKeyEntry.getKey();
            String message = bindingKeyEntry.getValue();

            channel.basicPublish(EXCHANGE_NAME, bindingKey, null, message.getBytes("UTF-8"));
            System.out.println("生产者发出消息:" + message);
        }
    }
}

Topics exchange

Topic 的介绍

在上一个小节中,我们改进了日志记录系统。我们没有使用只能进行随意广播的 fanout 交换机,而是使用了 direct 交换机,从而有能实现有选择性地接收日志。

尽管使用 direct 交换机改进了我们的系统,但是它仍然存在局限性——比方说我们想接收的日志类型有 info.base 和 info.advantage,某个队列只想 info.base 的消息,那这个时候direct 就办不到了。这个时候就只能使用 topic 类型

Topic的要求

发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。这些单词可以是任意单词

比如说:“stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”.这种类型的。

当然这个单词列表最多不能超过 255 个字节。

在这个规则列表中,其中有两个替换符是大家需要注意的:

  • *(星号)可以代替一个单词
  • #(井号)可以替代零个或多个单词

Topic 匹配案例

下图绑定关系如下
在这里插入图片描述

  • Q1–>绑定的是
    • 中间带 orange 带 3 个单词的字符串 (*.orange.*)
  • Q2–>绑定的是
    • 最后一个单词是 rabbit 的 3 个单词 (*.*.rabbit)
    • 第一个单词是 lazy 的多个单词 (lazy.#)

上图是一个队列绑定关系图,我们来看看他们之间数据接收情况是怎么样的

例子说明
quick.orange.rabbit被队列 Q1Q2 接收到
azy.orange.elephant被队列 Q1Q2 接收到
quick.orange.fox被队列 Q1 接收到
lazy.brown.fox被队列 Q2 接收到
lazy.pink.rabbit虽然满足两个绑定但只被队列 Q2 接收一次
quick.brown.fox不匹配任何绑定不会被任何队列接收到会被丢弃
quick.orange.male.rabbit是四个单词不匹配任何绑定会被丢弃
lazy.orange.male.rabbit是四个单词但匹配 Q2

注意:

  • 当一个队列绑定键是#,那么这个队列将接收所有数据,就有点像 fanout 了
  • 如果队列绑定键当中没有#和*出现,那么该队列绑定类型就是 direct 了

Topic 实战

在这里插入图片描述
代码如下:

package com.oddfar.seven;

import com.oddfar.utils.RabbitMqUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.HashMap;
import java.util.Map;

/**
 * 发送端
 *
 * @author zhiyuan
 */
public class EmitLogTopic {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        /**
         * Q1-->绑定的是
         *      中间带 orange 带 3 个单词的字符串(*.orange.*)
         * Q2-->绑定的是
         *      最后一个单词是 rabbit 的 3 个单词(*.*.rabbit)
         *      第一个单词是 lazy 的多个单词(lazy.#)
         *
         */
        Map<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("quick.orange.rabbit", "被队列 Q1Q2 接收到");
        bindingKeyMap.put("lazy.orange.elephant", "被队列 Q1Q2 接收到");
        bindingKeyMap.put("quick.orange.fox", "被队列 Q1 接收到");
        bindingKeyMap.put("lazy.brown.fox", "被队列 Q2 接收到");
        bindingKeyMap.put("lazy.pink.rabbit", "虽然满足两个绑定但只被队列 Q2 接收一次");
        bindingKeyMap.put("quick.brown.fox", "不匹配任何绑定不会被任何队列接收到会被丢弃");
        bindingKeyMap.put("quick.orange.male.rabbit", "是四个单词不匹配任何绑定会被丢弃");
        bindingKeyMap.put("lazy.orange.male.rabbit", "是四个单词但匹配 Q2");
        for (Map.Entry<String, String> bindingKeyEntry : bindingKeyMap.entrySet()) {
            String bindingKey = bindingKeyEntry.getKey();
            String message = bindingKeyEntry.getValue();

            channel.basicPublish(EXCHANGE_NAME, bindingKey, null, message.getBytes("UTF-8"));
            System.out.println("生产者发出消息:" + message);
        }
    }
}

public class ReceiveLogsTopic01 {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //声明 Q1 队列与绑定关系
        String queueName = "Q1";
        //声明
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定
        channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.*");
        System.out.println("等待接收消息........... ");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" 接收队列:" + queueName + " 绑定键:" + delivery.getEnvelope().getRoutingKey() + ",消息:" + message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

public class ReceiveLogsTopic02 {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //声明 Q2 队列与绑定关系
        String queueName = "Q2";
        //声明
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定
        channel.queueBind(queueName, EXCHANGE_NAME, "*.*.rabbit");
        channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");

        System.out.println("等待接收消息........... ");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" 接收队列:" + queueName + " 绑定键:" + delivery.getEnvelope().getRoutingKey() + ",消息:" + message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}


在这里插入图片描述


🐳结语

🐬初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。

🐟文章粗浅,希望对大家有帮助!

💧下一篇 -->【RabbitMQ教程】第四章 —— RabbitMQ - 死信队列

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

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

相关文章

《阿里大数据之路》读书笔记:第一章 总述

阿里巴巴大数据系统体系架构图 阿里数据体系主要分为数据采集、数据计算、数据服务和数据应用四大层次。 一、数据采集层 阿里巴巴建立了一套标准的数据采集体系方案&#xff0c;致力全面、高性能、规范地完成海量数据的采集&#xff0c;并将其传输到大数据平台。 数据来源主…

【C++】 STL(下)算法、迭代器、容器适配器 和 仿函数

文章目录 算法迭代器容器适配器栈&#xff08;stack&#xff09;队列&#xff08;queue&#xff09; 仿函数 算法 STL中的算法头文件位于和文件中&#xff08;以为主&#xff09; for_each(InputIterator First,InputIterator Last,Function _Func); 遍历&#xff0c;具体做什…

电影《天空之城》观后感

上周看了电影《天空之城》这部电影&#xff0c;这部电影是六一儿童节时上映的&#xff0c;本周也算是补票吧&#xff0c;童年时&#xff0c;看的都是免费的&#xff0c;早已经忘记是在哪里看到的&#xff0c;但当时对自己触动很大&#xff0c;算是启蒙电影&#xff0c;所以今天…

【RabbitMQ教程】第二章 —— RabbitMQ - 简单案例

&#x1f4a7; 【 R a b b i t M Q 教程】第二章—— R a b b i t M Q − 简单案例 \color{#FF1493}{【RabbitMQ教程】第二章 —— RabbitMQ - 简单案例} 【RabbitMQ教程】第二章——RabbitMQ−简单案例&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人…

汽车电子Autosar之以太网SOMEIP

前言 首先&#xff0c;请问大家几个小小问题&#xff0c;你清楚&#xff1a; 你知道什么是SOME/IP吗&#xff1f;你知道为什么会产生SOME/IP即相关背景吗&#xff1f;你知道SOME/IP与SOA又有着哪些千丝万缕的联系呢&#xff1f;SOME/IP在实践中到底应该如何使用呢&#xff1f…

QuickList

基本概述 ZipList虽节省内存但是申请内存必须是连续的&#xff0c;如果内存占用很大&#xff0c;申请内存效率就会很低&#xff0c;可以限制ZipList长度和entry大小&#xff1b; 实在要存储大量数据&#xff0c;超出ZipList最佳上限了&#xff0c;此时可以创建多个ZipList来分片…

Autosar诊断实战系列04-基于CAPL语言的通信类诊断测试脚本开发

本文框架 前言1.CAPL编程简单介绍2. CAPL脚本开发实战2.1 添加CAPL Test Module2.2 CAPL脚本实战编写前言 在本系列笔者将结合工作中对诊断实战部分的应用经验进一步介绍常用UDS服务的进一步探讨及开发中注意事项, Dem/Dcm/CanTp/Fim模块配置开发及注意事项,诊断与BswM/NvM关…

OpenAI官方提示词课(五)如何进行文本翻译校正修改

在本篇文章中&#xff0c;我们将探讨如何使用大语言模型进行文本转换任务&#xff0c;例如语言翻译、拼写和语法检查、语气调整和格式转换。 翻译 ChatGPT接受多种语言的训练&#xff0c;使得模型具备翻译能力。以下是如何使用这种能力的一些示例。 prompt f""&q…

利用lambda优化反射功能实现方法调用

最近在思考lambda相关的问题&#xff0c;简单记录下做的相关反射替代和函数映射的尝试。 原理分析 lambda是jdk8才提供的&#xff0c;原理其实就是动态生成内部类来执行函数映射的方法。也就是说一段lambda表达式会对应特定的类方法&#xff0c;之后调用。底层是通过LambdaMe…

《离散数学》:逻辑

〇、前言 离散数学是数学的一个分支&#xff0c;研究离散对象和离散结构的数学理论和方法。这学期学校开了离散数学的课程&#xff0c;我受益颇丰&#xff0c;感觉到了离散数学真正的魅力&#xff0c;也被开创离散数学各个分支的人的聪明与才智深深折服。与连续数学不同&#…

Stopwatch工具类计时器探究

搬砖的我们 特别是Java开发的童鞋们, 经常需要通过记录开始时间和结束时间&#xff0c;然后通过计算差值&#xff0c;得到时间差&#xff0c;如下面的代码&#xff1a; long start System.currentTimeMillis(); long end System.currentTimeMillis(); System.out.println(…

【数字调制】数字调制技术FSK与PSK分析与研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

栈的数据结构完成表达式(5*10+2-7+5)/10+5的计算

栈&#xff08;Stack&#xff09;是一种线性数据结构&#xff0c;具有后进先出&#xff08;LIFO&#xff09;的特性。它可以理解为一种类似于抽屉的容器&#xff0c;只能在顶部进行插入和删除操作&#xff0c;其他位置不可访问。栈的基本操作包括入栈&#xff08;push&#xff…

[数字图像处理]第五章 图像复原与重建

文章目录 第五章 图像复原与重建5.1图像退化复原过程的模型图像退化图像复原图像复原与图像增强图像退化的数学模型为什么采用线性位移不变系统模型来描述图像退化过程 5.2 噪声模型x5.2.1 噪声的空间和频率特性5.2.2 一些重要的噪声概率密度函数高斯噪声实验&#xff1a;采用高…

【微服务架构设计和实现】4.1 微服务架构概述和设计原则

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;【容器化应用程序设计和开发】 第三章&#xff1a;【基于容器的部署、管理和扩展】 4.1 微服务架构概述和设计原则 4.1 微服务架构概述和设计原则4.1.1 微服务架构的优点4.1.2 微服务架构遵…

在 ArcGIS Pro 中使用 H3 创建蜂窝六边形

在 ArcGIS Pro 中使用 H3 创建蜂窝六边形https://mp.weixin.qq.com/s/tGk7AT2jAcvsmNyp2bRvig 之前看了个有意思的ArcGIS博客&#xff1a;H3六边形&#xff0c;当然了这也不是个新鲜东西了。原文&#xff1a; https://www.esri.com/arcgis-blog/products/arcgis-pro/analytic…

为什么要学GIS开发

什么是地理信息系统技术&#xff1f; GIS技术使用专门的计算机系统来获取地理数据并将其集成到智能“超级”地图中。然后&#xff0c;这些数据可用于创建无穷无尽的“假设”场景&#xff0c;为以下应用程序提供强大的工具&#xff1a; 制图&#xff08;地图制作&#xff09;应…

【阅读随笔】Rewrite-Based Decomposition of Signal Temporal Logic Specifications

文章目录 Overview1 IntroLTL任务分解STL任务分解本文工作 Background and Problem DefinitionSTLAgent假设与问题方法 An STL Rewriting SystemRewriting SystemFormula Rewrite DAG Decomposing STL智能体编队任务分解最优分解 Exploring the Formula Rewrite DAGExperiments…

soci在windows下vs2010编译

需要下载 的资源 mysql connector c 因为其使用的的是mysql connector c的api&#xff0c;需要下载https://downloads.mysql.com/archives/c-c/ 分别对应32位和64位的 soci 4.0 从github上下载4.03分支 https://github.com/SOCI/soci/tree/v4.0.3 cmake 需要下载3.25版…

Java多线程快速入门

文章目录 Java多线程快速入门1、认识多线程2、多线程的实现2.1 继承Thread类2.2 实现Runnable接口2.3 利用Callable和Futrue接口2.4 三种方式的比较 3、Thread类常用API3.1 守护线程3.2 礼让线程3.3 插入线程3.4 线程的生命周期 5、线程安全问题5.1 synchronized5.2 Lock 6、等…