RocketMQ 消费者分类与分组

news2025/1/10 14:22:47

文章目录

  • 消费者分类
    • PushConsumer
      • PushConsumer 内部原理
      • 使用注意事项
    • SimpleConsumer
      • invisibleDuration 消息不可见时间
  • 消费者分组(消费者负载均衡)
    • 广播消费和共享消费
    • 负载均衡策略
    • 多个消费者消费顺序消息
      • 多消费者消费顺序消息示例
  • 消费者分组管理
    • 关闭自动创建消费者分组
    • 使用 admin tool 工具管理消费者分组
      • updateSubGroup 更新或修改订阅关系(更新或修改消费者分组信息)
      • deleteSubGroup 从 Broker 删除订阅关系(删除消费者分组)

消费者分类

RocketMQ 支持 PushConsumer 、 SimpleConsumer 以及 PullConsumer 这三种类型的消费者。每一种类型的消费者处理逻辑都回经过 消息获取—>消息处理—>消费状态提交 3 个阶段,只是它们每一个的实现方式不一样。

对比项PushConsumerSimpleConsumerPullConsumer
实现方式使用监听器回调接口返回消费结果,消费者仅允许在监听器范围内处理消费逻辑。业务方自行实现消息处理,并主动调用接口返回消费结果。业务方自行按队列拉取消息,并可选择性地提交消费结果
消费并发度管理由SDK管理消费并发度由业务方消费逻辑自行管理消费线程由业务方消费逻辑自行管理消费线程
负载均衡粒度5.0 SDK是消息粒度,更均衡,早期版本是队列维度消息粒度,更均衡队列粒度,吞吐攒批性能更好,但容易不均衡
接口灵活度高度封装,不够灵活原子接口,可灵活自定义原子接口,可灵活自定义
适用场景适用于无自定义流程的业务消息开发场景适用于需要高度自定义业务流程的业务开发场景仅推荐在流处理框架场景下集成使用

在实际使用场景中,PullConsumer 仅推荐在流处理框架中集成使用,大多数消息收发场景使用 PushConsumer 和 SimpleConsumer 就可以满足需求。PullConsumer 在gRPC协议客户端中目前还尚未实现。

相同的 ConsumerGroup 下严禁混用 PullConsumer 和其他两种消费者,否则会导致消息消费异常。

PushConsumer

PushConsumers 是一种高度封装的消费者类型,消费消息仅通过消费监听器处理业务并返回消费结果。消息的获取、消费状态提交以及消费重试都通过 RocketMQ 的客户端SDK完成。我们的示例目前都是使用的 PushConsumer ,这里我们就不再贴示例代码了。

PushConsumer 中我们的实际消费代码 是通过消费监听器 MessageListener 实现,其 public ConsumeResult consume(MessageView messageView) 方法的执行结果分如下几种情况:

  • 返回ConsumeResult.SUCCESS:表示该消息处理成功,服务端按照消费结果更新消费进度
  • 返回ConsumeResult.FAILURE:示该消息处理失败,需要根据消费重试逻辑判断是否进行重试消费
  • 方法内部异常:该结果按照消费失败处理,需要根据消费重试逻辑判断是否进行重试消费

这里涉及到 PushConsumer 的重试逻辑,这个我们会在后续 《RocketMQ 消息重试机制》一章中统一说明

PushConsumer 内部原理

在PushConsumer类型中,消息的实时处理能力是基于SDK内部的典型Reactor线程模型实现的。如下图所示,SDK内置了一个长轮询线程,先将消息异步拉取到SDK内置的缓存队列中,再分别提交到消费线程中,触发监听器执行本地消费逻辑

在这里插入图片描述

使用注意事项

  • 禁止在 PushConsumer 监听方法中再次定义线程来分发处理。因为这会导致消息尚未处理完成,而监听方法提前返回处理成功,导致如果线程执行失败,无法进入重试逻辑。
  • 如果消息处理时间不可预估(直接时间超长)或业务逻辑复杂需要用到多线程时,请使用 SimpleConsumer。

PushConsumer 默认的处理线程数量为 20,可通过 PushConsumerBuilder.setConsumptionThreadCount() 设置线程数

总结:PushConsumer 内置实现了消息的消费、确认、顺序消息的顺序性、异常重试等多项功能,使用起来比较简单,但其扩展性没有 SimpleConsumer 好,不允许使用异步化和自定义处理流程。

