RabbitMQ之快速入门、上手

前言

学习一样新技术、新框架,最重要的是学习其思想、原理。即原理性思维。

如果是因为工作原因,需要快速上手RabbitMQ,本篇或许适合你。

核心概念

  • Connection:publisher/consumer 和 broker 之间的 TCP 连接
  • Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
  • Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
  • Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
  • VHost: 虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离

image.png

安装

  • 修改host,添加下面数据

192.168.204.179 rabbit

  • 拉取镜像

docker pull rabbitmq:3.8.2-management

  • 启动容器
docker run -d --restart=always \
    --hostname rabbit \
    --name=rabbitmq \
    -p 5671:5617 -p 5672:5672 -p4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 \
    rabbitmq:3.8.2-management 

5671:开启管理插件时,管理界面接口
5671、5672:AMQP
4369:守护进程
25672:

用户、Virtual Host管理

用户角色

访问管理界面:192.168.204.179:15672
默认账号密码:guest

角色列表:

  • 超级管理员(administrator)可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。
  • 监控者(monitoring)可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
  • 策略制定者(policymaker)可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
  • 普通管理者(management)仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
  • 其他无法登陆管理控制台,通常就是普通的生产者和消费者。

image.png

Virtual Hosts配置

每个虚拟主机就相当于一个独立的MQ服务器,虚拟主机之间相互隔离。

  • 创建虚拟主机,主机名称一般以/开头:

image.png

  • 权限设置:

image.png

添加队列

image.png

添加交换机

  • 添加交换机

image.png

  • 绑定Queue

image.png

整合SpringBoot

		<!--amqp协议的起步依赖坐标-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<!--rabbit测试依赖坐标-->
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--SpringBoot测试依赖坐标-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

# RabbitMQ 服务host地址
spring.rabbitmq.host=rabbit
# 端口
spring.rabbitmq.port=5672
# 虚拟主机地址
spring.rabbitmq.virtual-host=/mytest
# rabbit服务的用户名
spring.rabbitmq.username=test
# rabbit服务的密码
spring.rabbitmq.password=123456

五种工作模式

1.简单模式

image.png

  • 先添加一个Queue

image.png

  • 生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        /**
         * 参数1:消息队列名称
         * 参数2:消息内容
         */
        rabbitTemplate.convertAndSend("/myqueue", "hello 小兔子!");
    }

}

  • 消费者
@Component
@RabbitListener(queues = "/myqueue")
public class SimpleListener {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====接收消息====>"+msg);
    }
}

2.工作队列模式

image.png
相比于简单模式,这个模式下会存在多个消费者。

  • 在普通模式基础上,增加订阅者即可:
@Component
@RabbitListener(queues = "/myqueue")
public class SimpleListener2 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====222接收消息====>"+msg);
    }
}
  • 测试:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        /**
         * 参数1:消息队列名称
         * 参数2:消息内容
         */
        for (int i = 0; i < 10000; i++) {
        rabbitTemplate.convertAndSend("/myqueue", "hello 小兔子!" + i);

        }
    }

}

交换机类型

image.png

消息将发给交换机,交换机的类型决定了它会怎么处理这个消息:

  • Direct(直连交换机):使用消息的 Routing Key 与队列的 Binding Key 进行精确匹配,只有消息的 Routing Key 与队列的 Binding Key 完全相同时,消息才会被路由到该队列。非广播
  • Fanout(扇形交换机):忽略消息的 Routing Key,直接将消息发送到所有与交换机绑定的队列。广播消息
  • Topic(主题交换机):使用通配符匹配的方式将消息路由到多个队列。通配符由字符 “#” 和 “” 组成,其中 “#” 表示匹配零个或多个单词,“” 表示匹配一个单词。支持灵活的消息路由。
  • Headers(头交换机):使用消息的 Headers 属性来匹配队列的 Binding Headers,从而确定消息的路由。较少使用,一般情况下使用 Direct、Fanout 或 Topic 类型的交换机就能满足大部分场景。

符号 “#” 匹配一个或多个词,符号""匹配不多不少一个词。因此“audit.#” 能够匹配到“audit.irs.corporate”,但是“audit.” 只会匹配到 “audit.irs”。

总结就是交换机负责消息转发,不进行数据存储,如果没有找到绑定的队列或匹配的队列,消息将会丢失。

3.广播模式

