SpringCloud全系列知识(6)——RabbitMQ(消息队列)

news2024/12/25 1:12:34

消息队列(MQ)—RabbitMQ

一 初识MQ

1.同步通信与异步通信

1.同步通信的问题

同步调用的优点在于时效性高,可以立即得到结果

微服务之间基于Feign的调用属于同步方式,存在一些问题

在这里插入图片描述

  • 耦合性:业务较多时,扩展维护不方便
  • 性能下降:一次性调用多个Feign,会导致耗时增加,性能和吞吐量下降
  • 资源浪费:调用Feign请求响应的过程中,CPU只能等待,无法做其他操作,造成系统资源的浪费
  • 级联失败:当有一个服务宕机时,很容易造成后面的服务阻塞,从而导致级联失败

在这里插入图片描述

2.异步调用方案

异步调用常见的实现就是 事件驱动模式

在这里插入图片描述

  • 服务解耦:新增或减少业务员,通过增加或取消事件订阅即可,无需再去修改上级服务
  • 性能提升:上级服务只需发送事件,不再关注其他业务逻辑
  • 消除级联:服务没有强依赖,不担心级联失败的问题
  • 流量削峰:Broker 起到缓冲作用,根据实际情况分发业务,保护订阅服务。

异步通信的缺点:

  • 过分依赖于Broker的可靠性,安全性,吞吐能力
  • 业务复杂,业务没有明显的流程线,不好追踪管理

2.MQ介绍

MQ(MessageQueue),中文含义为消息队列,用来存放消息,也就是事件驱动模式中的 Broker

常见的MQ技术包含一下四种:

在这里插入图片描述

  • RabbitMQ:现阶段使用最多的MQ技术,优点在于消息的低延迟和可靠性
  • ActiveMQ:开发语言基于Java,可以进行深度定制
  • RocketMQ:开发语言基于Java,可以进行深度定制,阿里巴巴开发
  • Kafka:单机吞吐量很高,但消息可靠性不高。

二 RabbitMQ的使用

1.安装与运行

RabbitMQ基于Erlang语言开发的开源消息中间件。

RabbitMQ官方地址:Messaging that just works — RabbitMQ

在DockerHub上拉取RabbitMQ的镜像,然后运行。

#1.拉取RabbitMQ
docker pull rabbitmq:3
#2.运行RabbitMQ镜像,(15672 为管理UI界面的端口,5672为后期通信接口)
docker run \
 -e RABBITMQ_DEFAULT_USER=shawn \
 -e RABBITMQ_DEFAULT_PASS=123456 \
 --name rabbitmq3 \
 --hostname myrabbit \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3
  • RABBITMQ_DEFAULT_USER :设置默认账户名
  • RABBITMQ_DEFAULT_PASS :设置默认密码

安装完成之后不能直接进行访问,需要先进入容器内部开启插件才可以访问

#1.进入 rabbitmq 容器内
docker exec -it myrabbitmq bash
#2.开启插件
rabbitmq-plugins enable rabbitmq_management
#3.如果直接拉取 management 版本的镜像,则无需以上步骤
docker pull rabbitmq:3-management
#4.运行 management 版本的rabbit
docker run \
 -e RABBITMQ_DEFAULT_USER=shawn \
 -e RABBITMQ_DEFAULT_PASS=123456 \
 --name rabbitmq3 \
 --hostname myrabbit \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

完成以后即可在浏览器中输入服务器地址+端口号访问了。

在这里插入图片描述

踩坑点:Rabbit的Web UI 中Channel模块无法打开并且提示 Stats in management UI are disabled on this node

是因为默认情况下Rabbit是禁止的。

The reason is that the default image disables metrics collector in the management_agent plugin

原因是默认图像禁用management_agent插件中的度量收集器

# 1.进入容器内部
docker exec -it myrabbitmq bash
# 2.切换至配置文件目录下
cd  /etc/rabbitmq/conf.d/
# 3.将 management_agent.disable_metrics_collector.conf 文件中的 management_agent.disable_metrics_collector 的值修改为 false
echo management_agent.disable_metrics_collector = false > management_agent.disable_metrics_collector.conf
# 4.重启 rabbit 容器
docker restart rabbitmq3