SimpleConsumer

SimpleConsumer 的使用涉及多个接口调用,由业务逻辑按需调用接口获取消息,然后分发给业务线程处理消息,最后按照处理的结果调用提交接口,返回服务端当前消息的处理结果。示例如下:

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.SimpleConsumer;
import org.apache.rocketmq.client.apis.message.MessageView;

import java.time.Duration;
import java.util.Collections;
import java.util.List;

public class SimpleConsumerDemo {

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

        // 用于提供:生产者、消费者、消息对应的构建类 Builder
        ClientServiceProvider provider = ClientServiceProvider.loadService();
        // 构建配置类(包含端点位置、认证以及连接超时等的配置)
        ClientConfiguration configuration = ClientConfiguration.newBuilder()
                // endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081
                .setEndpoints(MyMQProperties.ENDPOINTS)
                .build();

        // 设置过滤条件(这里为使用 tag 进行过滤)
        String tag = "ORDER_SUBMIT";
        FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);


        SimpleConsumer simpleConsumer = provider.newSimpleConsumerBuilder()
                // 设置消费者分组。
                .setConsumerGroup("MY_ORDER_SUBMIT_GROUP")
                // 设置接入点。
                .setClientConfiguration(configuration)
                // 设置预绑定的订阅关系。
                .setSubscriptionExpressions(Collections.singletonMap("MY_NORMAL_TOPIC", filterExpression))
                // 设置从服务端接受消息的最大等待时间
                .setAwaitDuration(Duration.ofSeconds(3))
                .build();

        //  一次获取多少个消息
        int maxMessageNum = 10;
        // 获取消息后,这些消息多长时间内,对其他消费者不可见
        Duration invisibleDuration = Duration.ofSeconds(30);

        try {
            List<MessageView> messageViewList = simpleConsumer.receive(maxMessageNum,invisibleDuration);
            // 循环处理所有取出的消息
            messageViewList.forEach(messageView -> {
                // TODO 业务代码
                System.out.println("simpleConsumer 消费消息:" + messageView);

                try {
                    // 处理完成需要手动编写 ACK 代码提交消费结果
                    simpleConsumer.ack(messageView);
                } catch (ClientException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }

            });
        }catch (Exception e){
            // 处理 receive 获取消息时的异常(比如:消息拉取失败),常常需要重新获取消息
            e.printStackTrace();
        }

    }

}

相对于 PushConsumer 的高度封装, SimpleConsumer 更加灵活,适用于需要异步处理、需要高度自定义消费的消息。

invisibleDuration 消息不可见时间

invisibleDuration 此参数十分重要,需要根据消息消费的时长来设定,该参数除了设定消息的不可见时间,还涉及到消息的重试与重试时间间隔。甚至还有一个方法:simpleConsumer.changeInvisibleDuration(); 和 simpleConsumer.changeInvisibleDurationAsync() 来在必要时修改延长这个不可见时间。关于消费重试的相关问题,这个我们会在后续 《RocketMQ 消息重试机制》一章中统一说明。

消费者分组(消费者负载均衡)

消费者分组,在gRPC协议客户端代码中就是一个字符串如 setConsumerGroup(“MY_FIFO_GROUP”),但其有十分重要的作用。

消费者分组是 RocketMQ 系统中承载多个消费行为一致的消费者的负载均衡分组,消费者分组并不是运行实体,而是一个逻辑资源。通过消费者分组内初始化多个消费者实现消费性能的水平扩展以及高可用容灾。

广播消费和共享消费

RocketMQ 的领域模型中,一条消息可以由多个消费者分组订阅,一个消费组中又可以初始化多个消费者,同一个消费组的消费者共享消费消息,不同消费组的间的消费者是广播消费。

在这里插入图片描述

  • 消费组间广播消费 :如上图所示,每个消费者分组只初始化唯一一个消费者,每个消费者可消费到消费者分组内所有的消息,各消费者分组都订阅相同的消息,以此实现单客户端级别的广播一对多推送效果。
  • 消费组内共享消费 :如上图所示,每个消费者分组下初始化了多个消费者,这些消费者共同分担消费者分组内的所有消息,实现消费者分组内流量的水平拆分和均衡负载。

关于广播消费的情况,我们在前面的章节 《RocketMQ 发送事务消息》的示例中已经使用。

负载均衡策略