将同一个消息广播到订阅的多个消费者手中。

创建队列和交换机

添加队列
添加交换机
绑定队列到交换机

生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQSPTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        /**
         * 参数1:消息队列名称
         * 参数2:消息内容
         */
        for (int i = 0; i < 10000; i++) {
            rabbitTemplate.convertAndSend("/fanout_exchange", null, "hello 小兔子!" + i);

        }
    }

}

消费者
@Component
@RabbitListener(queues = "/fanout_queue1")
public class SimpleListener_Fanout1 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====222接收消息====>"+msg);
    }
}

@Component
@RabbitListener(queues = "/fanout_queue2")
public class SimpleListener_Fanout2 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====3333接收消息====>"+msg);
    }
}

4.路由模式

在将队列绑定到交换机的时候,需要指定路由key;发送消息的时候也要指明路由key。
image.png

配置交换机

image.png

生产者

/**
 * 路由
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQRoutingTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        /**
         * 参数1:消息队列名称
         * 参数2:消息内容
         */
        for (int i = 0; i < 10000; i++) {
            if (i % 2 == 0) {
                rabbitTemplate.convertAndSend("/routing_exchange", "info", "hello 小兔子!" + i);

            } else {

                rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + i);
            }

        }
    }

}

消费者
@Component
@RabbitListener(queues = "/routing_err1")
public class Routing_Err1 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====routing_err1接收消息====>"+msg);
    }
}

@Component
@RabbitListener(queues = "/routing_err2")
public class Routing_Err2 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====routing_err2接收消息====>"+msg);
    }
}

@Component
@RabbitListener(queues = "/routing_info1")
public class Routing_info1 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====routing_info1接收消息====>"+msg);
    }
}

5.主题模式(通配符)

#:匹配零个或多个。
*:匹配一个。
image.png

配置

image.png

生产者

/**
 * topc
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTopicTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void contextLoads() {
        /**
         * 参数1:消息队列名称
         * 参数2:消息内容
         */
        for (int i = 0; i < 10000; i++) {
            if (i % 2 == 0) {
                rabbitTemplate.convertAndSend("/topic_exchange", "test.info", "hello 小兔子!" + i);
            } else {

                rabbitTemplate.convertAndSend("/topic_exchange", "test.err", "hello 小黑子!" + i);
            }
            rabbitTemplate.convertAndSend("/topic_exchange", "my.test.warn", "hello 小白子!" + i);
        }
    }

}

消费者
@Component
@RabbitListener(queues = "/topic_info")
public class Topic1 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====topic_info接收消息====>"+msg);
    }
}

@Component
@RabbitListener(queues = "/topic_err")
public class Topic2 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====topic_err接收消息====>"+msg);
    }
}

@Component
@RabbitListener(queues = "/topic_warn")
public class Topic3 {
    @RabbitHandler
    public void simpleHandler(String msg){
        System.out.println("=====topic_warn接收消息====>"+msg);
    }
}

工作模式总结

  • 简单模式:一个生产者和一个消费者,无需交换机。
  • 工作队列模式:一个生产者,多个消费者(竞争消息),无需交换机。
  • 发布订阅模式:fanout类型的交换机。消费广播到每个绑定的queue中。
  • 路由模式:direct类型的交换机。消息发送到路由key精确匹配的队列中。
  • 通配符模式:topic类型的交换机。消息发送到通配符匹配的路由key的队列中。

高级特性

生产者确认

rabbitmq提供了两种方式来保证投递的可靠性:

  • confirm 确认模式:消息发到交换机,不管是否成功,都回调confirmCallback。
  • return 退回模式:投递失败会回调returnCallback。
配置
spring:
  rabbitmq:
    password: 123456
    username: test
    virtualHost: /mytest
    port: 5672
    host: rabbit
    publisherReturns: true
    publisherConfirmType: SIMPLE

案例
@Slf4j
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback{

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            log.info("消息发送到exchange成功");
        } else {
            log.info("消息发送到exchange失败");
        }
    }
}


@Slf4j
public class RabbitReturnCallback implements RabbitTemplate.ReturnsCallback {

    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        log.info("消息发送失败:{}", returnedMessage.getMessage());

    }
}

@Configuration
public class RabbitCallBackConfig {
    @Resource
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitConfirmCallback());
        rabbitTemplate.setReturnsCallback(new RabbitReturnCallback());
    }

}


