kafka消费端之消费者协调器和组协调器
文章目录
- 概述
- 回顾历史
- 老版本获取消费者变更
- 老版本存在的问题
- 消费者协调器和组协调器
- 新版如何解决老版本问题
- 再均衡过程
- **第一阶段CFIND COORDINATOR**
- **第二阶段(JOINGROUP)**
- 选举消费组的lcader
- 选举分区分配策略
- 第三阶段(SYNC GROUP)
- 消费组元数据信息
- 第四阶段(HEARTBEAT)
- 消费者协调器和控制器关系
- 分区分配与消费者组管理
- 选举与状态同步
- 心跳检测与故障处理
概述
上一章我们讲解kafka消费端的分区分配策略时留下了两个问题,这章我们通过消费者协调器和组协调器继续详细回答那两个问题。其中我们会回归历史说明消费者协调器和组协调器出现的原因,最后会说下消费者协调器和控制器在工作上有何关联。
回顾历史
老版本获取消费者变更
消费者协调器和组协调器的概念是针对新版的消费者客户端而言的,Kafka建立之初并没有它们。旧版的消费者客户端是使用ZooKeeper的监听器(Watcher)来实现这些功能的。每个消费组王()在ZooKeeper中都维护了一个/consumers//ids路径,在此路径下使用临时节点记录录属于此消费组的消费者的唯一标识CconsumerIdString )consumerIdString由消费者启动时创建。消费者的唯一标识由aconsumer.id+主机名+时间截+UUID的部分信息构成,其中consumerid是旧版消费者客户端中的配置,相当于新版客户端中的client.id。
每个broker、主题和分区在ZooKeeper中也都对应一个路径:/brokers/ids/记录了 host、port及分配在此broker上的主题分区表;/brokers/topics/记录了每个分区的leader副本、ISR集合等信息。/brokers/topics//partitions//state记录了当前leader副本、leaderepoch等信息。如下图:
每个消费者在启动时都会在/consumers//ids和/brokers/ids路径上注册一个监听器。当/consumers//ids路径下的子节点发生变化时,表示消费组中的消费者发生了变化;当/brokers/ids路径下的子节点发生变化时,表示broker出现了增减。这样通过ZooKeeper所提供的Watcher,每个消费者就可以监听消费组和Kafka集群的状态了。
老版本存在的问题
这种方式下每个消费者对ZooKeeper E的相关路径分别进行监听,当触发再均衡操作时,一个消费组下的所有消费者会同时进行再均衡操作,而消费者之间并不知道彼此操作的结果,这样可能导致Kafka工作在一个不正确的状态。与此同时,这种严重依赖于ZooKeeper集群的做法还有两个比较严重的问题。
- (1)羊群效应(HerdEffect):所谓的羊群效应是指ZooKeeper中一个被监听的节点变化大量的Watcher通知被发送到客户端,导致在通知期间的其他操作延迟,也有可能发生类似死锁的情况。
- (2)脑裂问题(SplitBrain):消费者进行再均衡操作时每个消费者都与ZooKeeper进行通信以判断消费者或broker变化的情况,由于ZooKeeper本身的特性,可能导致在同一时刻各个消费者获取的状态不一致,这样会导致异常问题发生。
消费者协调器和组协调器
新版如何解决老版本问题
新版的消费者客户端对此进行了重新设计,将全部消费组分成多个子集,每个消费组的子集在服务端对应一个GroupCoordinator对其进行管理,GroupCoordinator是Kafka服务端中用于管理消费组的组件。而消费者客户端中的ConsumerCoordinator组件负责与GroupCoordinator进行交互,也就是发送心跳,加入group请求、提交位移等请求。
再均衡过程
ConsumerCoordinatorr与GroupCoordinator之间最重要的职责就是负责执行消费者再均衡的操作,包括前面提及的分区分配的工作也是在再均衡期间完成的。就目前而言,一共有如下几种情形会触发再均衡的操作:
- 有新的消费者加入消费组。
- 有消费者容机下线。消费者并不一定需要真正下线,例如遇到长时间的GC、网络延退导致消费者长时间未向GroupCoordinator发送心跳等情况时,GroupCoordinator会认为消费者已经下线。
- 有消费者主动退出消费组(发送LeaveGroupRequest请求)。比如客户端调用了
unsubscribleO方法取消对某些主题的订阅。 - 消费组所对应的GroupCoorinator节点发生了变更。
- 消费组内所订阅的任一主题或者主题的分区数量发生变化。
当有消费者加入消费组时,消费者、消费组及组协调器之间会经历一下几个阶段。
第一阶段CFIND COORDINATOR
消费者需要确定它所属的消费组对应的GroupCoordinator所在的broker,并创建与该broker相互通信的网络连接。如果消费者已经保存了与消费组对应的(GroupCoordinator节点的信息,并且与它之间的网络连接是正常的,那么就可以进入第二阶段。否则,就需要向集群中的某个节点发送FindCoordinatorRequest请求来查找对应的GroupCoordinator,这里的“某个节点”并非是集群中的任意节点,而是负载最小的节点。
Kafka 在收到 FindCoordinatorRequest请求之后,会根据coordinator_key(也就是groupId)查找对应的GroupCoordinator节点,如果找到对应的GroupCoordinator则会返回其相对应的nodeid、host和port信息。具体查找GroupCoordinator的方式是先根据消费组groupId的哈希值计算_consumer_offsets中的分区编号。找到对应的_consumer_offsets中的分区之后,再寻找此分区leader副本所在的broker节点,该broker节点即为这个groupId所对应的GroupCoordinator节点。消费者groupId最终的分区分配方案及组内消费者所提交的消费位移信息都会发送给此分区leader副本所在的broker节点,让此broker节点既扮演GroupCoordinator的角色,又扮演保存分区分配方案和组内消费者位移的角色,这样可以省去很多不必要的中间轮转所带来的开销。
第二阶段(JOINGROUP)
在成功找到消费组所对应的GroupCoordinator之后就进入加入消费组的阶段,在此阶段的消费者会向GroupCoordinator发送JoinGroupRequest请求,并处理响应。
JoinGroupRequest中的group_protocols域为数组类型,其中可以囊括多个分区分配策略,这个主要取决于消费者客户端参数partition.assignment.strategy的配置。如果配置了多种策略,那么JoinGroupRequest中就会包含多个protocol name和protocol metadata。
如果是原有的消费者重新加入消费组,那么在真正发送JoinGroupRequest请求之前还要执行一些准备工作:
-
(1)如果消费端参数enable.auto.commit设置为tue(默认值也为tue),即开启自动提交位移功能,那么在请求加入消费组之前需要向GroupCoordinator提交消费位移。这个过程是阻塞执行的,要么成功提交消费位移,要么超时。
-
(2)如果消费者添加了自定义的再均衡监听器(ConsumerRebalanceListener),那么此时会调用onPartitionsRevokedO方法在重新加入消费组之前实施自定义的规则逻辑,比如清除一些状态,或者提交消费位移等。
-
(3)因为是重新加入消费组,之前与GroupCoordinator节点之间的心跳检测也就不需要了,所以在成功地重新加入消费组之前需要禁止心跳检测的运作。
消费者在发送JoinGroupRequest请求之后会阻塞等待Kafka服务端的响应。服务端在收到JoinGroupRequest请求后会交由GroupCoordinator来进行处理。GroupCoordinator首先会对JoinGroupRequest请求做合法性校验,比如group_id是否为空、当前broker节点是否是请求的消费者组所对应的组协调器、rebalance_timeout的值是否在合理的范围之内。如果消费者是第一次请求加入消费组,那么JoinGroupRequest请求中的memberid值为null,即没有它自身的唯一标志,此时组协调器负责为此消费者生成一个memberid。这个生成的算法很
简单,具体如以下伪代码所示。
String memberId = clientId +"-"+UUID.randomuuID().toString()
其中clientld为消费者客户端的clientd,对应请求头中的clientid。由此可见消费者的memberid由clientId和UUID用“”字符拼接而成。
选举消费组的lcader
GroupCoordinator需要为消费组内的消费者选举出一个消费组的leader,这个选举的算法也很简单,分两种情况分析。如果消费组内还没有leader,那么第一个加入消费组的消费者即为消费组的leader。如果某一时刻leader消费者由于某些原因退出了消费组,那么会重新选举一个新的leader,这个重新选举leader的过程又更“随意”了。在GroupCoordinator中消费者的信息是以HashMap的形式存储的,其中key为消费者的memberid,value是消费者相关的元数据信息。leaderId表示leader
消费者的memberid,它的取值为HashMap中的第一个键值对的key,这种选举的方式基本上和随机无异。总体上来说,消费组的leader选举过程是很随意的。
选举分区分配策略
每个消费者都可以设置自己的分区分配策略,对消费组而言需要从各个消费者呈报上来的各个分配策略中选举一个彼此都“信服”的策略来进行整体上的分区分配。这个分区分配的选举并非由leader消费者决定,而是根据消费组内的各个消费者投票来决定的。这里所说的“根据组内的各个消费者投票来决定”不是指GroupCoordinator还要再与各个消费者进行进一步交互,而是根据各个消费者呈报的分配策略来实施。最终选举的分配策略基本上可以看作被各个
消费者支持的最多的策略,具体的选举过程如下:
(1)收集各个消费者支持的所有分配策略,组成候选集candidates。
(2)4每个消费者从候选集candidates中找出第一个自身支持的策略,为这个策略投上一票。
(3)计算候选集中各个策略的选票数,选票数最多的策略即为当前消费组的分配策略。
如果有消费者并不支持选出的分配策略,那么就会报出异常IllegalArgumentException:Memberdoesnotsupportprotocol。所以请不要为同一个消费组的不同消费者设置不同的分配策略,以防出现问题。需要注意的是,这里所说的“消费者所支持的分配策略”是指partition.assignment.strategy参数配置的策略,如果这个参数值只配置了RangeAssignor,那么这个消费者客户端只支持RangeAssignor分配策略,而不是消费者客户端代码中实现的3种分配策略及可能的自定义分配策略。
在此之后,Kafka服务端就要发送JoinGroupResponse响应给各个消费者,leader消费者和其他普通消费者收到的响应内容并不相同。leader消费者会收到最终的分配策略以及消费者成员信息,而普通消费者只能收到最终的分配策略。由此可见,Kafka把分区分配的具体分配交还给客户端,自身并不参与具体的分配细节,这样即使以后分区分配的策略发生了变更,也只需要重启消费端的应用即可,而不需要重启服务端。该过程可见如下图:
第三阶段(SYNC GROUP)
leader消费者根据在第二阶段中选举出来的分区分配策略来实施具体的分区分配,在此之后需要将分配的方案同步给各个消费者,此时leader消费者并不是直接和其余的普通消费者同步分配方案,而是通过GroupCoordinator这个“中间人”来负责转发同步分配方案的。在第三阶段,也就是同步阶段,各个消费者会向GroupCoordinator发送SyncGroupRequest请求来同步分配方案,如下图所示:
服务端在收到消费者发送的SyncGroupRequest请求之后会交由GroupCoordinator来负责具体的逻辑处理。GroupCoordinator同样会先对SyncGroupRequest请求做合法性校验,在此之后会将从leader消费者发送过来的分配方案提取出来,连同整个消费组的元数据信息一起存入Kafka的consumer_offsets主题中,最后发送响应给各个消费者以提供给各个消费者各自所属的分配方案。
消费者在获得消费分区后会连接broker进行消费,并定期发送心跳给消费者协调器表明自己活着。
消费组元数据信息
我们知道消费者客户端提交的消费位移会保存在Kafka的consumer_offsets主题中,这里也一样,只不过保存的是消费组的元数据信息(GroupMetadata)。具体来说,每个消费组的元数据信息都是一条消息,不过这类消息并不依赖于具体版本的消息格式,因为它只定义了消息中的key和value字段的具体内容,所以消费组元数据信息的保存可以做到与具体的消息格式无关。
第四阶段(HEARTBEAT)
进入这个阶段之后,消费组中的所有消费者就会处于正常工作状态。在正式消费之前,消费者还需要确定拉取消息的起始位置。假设之前已经将最后的消费位移提交到了GroupCoordinator,并且GroupCoordinator将其保存到了Kafka内部的consumeroffsets主题中,此时消费者可以通过OffsetFetchRequest请求获取上次提交的消费位移并从此处继续消费。
消费者通过向GroupCoordinator发送心跳来维持它们与消费组的从属关系,以及它们对分区的所有权关系。只要消费者以正常的时间间隔发送心跳,就被认为是活跃的,说明它还在读取分区中的消息。心跳线程是一个独立的线程,可以在轮询消息的空档发送心跳。如果消费者停止发送心跳的时间足够长,则整个会话就被判定为过期,GroupCoordinator也会认为这个消费者已经死亡,就会触发一次再均衡行为。消费者的心跳间隔时间由参数heartbeat.interval.ms指定,默认值为3000,即3秒,这个参数必须比session.timeout.ms参数设定的值要小,一般情况下heartbeat.interval.ms的配置值不能超过session.timeout.ms配置值的1/3。这个参数可以调整得更低,以控制正常重新平衡的预期时间。
如果一个消费者发生崩溃,并停止读取消息,那么GroupCoordinator会等待一小段时间,确认这个消费者死亡之后才会触发再均衡。在这一小段时间内,死掉的消费者并不会读取分区里的消息。这个一小段时间由session.timeout.ms参数控制,该参数的配置值必须在broker端参数group.min.session.timeout.ms(默认值为6000,即6秒)和group.max.session.timeout.ms(默认值为300000,即5分钟)允许的范围内。
还有一个参数max.po11.interval.ms,它用来指定使用消费者组管理时poll0方法调用之间的最大延退,也就是消费者在获取更多消息之前可以空闲的时间量的上限。如果此超时时间期满之前polio没有调用,则消费者被视为失败,并且分组将重新平衡,以便将分区重新分配给别的成员。
除了被动退出消费组,还可以使用LeaveGroupRequest请求主动退出消费组。
消费者协调器和控制器关系
在Kafka中,控制器(Controller)与组协调器(Group Coordinator)在工作上存在一定的交互,具体体现在以下几个方面:
分区分配与消费者组管理
-
控制器负责分区管理:控制器负责管理Kafka集群中的分区状态,如分区的创建、删除以及副本的分配等。当一个新的分区被创建时,控制器会决定该分区的副本分布在哪些Broker上。
-
组协调器依赖分区信息:组协调器在进行消费者组的分区分配时,需要依赖控制器所管理的分区元数据信息。例如,组协调器要根据分区的数量、副本分布以及消费者组内消费者的数量和位置等信息,来为消费者分配合适的分区,以实现负载均衡和高效的数据消费。
选举与状态同步
-
控制器主导Broker选举:在Kafka集群中,当Broker出现故障或新的Broker加入时,控制器会负责选举新的Leader Broker以及进行相关的状态变更。
-
组协调器获取选举结果:组协调器需要与控制器进行交互,以获取最新的Broker选举结果和集群状态信息。这有助于组协调器了解哪些Broker是活跃的,哪些是不可用的,从而更好地管理消费者组的状态,确保消费者能够正确地连接到合适的Broker进行数据消费。
心跳检测与故障处理
-
组协调器检测消费者心跳:组协调器通过心跳机制来检测消费者的存活状态。如果消费者长时间没有发送心跳,组协调器会认为消费者可能出现了故障,并进行相应的处理,如重新分配分区。
-
控制器协助故障判断:在这个过程中,组协调器可能会与控制器进行交互,以获取更全面的集群状态信息,来确定消费者的故障是否是由于Broker故障等原因引起的。控制器可以提供关于Broker状态、分区状态等方面的信息,帮助组协调器更准确地判断故障情况,并采取合适的措施,如触发重新平衡操作。
相关文章:

kafka消费端之消费者协调器和组协调器
文章目录 概述回顾历史老版本获取消费者变更老版本存在的问题 消费者协调器和组协调器新版如何解决老版本问题再均衡过程**第一阶段CFIND COORDINATOR****第二阶段(JOINGROUP)**选举消费组的lcader选举分区分配策略 第三阶段(SYNC GROUP&…...

线上hbase rs 读写请求个数指标重置问题分析
问题描述: 客户想通过调用hbase的jmx接口获取hbase的读写请求个数,以此来分析HBase读写请求每日增量。 但是发现生产,测试多个集群,Hbase服务指标regionserver读写请求个数存在突然下降到0或者大幅度下降情况。 需要排查原因: 某个Region的读写请求数:会发现经常会重置为…...

DeepSeek-R1 本地电脑部署 Windows系统 【轻松简易】
本文分享在自己的本地电脑部署 DeepSeek,而且轻松简易,快速上手。 这里借助Ollama工具,在Windows系统中进行大模型部署~ 1、安装Ollama 来到官网地址:Download Ollama on macOS 点击“Download for Windows”下载安装包&#x…...

数据库,数据表的增删改查操作
一.数据库的基本操作 (1)创建数据库 创建数据库就是在数据库系统中划分一块存储数据的空间,方便数据的分配、放置和管理。在MySQL中使用CREATE DATABASE命令创建数据库,语法格式如下: CREATE DATABASE数据库名称; 注:…...

VUE 集成企微机器人通知
message-robot 便于线上异常问题及时发现处理,项目中集成企微机器人通知,及时接收问题并处理 企微机器人通知工具类 export class MessageRobotUtil {constructor() {}/*** 发送 markdown 消息* param robotKey 机器人 ID* param title 消息标题* param…...

《Java核心技术 卷II》Java平台的脚本机制
Java平台的脚本机制 脚本引擎:可以执行用某种特定语言编写的脚本类库。 ScriptEngineManager 虚拟机启动时用它发现可用的脚步引擎。 调用getEngineFactories来枚举这些引擎。 知道所需要的引擎可以通过名字、MIME类型或拓展文件来请求它。 var manager new S…...

Ollama + AnythingLLM + Deepseek r1 实现本地知识库
1、Ollama:是一个开源的大型语言模型 (LLM)服务工具,旨在简化在本地运行大语言模型的过程,降低使用大语言模型的门槛。 2、AnythingLLM:是由Mintplex Labs Inc. 开发的一款全栈应用程序,旨在构建一个高效、可定制、…...

记录 | WPF基础学习Style局部和全局调用
目录 前言一、Style1.1 例子1.2 为样式起名字1.3 BasedOn 继承上一个样式 二、外部StyleStep1 创建资源字典BaseButtonStyle.xamlStep2 在资源字典中写入StyleStep3 App.xaml中写引用路径【全局】Step4 调用三、代码提供四、x:Key和x:Name区别 更新时间 前言 参考文章ÿ…...

PromptSource安装报错
一、现象 运行命令:streamlit run promptsource/app.py 报错: streamlit run promptsource/app.py Traceback (most recent call last): File "/usr/local/bin/streamlit", line 5, in <module> from streamlit.cli import main File …...

Leetcode 3448. Count Substrings Divisible By Last Digit
Leetcode 3448. Count Substrings Divisible By Last Digit 1. 解题思路2. 代码实现 题目链接:3448. Count Substrings Divisible By Last Digit 1. 解题思路 这一题的话我们走的是一个累积数组的思路。 首先,我们使用一个cache数组记录下任意段数字…...

Maven 下载与配置教程:附百度网盘地址
一、引言 在 Java 开发领域,Maven 是一款广泛使用的项目管理和构建工具。它能够帮助开发者自动化项目的构建、依赖管理和文档生成等任务,从而提高开发效率和项目质量。本文将详细介绍 Maven 的下载方法、安装步骤、配置教程以及使用技巧,并提…...

基于 GEE 的网格化降雨数据可视化与时间序列分析
目录 1 数据介绍 2 代码解析 3 完整代码 4 运行结果 降雨数据在遥感分析中是一个重要的因素,GEE 中有许多相关的降雨量数据以供研究。本文分享以 CHIRPS 网格化降雨量数据为例,进行时间序列分析,统计研究区年降雨量,以及将年降雨量导出至 csv 中。 1 数据介绍 气候灾…...

java-初识List
List: List 是一个接口,属于 java.util 包,用于表示有序的元素集合。List 允许存储重复元素,并且可以通过索引访问元素。它是 Java 集合框架(Java Collections Framework)的一部分 特点: 有序…...

windows下搭建tftp服务器+网络启动Linux
1. 安装windows下tftp服务器 https://pjo2.github.io/tftpd64/2. SD卡启动,tftp下载zImage、tdb文件,从SDRAM启动 下载linux镜像 tftp 80800000 zImage下载设备树 tftp 83000000 imx6ull-my-emmc.dtb启动 bootz 80800000 - 83000000 3. 网络启动 改…...

DeepSeek使用技巧大全(含本地部署教程)
在人工智能技术日新月异的今天,DeepSeek 作为一款极具创新性和实用性的 AI,在众多同类产品中崭露头角,凭借其卓越的性能和丰富的功能,吸引了大量用户的关注。 DeepSeek 是一款由国内顶尖团队研发的人工智能,它基于先进…...

PHP 面向对象编程详解
PHP 面向对象编程详解 引言 PHP 作为一种广泛使用的服务器端脚本语言,自诞生以来就以其简洁、易学、高效的特点受到开发者的喜爱。随着互联网技术的不断发展,PHP 也在不断地进化,其中面向对象编程(OOP)已经成为 PHP …...

openbmc web/redfish到底层设计(持续更新...)
1.说明 本节是厘清openbmc的界面层web或者redfish到底层数据获取与展示。 不可或缺的是先阅读官方关于redfish的设计文档: 1.https://github.com/openbmc/docs/blob/master/designs/redfish-authorization.md2.https://github.com/openbmc/docs/blob/master/designs/redfish…...

Linux init
如何检查你的 Linux 系统是否使用 systemd | Linux 中国|init|echo|stat|linux_网易订阅 初始化软件 Systemd,OpenRC,SysVinit,Busybox,runit,s6。 查看软件 stat /sbin/init readlink -f /sbin/init Artix Linux 有…...

Maven 版本管理与 SNAPSHOT 详解
1. Maven 版本管理概述 在 Maven 项目中,版本号(Version)是用于区分不同软件版本的重要标识。Maven 提供了一套标准的版本管理机制,包括: 正式版本(Release Version)快照版本(SNAP…...

TCP三次握手全方面详解
文章目录 (1) 三次握手各状态CLOSE状态SYN_SENT状态SYN_RECV状态ESTABLISHED状态 (2) 为什么握手时的seqnum是随机值,以及acknum的功能(3) 三次握手中的半连接队列(SYN队列)和全连接队列(ACCEPT队列)半连接队列全连接队…...

【C#】一维、二维、三维数组的使用
在C#中,数组是用于存储固定数量相同类型元素的数据结构。根据维度的不同,可以分为一维数组、二维数组(矩阵阵列)、三维数组等。每增加一个维度,数据的组织方式就会变得更加复杂。 一维数组 一维数组是最简单的数组形…...

MIT开源7B推理模型Satori:用行动思维链进行强化学习,增强自回归搜索
自OpenAI的o1发布以来,研究社区为提升开源LLM的高级推理能力做出了诸多努力,包括使用强大的教师模型进行蒸馏、蒙特卡洛树搜索(MCTS)以及基于奖励模型的引导搜索等方法。 本研究旨在探索一个新的研究方向:使LLM具备自回…...

【JVM详解二】常量池
一、常量池概述 JVM的常量池主要有以下几种: class文件常量池运行时常量池字符串常量池基本类型包装类常量池 它们相互之间关系大致如下图所示: 每个 class 的字节码文件中都有一个常量池,里面是编译后即知的该 class 会用到的字面量与符号引…...

w200基于spring boot的个人博客系统的设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...

【算法】快速排序算法的实现:C 和 C++ 版本
1. 算法简介 快速排序(Quick Sort)是由英国计算机科学家霍尔(C.A.R. Hoare)在1960年提出的一种高效的排序算法。它采用了分治法(Divide and Conquer)策略,通常具有很好的性能。在平均情况下,快速排序的时间复杂度为 O(n log n),但在最坏情况下可能退化为 O(n^2),不过…...

前沿科技一览未来发展趋势
脑机接口技术在医疗康复领域有了新进展。这技术让机器读懂大脑信号,帮助病人找回身体功能。 比如,瘫痪人士可以用它来控制假肢。在美国,一名瘫痪者通过这个技术,能用自己意念控制机械臂,喝到饮料。这种技术对提升患者…...

js滚动到页面最底部
setTimeout(()> { //延后执行,等页面渲染结束let container document.querySelector(.raise-flag-content); //找到当前divif (container) {container.scrollTop container.scrollHeight - (container.clientHeight - 400 );}})container.scrollTop container…...

视觉硬件选型和算法选择(CNN)
基础知识 什么是机械视觉: 机械视觉是一种利用机器代替人眼来进行测量和判断的技术,通过光学系统、图像传感器等设备获取图像,并运用图像处理和分析算法来提取信息,以实现对目标物体的识别、检测、测量和定位等功能。 机械视觉与人类视觉有什…...

Mybatis篇
1,什么是Mybatis ( 1 )Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁…...

【Python】元组
个人主页:GUIQU. 归属专栏:Python 文章目录 1. 元组的本质与基础概念1.1 不可变序列的意义1.2 元组与数学概念的联系 2. 元组的创建方式详解2.1 标准创建形式2.2 单元素元组的特殊处理2.3 使用 tuple() 函数进行转换 3. 元组的基本操作深入剖析3.1 索引操…...