RocketMQ 5.x + gRPC 客户端,默认是消息粒度的负载均衡策略,同一消费组中的多个消费者按照消息的粒度平均分摊主题中的所有消息,但具体某个消息被分发到哪个消费者是随机的。

消费者获取某条消息后,服务端会将该消息加锁,保证这条消息对其他消费者不可见,直到该消息消费成功或消费超时,因此,即使多个消费者同时消费同一队列的消息,服务端也可保证消息不会被多个消费者重复消费。

在这里插入图片描述

多个消费者消费顺序消息

在这里插入图片描述

如上图所述,队列Queue1中有4条顺序消息,这4条消息属于同一消息组G1,存储顺序由M1到M4。在消费过程中,前面的消息M1、M2被消费者Consumer A1处理时,只要消费状态没有提交,消费者A2是无法并行消费后续的M3、M4消息的,必须等前面的消息提交消费状态后才能消费后面的消息。

多消费者消费顺序消息示例

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;

import java.nio.ByteBuffer;
import java.util.Collections;

public class FifoConsumerManyDemo {

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



        // 用于提供:生产者、消费者、消息对应的构建类 Builder
        ClientServiceProvider provider = ClientServiceProvider.loadService();

        // 构建配置类(包含端点位置、认证以及连接超时等的配置)
        ClientConfiguration configuration = ClientConfiguration.newBuilder()
                // endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081
                .setEndpoints(MyMQProperties.ENDPOINTS)
                .build();


        // 设置过滤条件(这里为使用 tag 进行过滤)
        String tag = "ORDER_CREATE";
        FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);

        // 构建消费者
        provider.newPushConsumerBuilder()
                .setClientConfiguration(configuration)
                // 设置消费者分组
                .setConsumerGroup("MY_FIFO_GROUP")
                // 设置主题与消费者之间的订阅关系
                .setSubscriptionExpressions(Collections.singletonMap("MY_FIFO_TOPIC", filterExpression))
                .setMessageListener(messageView -> {
                    System.out.println(messageView);
                    ByteBuffer rs = messageView.getBody();
                    byte[] rsByte = new byte[rs.limit()];
                    rs.get(rsByte);

                    if(new String(rsByte).contains("user2")) {
                        System.out.println("consumer1 Message body:" + new String(rsByte));
                        // 处理消息并返回消费结果。
                        System.out.println("consumer1 Consume message successfully, messageId=" + messageView.getMessageId());
                    }

                    return ConsumeResult.SUCCESS;
                }).build();



        provider.newPushConsumerBuilder()
                .setClientConfiguration(configuration)
                // 设置消费者分组
                .setConsumerGroup("MY_FIFO_GROUP")
                // 设置主题与消费者之间的订阅关系
                .setSubscriptionExpressions(Collections.singletonMap("MY_FIFO_TOPIC", filterExpression))
                .setMessageListener(messageView -> {
                    System.out.println(messageView);
                    ByteBuffer rs = messageView.getBody();
                    byte[] rsByte = new byte[rs.limit()];
                    rs.get(rsByte);

                    if(new String(rsByte).contains("user2")) {
                        System.out.println("consumer2 Message body:" + new String(rsByte));
                        // 处理消息并返回消费结果。
                        System.out.println("consumer2 Consume message successfully, messageId=" + messageView.getMessageId());
                    }
                    return ConsumeResult.SUCCESS;
                }).build();


        // 如果不需要再使用 PushConsumer,可关闭该实例。
        // pushConsumer.close();

    }

}

生产者代码,请参考《RocketMQ 发送顺序消息》一章。为了方便验证,我们在示例中判断了单个用户的所有消息消费情况做打印,看看结果是否是根据单个用户顺序的。

消费者分组管理

RocketMQ 5.x 开始,除了我们前面说的 Topic 不建议自动创建外,消费者分组也不建议自动创建了。消费者分组也可以通过 mqadmin 命令来添加或删除。

关闭自动创建消费者分组

5.1.3 版本的 RocketMQ目前是默认开启字段添加主题的,可以配置 borker 参数 autoCreateSubscriptionGroup 为 false 来禁用。关于如何配置,我们会在后续文章中说明。我们建议 Topic 和 消费者分组都禁用自动创建。避免在使用上的混乱,给主题和消费者分组的管理上带来不必要的麻烦。

使用 admin tool 工具管理消费者分组