@RestController
public class TestController {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @GetMapping("confirmCallBack")
    public String confirmCallBack() {
        for (int i = 0; i < 10000; i++) {
            rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + i);
        }
        return "ok";
    }

    @GetMapping("returnCallBack")
    public String returnCallBack() {
        for (int i = 0; i < 10000; i++) {
            // 不存在的routingkey
            rabbitTemplate.convertAndSend("/routing_exchange", "err2", "hello 小黑子!" + i);
        }
        return "ok";
    }

}

消费者确认

消费者消息确认有三种类型:

  • 无确认:none。收到生产者的消息之后,直接ACK。
  • 手动确认:manual。消费者需要显式的告诉RabbitMQ消息已经确认。手动确认更安全。
  • 自动确认:auto。客户端收到消息之后,mq自动ACK。

为什么手动确认更安全?
消费者处理消息失败时,可以重新处理消息。其它优势:1.消费者可以根据处理能力控制消费速率;2.批量确认多个信息。


import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义监听器,监听到消息之后,立即执行onMessage方法
 */
@Component
public class CustomAckConsumerListener implements ChannelAwareMessageListener {

    private static final AtomicInteger errCount = new AtomicInteger();

    /**
     * 监听到消息之后执行的方法
     *
     * @param message 消息内容
     * @param channel 消息所在频道
     */
    @Override
    public void onMessage(Message message, Channel channel) throws Exception {

        //获取消息内容
        byte[] messageBody = message.getBody();
        String msg = new String(messageBody, "UTF-8");
        System.out.println("接收到消息,执行具体业务逻辑{} 消息内容:" + msg);

        //获取投递标签
        MessageProperties messageProperties =
                message.getMessageProperties();
        long deliveryTag = messageProperties.getDeliveryTag();

        // 模拟业务错误
        if(errCount.getAndIncrement() % 2 == 0) {
            System.out.println("业务报错,重回队列");
            channel.basicNack(deliveryTag, false, true);
            return;
        }
        // 签收消息,前提条件,必须在监听器的配置中,开启手动签收模式
        // 参数1:消息投递标签
        // 数2:是否批量签收:true一次性签收所有,false,只签收当前消息
        channel.basicAck(deliveryTag, false);
        System.out.println("手动签收完成:{}");
    }
}


import cn.lsj.consumer.listener.CustomAckConsumerListener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ListenerConfiguration {


    /**
     * 注入消息监听器适配器
     *
     * @param customAckConsumerListener 自定义监听器对象
     */
    @Bean
    public MessageListenerAdapter messageListenerAdapter(CustomAckConsumerListener customAckConsumerListener) {
        //创建自定义监听器适配器对象
        return new MessageListenerAdapter(customAckConsumerListener);
    }

    /**
     * 注入消息监听器容器
     *
     * @param connectionFactory      连接工厂
     * @param messageListenerAdapter 自定义的消息监听器适配器
     */
    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {
        //简单的消息监听器容器对象
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        //绑定消息队列
        container.setQueueNames("/routing_err3");
        //设置连接工厂对象
        container.setConnectionFactory(connectionFactory);
        //设置消息监听器适配器
        container.setMessageListener(messageListenerAdapter);
        //设置手动确认消息:NONE(不确认消息),MANUAL(手动确认消息),AUTO(自 动确认消息)
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return container;
    }

}

消费端限流

应用场景:

  • 宕机恢复之后处理大量的积压消息导致业务系统再次崩溃。
  • 短时间大量请求来到,业务系统不支持同时处理那么多的消息。

要求:必须为手动确认消息。

package cn.lsj.consumer.config;

import cn.lsj.consumer.listener.CustomAckConsumerListener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ListenerConfiguration {


    /**
     * 注入消息监听器适配器
     *
     * @param customAckConsumerListener 自定义监听器对象
     */
    @Bean
    public MessageListenerAdapter messageListenerAdapter(CustomAckConsumerListener customAckConsumerListener) {
        //创建自定义监听器适配器对象
        return new MessageListenerAdapter(customAckConsumerListener);
    }

    /**
     * 注入消息监听器容器
     *
     * @param connectionFactory      连接工厂
     * @param messageListenerAdapter 自定义的消息监听器适配器
     */
    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {
        //简单的消息监听器容器对象
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        //绑定消息队列
        container.setQueueNames("/routing_err3");
        //设置连接工厂对象
        container.setConnectionFactory(connectionFactory);
        //设置消息监听器适配器
        container.setMessageListener(messageListenerAdapter);
        //设置手动确认消息:NONE(不确认消息),MANUAL(手动确认消息),AUTO(自 动确认消息)
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        
        //设置消费端限流,每次拉取消息多少条,默认是250条
        container.setPrefetchCount(1);

        
        return container;
    }

}