2.RabbitMQ概述

在这里插入图片描述

RabbitMQ通过 VirtualHost 进行隔离,相互不可见。

RabbitMQ中的相关概念

  • channel:操作MQ的工具
  • exchange:路由消息到队列中,将消息发送给exchange,由exchange交给队列
  • queue:缓存消息
  • virtual host:虚拟主机,是对queue,exchange等资源的逻辑分组,不同用户可以访问不同的虚拟主机

三 RabbitMQ消息模型

在这里插入图片描述

1.简单队列(Hello World)

在这里插入图片描述

1.创建项目引入依赖

创建两个项目分别为发送者和订阅者,引入RabbitMQ的maven坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

或者在初始化项目时直接勾选RabbitMQ

在这里插入图片描述

2.编写测试代码示例

消息发送者示例代码:publisher

@Test
void publisher() throws IOException, TimeoutException {
    ConnectionFactory factory=new ConnectionFactory();
    factory.setHost("192.168.119.101");
    factory.setUsername("shawn");
    factory.setPassword("123456");
    factory.setPort(5672);
    factory.setVirtualHost("/");
    //建立连接
    Connection connection=factory.newConnection();
    //创建通道
    Channel channel=connection.createChannel();
    //创建队列
    String queueName="simple.queue";
    String message="贾君鹏,你妈喊你回家吃饭!";
    channel.basicPublish("",queueName,null,message.getBytes());
    System.out.println("发送消息:"+message);
    channel.close();
    connection.close();
}

消息接受者示例代码:consumer

@Test
void consumer() throws IOException, TimeoutException {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("192.168.119.101");
    factory.setUsername("shawn");
    factory.setPassword("123456");
    factory.setPort(5672);
    factory.setVirtualHost("/");
    //创建连接和通道
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    //创建队列
    String queueName = "simple.queue";
    channel.queueDeclare(queueName, false, false, false, null);
    //订阅消息
    channel.basicConsume(queueName,true,new DefaultConsumer(channel){
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            // 处理消息
            String message=new String(body,"UTF-8");
            System.out.println("接受消息:"+message);
        }
    });
    System.out.println("等待接受消息,退出请按 CTRL+C");
}

3.基本消息队列的发送流程

  • 建立 Connection
  • 创建 Channel
  • 使用 Channel 声明队列
  • 使用 Channel 向队列发送消息 / 使用 consumer 的消费行为订阅消息

2.SpringAMQP

官方文档:https://spring.io/projects/spring-amqp

在这里插入图片描述

AMQP是消息接受与发送的协议或者标准,与语言和平台无关

1.RabbitTemplate

RabbitTemplate是一个Spring封装的用来发送消息的工具类,类似 RedisCache,RestTemplate,可以简单,高效,优雅的实现消息的发送和接收

引入 spring-boot-starter-amqp 依赖,然后在相关项目(publisher 和 consumer )的配置文件中配置相关参数。

spring:
  application:
    name: publisher
  rabbitmq:
    host: 192.168.119.101
    # 默认端口为5672,使用默认端口时可不写
    port: 5672
    username: shawn
    password: 123456
    virtual-host: /

在 publisher 项目中编写测试代码,发送消息

@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void publisher() {
    String queueName="hello.world";
    String message="Hello,RabbitTemplate";
    # 这里使用 convertAndSend 进行消息的处理和发送
    rabbitTemplate.convertAndSend(queueName,message);
    System.out.println("消息发送完成");
}

在 consumer 项目中编写代码,订阅并且接受消息

/**
* 使用 Component 将当前类申明为一个 bean
* 定义一个类,使用 RabbitListener 注解订阅要接受消息的队列
*/
@Component
public class SimpleMessageRabbitListener {
    @RabbitListener(queues = "simple.queue")
    public void listenSimpleMessage(String message) {
        System.out.println("接受消息:" + message);
    }
}

注意:消息一旦消费,就会从消息队列中删除,RabbitMQ没有消息回溯

3.WorkQueue(工作队列)

工作队列模型,多个消费者绑定到一个队列,同一条消息只会被一个消费者处理。

在这里插入图片描述