前面我们提到,消费者分组是否重要, 因为消息的负载均衡策略、重试机制、顺序消费等属性都与之相关。在 ~/stroe/config/subscriptionGroup.json 文件中,可以看到我们在顺序消息的消费者中定义的消费者分组(MY_FIFO_GROUP)信息,信息如下:

"MY_FIFO_GROUP":{
        "attributes":{},
        // 当前消费者 brokerId,-i 参数
        "brokerId":0,
        "consumeBroadcastEnable":false,// 是否是广播模式,-d 参数
        "consumeEnable":true, // 分组是否允许消费,-s 参数
        "consumeFromMinEnable":false,// 是否从最小offset开始消费,-m 参数
        "consumeMessageOrderly":true,// 是否顺序消费,-o 参数
        "consumeTimeoutMinute":15,
        "groupName":"MY_FIFO_GROUP",
        "groupRetryPolicy":{ // 重试策略
                "type":"CUSTOMIZED"
        },
        "groupSysFlag":0,
        // 当消费者数量变化时是否通知其他消费者负载均衡,-a 参数
        "notifyConsumerIdsChangedEnable":true,
        "retryMaxTimes":16, // 最大重试次数
        "retryQueueNums":1, // 重试队列数
        // 如果当前broker执行消费慢,使用另一个broker的id,-w 参数
        "whichBrokerWhenConsumeSlowly":1 
},

Store 目录的配置,可以通过 ./mqadmin getBrokerConfig 命令查看 Broker 的相关配置中的 storePathRootDir 对应的位置。此地址默认为 ~/stroe

updateSubGroup 更新或修改订阅关系(更新或修改消费者分组信息)

查看命令选项

$> ./mqadmin updateSubGroup -h