TTL(消息存活时间)

消息过期时间到未被消费则被自动清楚。

可以针对消息设置,也可以针对队列设置。

消息过期时间
rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + count.getAndIncrement(), m -> {
    // 10秒
    m.getMessageProperties().setExpiration(String.valueOf(10000L));
    return m;
});
队列过期时间

参数message-TTL ,单位毫秒,在创建队列的时候添加。
image.png

注意点
  • 消息过期的优先级高于队列;
  • 即使消息已经过期,也要等到前面的消费被消费或删除才进一步处理,所以要注意消息堆积的情况。

死信队列

是什么

当消息过期未消费;当消费者拒接消息且不放回源队列;当队列队列达到最大限度时。

以上三个场景,导致了Dead message(死消息)的产生。
image.png

如何设置死信队列

image.png

延迟队列

是什么

消息到达队列之后,不会马上被消费,而是等待一段时间之后才会被消费。

应用场景
  • 定时任务;
  • 订单超时;
  • 定时通知;
  • 消息重试\错误重试:消息放入延迟队列,过一段时间重试;
如何实现?

死信队列配合过期队列实现延迟队列

如下图,我们不设置过期队列的消费者,让消息过期之后进入死信队列,达到延迟效果。
image.png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop之Yarn 详细教程

1、yarn 的基本介绍和产生背景 YARN 是 Hadoop2 引入的通用的资源管理和任务调度的平台&#xff0c;可以在 YARN 上运行 MapReduce、Tez、Spark 等多种计算框架&#xff0c;只要计算框架实现了 YARN 所定义的 接口&#xff0c;都可以运行在这套通用的 Hadoop 资源管理和任务调…

nodejs+vue+微信小程序+python+PHP的冷链物流配送系统-计算机毕业设计推荐

对于冷链物流信息调度系统所牵扯的管理及数据保存都是非常多的&#xff0c;例如管理员&#xff1b;首页、用户管理&#xff08;管理员、客户、业务员、配送员&#xff09;客户管理&#xff08;货物信息、客户运输单、车辆信息、调度安排&#xff09;这给管理者的工作带来了巨大…

【机组期末速成】指令系统|机器指令概述|操作数类型与操作类型|寻址方式|指令格式

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;计算机组成原理&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 目录 前言&#xff1a; 一、本章考点总览 二、考点分析 1、以下有关指令系统的说法中错误的是&#xff08; &#xff09;。 2…

【电商项目实战】MD5登录加密及JSR303自定义注解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

mac安装k8s环境

安装kubectl brew install kubectl 确认一下安装的版本 kubectl version --client 如果想在本地运行kubernetes 需要安装minikube brew install minikube 需要注意安装minikube需要本地的docker服务是启动的 启动 默认连接的是google的仓库 minikube start 指定阿…

下载和安装AD14 - Altium Designer 14.3.20.54863

这个版本应该还支持XP 系统[doge]&#xff0c;总之就是想安装一下&#xff0c;没什么特别的意义。 下载 资源来自毛子网站&#xff1a;https://rutracker.net/forum/viewtopic.php?t5140739&#xff0c;带上个网页翻译插件就行。要用磁力链接下载&#xff0c;推荐用qbittorr…

远程网络唤醒家庭主机(openwrt设置)

远程网络唤醒家庭主机&#xff08;openwrt设置&#xff09; 前提&#xff1a; 1.配置好主板bios的网络唤醒功能(网络教程自己百度一下找) 2.电脑开启网络唤醒功能(网络教程自己百度一下找) 3.路由器通过ddns实现域名和动态IP绑定内网穿透方法汇总_不修改光猫进行内网穿透-C…

最新AI系统ChatGPT网站H5系统源码,支持AI绘画,GPT语音对话+ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

代码质量评价及设计原则