工作队列,可以提高消息处理速度,避免消息堆积

@RabbitListener(queues = "simple.queue")
public void listenWorkMessage1(String message) throws InterruptedException {
    System.out.println("消费者【1】接受消息:" + message + "-" + LocalDateTime.now());
    Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenWorkMessage2(String message) throws InterruptedException {
    System.out.println("消费者【2】接受消息:" + message + "-" + LocalDateTime.now());
    Thread.sleep(200);
}

定义两个执行效率不同的消费者,模拟一个简单的工作队列模型,发现消费者并不会因为执行效率的高低自动的增加或处理消息的处理量,而是平均分配。

可以通过消息的欲取机制来控制消息的处理量

欲取机制,即默认情况下,有多少消息就拿多少消息,并不会考虑有多少个消费者。

通过调整 prefetch 属性的值,来控制消费者的消息预取能力

spring:
  application:
    name: consumer
  rabbitmq:
    host: 192.168.119.101
    port: 5672
    username: shawn
    password: 123456
    virtual-host: /
    listener:
      simple:
        # 通过调整 prefetch 属性的值,来控制消费者的消息预取能力
        prefetch: 1

4.发布订阅模式

发布订阅模式(Publish-Subscribe)的核心是,允许将一个消息发给多个消费者。具体的实现方式是加入了exchange(交换机)。

在这里插入图片描述

注意:exchange只负责消息的转发,而不是存储。路由失败则消息丢失

1.Exchange(交换机)

交换机的作用:

  • 接受 Publisher 发送的消息
  • 将消息按照规则路由到与之绑定的队列上
  • 不存储消息,只负责消息的转发。路由失败,消息丢失
  • FanoutExchange会将消息路由到每一个和它绑定的队列上

需要使用到的 Bean:FanoutExchange Queue Binding

2.FanoutExchange

Fanout Exchange 会将接收到的消息路由到每一个跟其绑定的queue

在 Consumer 项目中定义队列(queue),交换机(exchange),并且将队列绑定到交换机上。

/**
示例: 声明一个交换机和两个队列,并且将队列与交换机进行绑定
*/
@Configuration
public class FanoutExchangeConfig {
   
    /**
    *声明一个FanoutExchange对象,并且添加到bean
    */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("shawn.exchange");
    }
    /**
    *声明一个Queue对象,并且添加到bean
    */
    @Bean
    public Queue fanoutQueue1() {
        return new Queue("shawn.queue1");
    }
    /**
    * 将队列和Exchange进行绑定
    */
    @Bean
    public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        // 使用 BindingBuilder 提供的方法进行绑定,最后返回Binding对象
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    @Bean
    public Queue fanoutQueue2() {
        return new Queue("shawn.queue2");
    }

    @Bean
    public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

接受消息的监听器示例代码:

@Component
public class SimpleMessageRabbitListener {
    @RabbitListener(queues = "shawn.queue1")
    public void listenWorkMessage1(String message) throws InterruptedException {
        System.out.println("消费者【1】接受消息:" + message + "-" + LocalDateTime.now());
        Thread.sleep(20);
    }

    @RabbitListener(queues = "shawn.queue2")
    public void listenWorkMessage2(String message) throws InterruptedException {
        System.out.println("消费者【2】接受消息:" + message + "-" + LocalDateTime.now());
        Thread.sleep(200);
    }
}

在 Publisher 项目中编写消息发送的测试代码:

@Test
void testSendExchange() throws InterruptedException {
    String exChangeName = "shawn.exchange";
    String message = "贾君鹏,你妈喊你回家吃饭";
    // 向指定名称的Exchage(交换机)发送消息
    rabbitTemplate.convertAndSend(exChangeName, "", message);
}

测试结果:两个队列均能收到消息

在这里插入图片描述

3.DirectExchange

DirectExchange会将接受到的消息根据规则路由到指定的Queue,因此成为路由模式(routes)。

  • 每一个Queue都与DirectExchange设置一个BindingKey
  • 发布者发布消息时,指定消息的RoutingKey
  • DirectExchange会将消息路由到BindingKey与消息RoutingKey一致的队列
  • 可以为队列(queue)设置多个BindingKey,且一个BindingKey也可以设置给多个队列

在这里插入图片描述

当多个队列使用一个BindingKey时,DirectExchange会将消息发送给所有使用了这个BindingKey的队列

这种情况下,DirectExchang与FanoutExchange相同,也属于广播模式

因此可以认为 DirectExchange 可以模拟 FanoutExchang, 且比 FanoutExchange 灵活

示例:基于RabbitListener 实现 DirectExchange

编写 Consumer 项目的代码:

/**
* 使用 RabbitListener 声明要绑定的队列、BindingKey、交换机和交换机类型,BindingKey可以设置多个
*/
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "shawn.queue1"),
        exchange = @Exchange(name = "direct.exchange", type = ExchangeTypes.DIRECT),
        key = {"red", "blue"}
))
public void listenWorkMessage1(String message) throws InterruptedException {
    System.out.println("消费者【1】接受消息:" + message + "-" + LocalDateTime.now());
    Thread.sleep(20);
}

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "shawn.queue2"),
        exchange = @Exchange(name = "direct.exchange", type = ExchangeTypes.DIRECT),
        key = {"red", "yellow"}
))
public void listenWorkMessage2(String message) throws InterruptedException {
    System.out.println("消费者【2】接受消息:" + message + "-" + LocalDateTime.now());
    Thread.sleep(200);
}