usage: mqadmin updateSubGroup [-a <arg>] [--attributes <arg>] [-b <arg>] [-c <arg>] [-d <arg>] -g <arg> [-h]
       [-i <arg>] [-m <arg>] [-n <arg>] [-o <arg>] [-p <arg>] [-q <arg>] [-r <arg>] [-s <arg>] [-w <arg>]
 -a,--notifyConsumerIdsChanged <arg>       notify consumerId changed
    --attributes <arg>                     attribute(+a=b,+c=d,-e)
 -b,--brokerAddr <arg>                     create subscription group to which broker
 -c,--clusterName <arg>                    create subscription group to which cluster
 -d,--consumeBroadcastEnable <arg>         broadcast
 -g,--groupName <arg>                      consumer group name
 -h,--help                                 Print help
 -i,--brokerId <arg>                       consumer from which broker id
 -m,--consumeFromMinEnable <arg>           from min offset
 -n,--namesrvAddr <arg>                    Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'
 -o,--consumeMessageOrderly <arg>          consume message orderly
 -p,--groupRetryPolicy <arg>               the json string of retry policy ( exp:
                                           {"type":"EXPONENTIAL","exponentialRetryPolicy":{"initial":5000,"max
                                           ":7200000,"multiplier":2}}
                                           {"type":"CUSTOMIZED","customizedRetryPolicy":{"next":[1000,5000,100
                                           00]}} )
 -q,--retryQueueNums <arg>                 retry queue nums
 -r,--retryMaxTimes <arg>                  retry max times
 -s,--consumeEnable <arg>                  consume enable
 -w,--whichBrokerWhenConsumeSlowly <arg>   which broker id when consume slowly

使用此命令,我们可以添加消费者分组,并且可以设置分组相关信息,比如-o 顺序消费,-r 最大重试次数等。-o 参数我们在《RocketMQ 发送顺序消息》一章使用了。

示例

./mqadmin updateSubGroup -n 127.0.0.1:9876 -g MY_FIFO_GROUP -o true -c DefaultCluster

-n、-c、-g 参数为必须

deleteSubGroup 从 Broker 删除订阅关系(删除消费者分组)

查看命令选项

$> ./mqadmin deleteSubGroup -h

usage: mqadmin deleteSubGroup [-b <arg>] [-c <arg>] -g <arg> [-h] [-n <arg>] [-r <arg>]
 -b,--brokerAddr <arg>     delete subscription group from which broker
 -c,--clusterName <arg>    delete subscription group from which cluster
 -g,--groupName <arg>      subscription group name
 -h,--help                 Print help
 -n,--namesrvAddr <arg>    Name server address list, eg: '192.168.0.1:9876;192.168.0.2:9876'

示例

$> ./mqadmin deleteSubGroup -n 127.0.0.1:9876 -g MY_FIFO_GROUP -c DefaultCluster

消费重试的相关问题我们将在下一章《RocketMQ 消息重试机制》中详细说明。

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

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

相关文章

八股文死记硬背打脸记

背景 我们都知道&#xff0c;再编程领域数据结构的重要性&#xff0c;常见的数据结构包括 List、Set、Map、Queue、Tree、Graph、Stack 等&#xff0c;其中 List、Set、Map、Queue 可以从广义上统称为集合类数据结构。而Java也提供了很多的集合数据结构以供开发者开箱即用&…

左神高级提升班2 约瑟夫环结构

目录 【案例1】 【题目描述】 【输入描述&#xff1a;】 【输出描述&#xff1a;】 【输入】 【输出】 【思路解析】 【代码实现】 【案例1】 【题目描述】 某公司招聘&#xff0c;有n个人入围&#xff0c;HR在黑板上依次写下m个正整数A1、A2、……、Am&#xff0c;然后…

alist windows桌面版下载安装

官网 Desktop | AList Docs 点击下载windows版本 安装 双击exe 修改安装路径 太可惜了&#xff0c;需要收费

LeetCode刷题---Add Two Numbers(一)

文章目录 &#x1f352;题目&#x1f352;解法一 迭代&#x1f352;解法二 递归&#x1f352;递归小案例&#x1f352;迭代 VS 递归 &#x1f352;题目 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只…

聚观早报 | 飞书签约韵达速递;蔚来首颗自研芯片“杨戬”量产

【聚观365】9月22日消息 飞书签约韵达速递 蔚来首颗自研芯片“杨戬”10月量产 靳玉志接任华为车 BU CEO 亚马逊发布全新Alexa语音助手 OpenAI推出图像生成器DALL-E 3 飞书签约韵达速递 近日&#xff0c;国内物流服务公司韵达快递宣布全员上飞书。飞书解决方案副总裁何斌表…

【大数据】Doris 构建实时数仓落地方案详解(一):实时数据仓库概述

本系列包含&#xff1a; Doris 构建实时数仓落地方案详解&#xff08;一&#xff09;&#xff1a;实时数据仓库概述Doris 构建实时数仓落地方案详解&#xff08;二&#xff09;&#xff1a;Doris 核心功能解读Doris 构建实时数仓落地方案详解&#xff08;三&#xff09;&#…

云原生Kubernetes:K8S集群list-watch机制与 pod调度约束

目录 一、理论 1.K8S的list-watch 机制 2.亲和性 二、实验 1. 指定调度节点 2.节点亲和性 3.亲和性和反亲和 三、问题 1.新生成pod一直为pending 2.如何一次性删除pod和deployment 3.pod亲和性资源报错 4.pod反亲和性资源报错 四、总结 一、理论 1.K8S的list-wat…

zookeeper + kafka

Zookeeper 概述 Zookeeper是一个开源的分布式服务管理框架。存储业务服务节点元数据及状态信息&#xff0c;并负责通知再 ZooKeeper 上注册的服务几点状态给客户端 Zookeeper 工作机制 Zookeeper从设计模式角度来理解: 是一个基于观察者模式设计的分布式服务管理框架&…

vector的扩容机制—为何是1.5倍或者是2倍

文章目录 前言一、Vector 扩容过程二、为什么是1.5倍或者2倍&#xff1f; 前言 在 C 编程中&#xff0c;Vector 是一种常用的动态数组容器。其大小是可以动态调整的&#xff0c;而在扩容操作中&#xff0c;Vector 通常会将容量增加为原来的两倍。本篇博客将详细介绍 Vector 扩…

Kindle电子书下载功能关闭怎么办,借助calibre和cpolar搭建私有的网络书库公网访问

Kindle中国电子书店停运不要慌&#xff0c;十分钟搭建自己的在线书库随时随地看小说&#xff01; 文章目录 Kindle中国电子书店停运不要慌&#xff0c;十分钟搭建自己的在线书库随时随地看小说&#xff01;1.网络书库软件下载安装2.网络书库服务器设置3.内网穿透工具设置4.公网…

RT-Thread(学习)

RT-Thread是一款完全由国内团队开发维护的嵌入式实时操作系统&#xff08;RTOS&#xff09;&#xff0c;具有完全的自主知识产权。经过16个年头的沉淀&#xff0c;伴随着物联网的兴起&#xff0c;它正演变成一个功能强大、组件丰富的物联网操作系统。 RT-Thread概述 RT-Threa…

openssl创建CA证书教程

配置生成CA证书 总示意图&#xff1a; (1)&#xff0c;通过openssl创建CA证书 第一步&#xff1a;创建一个秘钥&#xff0c;这个便是CA证书的根本&#xff0c;之后所有的东西都来自这个秘钥 # 通过rsa算法生成2048位长度的秘钥 openssl genrsa -out myCA.key 2048 第二步&#…

Wireshark TS | MQ 传输缓慢问题

问题背景 应用传输慢是一种比较常见的问题&#xff0c;慢在哪&#xff0c;为什么慢&#xff0c;有时候光从网络数据包分析方面很难回答的一清二楚&#xff0c;毕竟不同的技术方向专业性太强&#xff0c;全栈大佬只能仰望&#xff0c;而我们能做到的是在专注于自身的专业方向之…

什么是Selenium?使用Selenium进行自动化测试!

你知道什么是 Selenium 吗&#xff1f;你知道为什么要使用它吗&#xff1f;答案就在本文中&#xff0c;很高兴能够与你共飧。 自动化测试正席卷全球&#xff0c;Selenium 认证是业界最抢手的技能之一。 什么是 Selenium&#xff1f; Selenium 是一种开源工具&#xff0c;用于…

Spring Cloud Alibaba Nacos 2.2.3 (2) - 单机版启动 (winodows 和 linux )

Nacos 2.2.3 (1) - 下载与数据库配置 参考下载与数据库配置 启动服务器 执行 nacos-server-2.2.3\bin 下的startup.sh或者startup.cmd &#xff08;根据不同系统&#xff09; windows 下nacos 单机启动 方式一&#xff1a; 1&#xff0c;打开cmd 2&#xff0c;cd 到nacos-s…

数据库数据恢复-SQL SERVER数据库分区被格式化的数据恢复方案

SQL SERVER数据库故障类型&#xff1a; 1、SQL SERVER数据库文件被删除。 2、SQL SERVER数据库所在分区格式化。 3、SQL SERVER数据库文件大小变为“0”。 4、使用备份还原数据库时覆盖原数据库。 SQL SERVER数据库故障原因&#xff1a; 1、人为误操作。 2、文件系统损坏&#…

基于Yolov8的工业小目标缺陷检测实战(2):动态蛇形卷积(Dynamic Snake Convolution),实现暴力涨点 | ICCV2023

目录 1.工业油污数据集介绍 1.1 小目标定义 1.2 难点 1.3 工业缺陷检测算法介绍 1.3.1 YOLOv8 2.Dynamic Snake Convolution 2.1 Dynamic Snake Convolution加入到yolov8 3.训练结果分析 4.系列篇 1.工业油污数据集介绍 三星油污缺陷类别&#xff1a;头发丝和小黑点&…

AirtestIDE编辑窗内,脚本内容消失,显示一片红色怎么办?

airtest编辑窗内&#xff0c;脚本内容消失&#xff0c;显示一片红色怎么办 如果突然有一天打开脚本&#xff0c;发现脚本全都变成了红色的点&#xff08;因未知错误导致的脚本异常&#xff09;&#xff1a; 原本辛辛苦苦写的三百多行代码全消失&#xff0c;是不是很难受很心慌…

Android Camera2获取摄像头的视场角(FOV)信息

一、概念 FOV&#xff08;Field of View&#xff09;是一个用于描述视野范围的术语。它通常用于计算设备&#xff08;如摄像机、虚拟现实头显或眼睛&#xff09;所能捕捉到的可见区域。 水平FOV&#xff08;Horizontal FOV&#xff09;&#xff1a;描述视野在水平方向上的范围…

可以查看输入字数的手机备忘录软件用哪个?

在当下这个科技迅速发展的时代&#xff0c;我们常常需要依赖备忘录来记录重要的信息、灵感和待办事项。手机备忘录成为了我们随身携带的工具&#xff0c;但是你有没有遇到这样的情况&#xff1a;你写了很多内容&#xff0c;但不知道写了多少字&#xff1f;这个问题或许曾经困扰…