1.评价代码质量的标准 1.1 可维护性 可维护性强的代码指的是: 在不去破坏原有的代码设计以及不引入新的BUG的前提下,能够快速的修改或者新增代码. 不易维护的代码指的是: 在添加或者修改一些功能逻辑的时候,存在极大的引入新的BUG的风险,并且需要花费的时间也很长. 代码可…

如何让python在手机上运行,python程序在手机上运行

大家好&#xff0c;给大家分享一下python怎么在手机上运行爱心代码&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 1. 写在前面的话 天天都在PC端运行Python代码的我&#xff0c;今天突然灵光一现&#xff0c;想着是不是能够在移动端运行P…

C++继承与派生——(6)派生类的析构函数

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 站在巨人的肩上&#xff0c;是为了超过…

初识智慧城市

文章目录 智慧家居 智慧社区 智慧交通 智慧医疗 智慧教育 智慧旅游 智慧农业 智慧安防 智慧家居 利用智能语音、智能交互等技术,实现用户对家居系统各设备的远程操控和能控制如开关窗帘(窗户)、操控家用电器和照明系统、打扫卫生等操作。利用计算机视觉等技术,对被照看…

Spring boot:3.X + Security OAuth2 自定义登录页面、登出后跳转到登录页

本文描述了基于 Spring Oauth2 的 code 模式&#xff0c;实现登陆同时授权、自定义登录界面、登出页面的功能。 1.Maven依赖&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactI…

SAP缓存 表缓存( Table Buffering)

本文主要介绍SAP中的表缓存在查询数据&#xff0c;更新数据时的工作情况以及对应概念。 SAP表缓存的工作 查询数据 更新数据 删除数据 表缓存的概念 表缓存技术设置属性 不允许缓冲&#xff1a; 允许缓冲&#xff0c;但已关闭&#xff1a; 缓冲已激活&#xff1a; 已…

笔记1:基于锚框(先验框)的目标检测

一、边缘框&#xff08;bounding box&#xff09; 1.1 定义 边缘框&#xff1a;真实标注的物体位置 2.1 表示方式 1、&#xff08;x1,y1)和(x2,y2) 2、&#xff08;x1,y1)和w,h 二、锚框(anchor box)/先验框&#xff08;prior bounding box&#xff09; 2.1 定义 对边缘…

webpack打包批量替换路径(string-replace-webpack-plugin插件)

string-replace-webpack-plugin 是一个用于在 webpack 打包后的文件中替换字符串的插件。它可以用于将特定字符串替换为其他字符串&#xff0c;例如将敏感信息从源代码中移除或对特定文本进行本地化处理。比如文件的html、css、js中的路径地址想批量更改一下 http://localhost:…

软件安装(详细讲解VMware虚拟机的安装及VMvare安装winServer2012)实施必用!!!

文章目录 前言&#xff1a;一、VMware虚拟机的安装1.1VMware虚拟机的安装步骤(一直下一步)&#xff1a;1.2Vmware配置虚拟机&#xff1a; 二、VMware安装winServer20122.1激活windows: 思维导图&#xff1a; 前言&#xff1a; 本编给大家详细的介绍VMware虚拟机的安装和安装wi…

自定义View绘制基础之Canvas

画布Canvas Canvas&#xff08;画布&#xff09;是一个用于绘制图形的重要类&#xff0c;它提供了一组绘图操作的方法&#xff0c;允许你在屏幕上绘制各种形状、图像和文本。下面是Canvas类的主要函数&#xff1a; 目录 一、绘制图形和图像 1.drawColor 2.drawCircle 3.dr…

Gitlab 定时备份

要求 1.为了能够备份和恢复&#xff0c;请确保你的系统上安装了Rsync #Debian/Ubauntu sudo apt-get install rsync # RHEL/Centos sudo yum install rsync 配置与备份目标机器之间的免密认证 修改gitlab配置文件 vim /etc/gitlab/gitlab.rb gitlab_rails[manage_backup_…

服务器运行状况监控工具

服务器运行状况监视提供了每个服务器状态和性能的广泛概述&#xff0c;通过监控服务器指标&#xff0c;如 CPU 使用率、内存消耗、I/O、磁盘使用率、进程等&#xff0c;服务器运行状况监控可以避免服务器停机。 服务器性能监控指标 服务器是网络中最重要的组件之一&#xff0…