在 Publuisher 项目中编写测试代码:

@Test
void testSendDirectExchange() throws InterruptedException {
    String exChangeName = "direct.exchange";
    String message = "贾君鹏,你妈喊你回家吃饭";
    String routingKey="blue";
    rabbitTemplate.convertAndSend(exChangeName,routingKey, message);
}

此时,只有包含指定的 RoutingKey 的队列,才能收到消息。

4.TopicExchange

TopicExchange 与 DirectExchange 的区别在于,routingKey必须是多个单词的列表,并且使用英文句号( . )分割

在这里插入图片描述

Queue 与 Exchange 进行绑定时支持通配符:

  • (#)表示 0个或多个单词
  • (*)表示 一个单词

示例:使用 TopicExchange 实现消息发送和接受

在 Consumer 项目中编写示例代码

@Component
public class TopicExchangeListener {
    
    /**
    * listenWorkMessage1 接收和 china.# 有关的消息
    */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "shawn.queue1"),
            exchange = @Exchange(name = "topic.exchange", type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))
    public void listenWorkMessage1(String message) throws InterruptedException {
        System.out.println("消费者【1】接受消息:" + message + "-" + LocalDateTime.now());
        Thread.sleep(20);
    }
    
   /**
    * listenWorkMessage2 接收和 #.news 有关的消息
    */
   @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "shawn.queue2"),
            exchange = @Exchange(name = "topic.exchange", type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenWorkMessage2(String message) throws InterruptedException {
        System.out.println("消费者【2】接受消息:" + message + "-" + LocalDateTime.now());
        Thread.sleep(200);
    }
}

在 Publisher 项目中编写测试方法

@Test
void testSendTopicExchange() throws InterruptedException {
    String exChangeName = "topic.exchange";
    String message = "贾君鹏,你妈喊你回家吃饭";
    // 此处的 routingKey 需要按照TopicExchange的规则编写
    String routingKey = "china.food";
    rabbitTemplate.convertAndSend(exChangeName, routingKey, message);
}

定义的交换机和队列信息,均可以在 RabbitMQ Web UI 中看到

在这里插入图片描述

四 消息转换器

在SpringAMQP中,接受消息的类型时Object,也就是说,我们可以发送任何类型的对象给消费者,SpringAMQP会帮助我们进行序列化成字节后发送。

Spring中对消息对象的处理是由 SpringAMQP中的一个名为MessageConvert来处理的。默认实现是SimpleMessageConvert,基于JDK的ObjectOutputStream来完成。

通过重新定义一个 MessageConverter 的 Bean 来修改序列化方式。

推荐使用 JSON 方式序列化,消息体将会更加短小精悍,传输速度更快。

引入 jackson-dataformat-xml 依赖

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.14.1</version>
</dependency>

定义 Bean

@Configuration
public class MessageConverterConfig {
    @Bean
    public Jackson2JsonMessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

编写一个传输对象的简单队列测试示例

@Test
void testSendObjectMessage() {
    String queueName = "object.queue";
    SystemLog log = new SystemLog();
    log.setAddress("陕西省西安市");
    log.setAge(29);
    log.setName("shawn");
    log.setId(1L);
    log.setPassword("123456");

    rabbitTemplate.convertAndSend("", queueName, log);
}

此时消费者接受到的消息类型将会是Json格式,将Json信息转换对应的对象就可以拿到对象消息了。

在这里插入图片描述

注意:发送消息和接收消息时注意使用相同的 MessageConverter。可直接将消息转换为发送时的对象(自定义类型需手动转换)

完结撒花。

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

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

相关文章

分布式数据库与集中式数据库的差异

第一章&#xff1a;分布式数据库与集中式数据库的差异 1. 数据库是核心的IT基础设施 • 互联网业务增长&#xff0c;带动核心系统升级 • 核心系统引入数据库分布式与云化改造&#xff0c;支撑横向平滑扩展 • 5G规模推广&#xff0c;带动IT系统升级 • 5G具备大带宽和超低延时…

基于java+springboot+mybatis+vue+mysql的企业客户信息反馈平台

项目介绍 企业客户信息反馈平台能够通过互联网得到广泛的、全面的宣传&#xff0c;让尽可能多的用户了解和熟知企业客户信息反馈平台的便捷高效&#xff0c;不仅为客户提供了服务&#xff0c;而且也推广了自己&#xff0c;让更多的客户了解自己。对于企业客户信息反馈而言&…

小蓝本 第一本 《因式分解技巧》 第六章 二元二次式的分解 笔记(第六天)

小蓝本 第一本 《因式分解技巧》 第六章 二元二次式的分解 笔记&#xff08;第六天&#xff09;前言二元二次式的分解研究对象类型普通二元二次式基本形式分解方法总体总结——长十字相乘注意三元齐次式基本形式分解方法总体总结——长十字相乘注意提示习题6题目题解前言 今天…

【数据库】MySQL和Navicate安装和使用

MySQL和Navicate安装使用MySQLNavicate使用数据库MySQL 1、下载 可以考虑在官网下载或者在其它途径获取MySQL https://www.mysql.com/ download-》选择免费版或者其他版本-》选择系统和版本号-》根据需要下载 MySQL的Windows安装版只提供 32 位了 2、运行安装文件 可以选择…

基于51单片机的交通信号灯系统设计

功能&#xff1a; 十字路口交通灯控制程序&#xff1a; 正常时&#xff0c;EW方向计时60s&#xff0c;SN方向计时40s 若按时间加按键&#xff08;Add_Button&#xff09;按钮&#xff0c;EW、SN方向各加5s&#xff0c;不可大于99s&#xff0c;79s 若按时间减按键&#xff08;R…

全网最新的Fiddler(3):fiddler界面工具栏介绍

fiddler界面工具栏介绍 &#xff08;1&#xff09;WinConfig&#xff1a;windows 使用了一种叫做“AppContainer”的隔离技术&#xff0c;使得一些流量无法正常捕获&#xff0c;在 fiddler中点击 WinConfig 按钮可以解除这个诅咒&#xff0c;这个与菜单栏 Tools→Win8 Loopback…

【Flutter 组件】003-基础组件:按钮

【Flutter 组件】003-基础组件&#xff1a;按钮 文章目录【Flutter 组件】003-基础组件&#xff1a;按钮一、ElevatedButton 悬浮按钮1、概述2、构造方法3、示例代码示例运行结果二、TextButton 文本按钮1、概述2、构造方法3、示例代码示例运行结果三、OutlinedButton 边框按钮…

本地搭建xxl-job服务及连接验证

1、本地搭建xxl-job服务 1.1、使用git下载https://github.com/xuxueli/xxl-job.git代码 1.2、使用idea打开下载的项目&#xff0c;切换2.2.0分支 1.3、 源码主要包括3部分&#xff0c;admin模块是控制台的 core包是核心包&#xff0c;包括一些调度的逻辑等&#xff0c;项目中…

负荷预测|一种改进支持向量机的电力负荷预测方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f4dd;目前更新&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;电力系统相关知识&#xff0c;期刊论文&…

[附源码]Python计算机毕业设计SSM基于的防疫隔离服务系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

从Qt 4到Qt 5(一)Qt 5.2安装、程序迁移和发布

导语 Qt 5的第二个重大版本Qt 5.2的beta版终于发布了&#xff0c;Qt 5.2是官方一再强调开发Android要使用的版本。经过了近一年的等待&#xff0c;这次终于可以完成夙愿&#xff0c;继续更新Qt系列教程了。在后面的教程中会尽量涉及大家经常问到、急需解决的问题&#xff0c;也…

YOLOV7 目标检测模型调试记录

前言 YOLO系列在目标检测领域可谓名声赫赫&#xff0c;其性能表现不俗&#xff0c;如今其已经更新到了YOLOV7版本&#xff0c;今天便来一睹其风采。 博主之前只是对YOLO算法的原理一知半解&#xff0c;并未实验&#xff0c;因此并不熟练&#xff0c;因此&#xff0c;借此机会来…

【JavaSE成神之路】一文洞悉Java的方法

哈喽&#xff0c;我是兔哥呀&#xff0c;今天就让我们继续这个JavaSE成神之路&#xff01; 这一节啊&#xff0c;咱们要学习的内容是Java语言的方法。 目录 1.什么是Java方法 2.快速入门Java方法 3.如何调用Java方法 4.关于Java方法重载这件事 作业 1.什么是Java方法 Jav…

补遗: CS61a

补遗&#xff1a; CS61a 通过“圣经”《SICP》 了解到这门课。SCIP读着有点困难&#xff0c;想通过课程的引导。但是这个课程要比书基础很多&#xff0c;就当对计科学习的回顾和补遗了。本笔记也会在我读完 SICP 后更新。 课程地址&#xff1a;CS 61A Fall 2022 参照原书目录…

第52篇 Qt Quick简介

导语 在上一篇我们已经安装好了Qt 5.5&#xff0c;现在正式开始学习Qt5中全新的Qt Quick编程。Qt Quick对于大部分人来说是一个全新的概念&#xff0c;对这样一个全新的东西要怎样开始学习呢&#xff1f;在没有专业书籍&#xff08;当然&#xff0c;《Qt 5编程入门》现在已经出…

java计算机毕业设计ssm在线学习系统的设计与开发3nnzq(附源码、数据库)

java计算机毕业设计ssm在线学习系统的设计与开发3nnzq&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&a…

MapReduce 工作原理

文章目录MapReduce 工作原理一、MapReduce工作过程二、MapTask工作原理三、Reduce Task工作原理四、Shuffle工作原理五、MapReduce编程组件1、inputFormat组件2、Mapper组件3、Reducer组件4、Partitioner组件5、Combiner组件6、OutputFormat组件六、MapReduce运行模式1、本地运…

关于个人网站的搭建日志(1)静态网页

注&#xff1a;这篇文章不是教程&#xff0c;仅仅是个人踩过的一些坑的整理&#xff0c;建议大家去和前辈们多多交流&#xff0c;祝早日进步 &#xff08;1&#xff09;第一步&#xff0c;关于服务器&#xff1a; 我这里使用的是阿里云的云服务器&#xff08;0元一个月。。。…

视觉合集4

这里总结一些论文,包括多标签分类、姿态估计、目标检测、HOI、小样本学习等研究方向。 01 面向具有标注噪声的人脸表情识别 Attack can Benefit: An Adversarial Approach to Recognizing Facial Expressions under Noisy Annotations 大规模人脸表情数据集通常表现出极端的…

基于VBA实现电缆结构自动出图(一)——自动出圆形

大家敢相信吗&#xff0c;原来VBA竟然可以实现电缆结构自动出图&#xff0c;换句话说&#xff0c;只要输入数据&#xff0c;VBA会自动将电缆的结构画出来&#xff0c;同时还可以渲染&#xff0c;结果竟然不输画图软件&#xff0c;真真让我刮目相看。这里我就不过多介绍VBA了&am…