Kubernetes控制平面组件:etcd(一)

news2025/2/12 14:33:22

云原生学习路线导航页(持续更新中)

  • kubernetes学习系列快捷链接
    • Kubernetes架构原则和对象设计(一)
    • Kubernetes架构原则和对象设计(二)
    • Kubernetes架构原则和对象设计(三)
    • kubectl 和 kubeconfig 基本原理
    • kubeadm 升级 k8s集群 1.17到1.20
    • Kubernetes常见问题解答
    • 查看云机器的一些常用配置

本文主要对Kubernetes控制平面组件:etcd进行介绍,深入理解etcd的设计原理,深入分析 Raft 协议如何实现一致性,如何完成leader选举和日志复制等

1.Etcd介绍

1.1.Etcd简介

  • 任何一个系统都要有地方存数据,而在Kubernetes中唯一存储数据的就是etcd

  • etcd是CoreOS公司研发的,后来CoreOS被RedHat收购了

  • etcd的字面意思为 etc distribution,在Linux中etc目录一般是用于存储配置文件的,所以etcd可以理解为分布式配置管理中心。又因为etcd中存储的是key-value形式,所以又可以理解为 分布式键值存储数据库

  • 本节主要向大家分享etcd的设计理念,遵循的协议是什么,怎么保证数据一致,如何支持我们之前讲的watch机制,进而理解为什么Kubernetes会选它作为唯一的数据存储
    在这里插入图片描述

  • 分布式场景中需要考虑的一些问题

    • 保证服务高可用性
      • 在没有分布式部署时,单节点部署存在很多问题:数据备份压力(备份周期不好选择、备份数据有时效性、故障时需要人工手动恢复等)、单点故障导致的数据丢失问题。为了保证数据库的高可用,一般要多实例冗余部署。
      • 但是 有状态的服务 和 无状态的服务 高可用复杂性完全不可同日而语,无状态可以非常轻松的完成副本的扩缩,死一个无关紧要,但有状态应用,比如etcd数据库多个member组成的一个集群中数据怎么保持一致性 是非常复杂的事情。
    • 实现服务注册和发现
      • 在微服务架构中,a服务要访问b服务时,需要获取到b服务的地址,此时就需要服务注册和发现,很多时候注册中心使用键值对就可以满足这个需求,而且效率也比较高
    • 实现监听变更
      • 如果使用key-value存储一些数据,希望在某个key-value发生变化时得到通知,就需要有一个监听-通知机制
      • 这是一个典型的消息机制,可以使用消息队列实现
  • Etcd介绍

    • Etcd是一个 基于raft协议的 分布式键值存储数据库,对上述三个问题的处理比较友好。Etcd有分布式部署保证自身高可用和数据高可用,可以用key-value实现服务注册和发现,自带watch机制实现消息机制
    • Etcd中通过API访问数据,且具备可选的SSL客户端认证来保证数据安全性,有一套完备的认证授权体系,包括角色管理、用户管理等。
    • Etcd还具备key的过期和续约机制,使用
    • 不过Kubernetes没有使用Etcd自带的认证鉴权体系,所以etcd的速度还是不错的
    • Etcd 基于Raft协议保证数据一致性,单实例每秒支持1000次的写操作,2000次的读操作,随着Etcd的升级效果可能会有所提升。

1.2.Etcd的主要功能

在这里插入图片描述

  • Etcd的Key-value存储
    • 键值存储是Etcd的核心功能,结构简单,不需要像关系型数据库那样有结构化表格的row和column,key-value的存储保证了查询速度的迅速
    • 另外Etcd也支持数据持久化,存储包括内存数据和磁盘数据
  • Etcd的监听机制
    • Etcd的监听机制,就是利用watch机制把自己打造成一个消息队列,进而避免引入第三方消息组件
    • Kubernetes正是利用了Etcd的watch机制将自己变成一个异步组件
  • Etcd使用Lease实现过期续约机制
    • 比如说你给一个key设个1分钟,在一分钟有效期之内是可以get到这个数据的,超出1分钟这个数据就失效了,就get不到了
    • 当然,可以在过期前发起renew,对key进行续约
  • Etcd还可以用于实现 Compare And Swap 乐观锁机制
    • Compare And Swap 即更改之前先Get一把,看是否被修改过,没有修改过自己再发起修改。
    • CAS 可以用于服务发现和服务注册

1.3.Etcd的键值对存储

在这里插入图片描述

  • 在etcd中,key以B树的形式存储在内存中, value以B+树的形式存储在硬盘中

1.4.Etcd应用于服务注册与发现

在这里插入图片描述

有些人可能对服务注册和发现很熟悉,尤其是做过微服务开发的人,但有些人不一定了解。这里我们举一个最简单的例子来说明。

  • 在 Spring Cloud 中,它是一个微服务框架。我们先抛开 ETCD,Spring Cloud 本身有一个服务注册中心,叫做 Eureka
  • 服务提供者的注册
    • 如果你要写一个应用,作为一个服务提供方的角色,比如提供一个 REST API 给别人用,那么在部署时,通常会进行高可用部署。例如,你部署了 3 个实例。
    • 那么,别人如何使用你的服务呢?你部署的 3 个实例,每个实例的 IP 地址和端口你是知道的。你需要通过服务注册机制,将这些 IP 地址和端口注册到服务注册中心。这是第一步:将自己注册上去
  • 保活与健康检查
    • 第二个重点是保活。服务提供者需要不断进行健康检查,确保自己的实例是健康的还是异常的。健康检查非常重要,服务提供者会不断刷新注册中心的状态。
    • 如果注册中心在一段时间内没有收到某个实例的心跳,就会认为该实例已经失效。这个机制在很多服务注册中心中都是通用的,比如 Kubernetes 中的 Pod 探活机制(通过 Probe 探测 Pod 是否 Ready)。
  • 消费者的服务发现
    • 通过这种机制,服务提供者始终将健康的实例注册到服务注册中心。
    • 当消费者需要访问某个服务时,它会向服务注册中心查询当前可用的实例。
    • 例如,如果当前有 3 个实例都健康,服务注册中心会将这 3 个实例的 IP 地址返回给消费者,消费者即可访问这些实例。
  • 续约机制
    • 服务提供者需要不断续约,向注册中心表明自己仍然存活。ETCD 的续约和过期机制天然满足这种需求。
    • 例如,注册一个服务时,服务中包含 3 个实例,每个实例有自己的 IP 和健康状况。服务提供者会定期更新实例的状态,并以固定时间间隔续约。如果某个实例在指定时间内未完成续约,注册中心会认为该实例已失效。
    • 当消费者查询时,失效的实例不会被返回。这就是 ETCD 作为服务注册中心的一种用法,其机制与 Consul 等开源产品类似。

1.5.Etcd应用于消息发布和订阅

在这里插入图片描述

1.5.1.消息发布与订阅的基本概念

  • 消息发布与订阅是消息队列(Message Queue)的核心功能之一。有些同学可能不太理解消息中心是什么,其实消息中心就是这样的:它同样有多种角色,比如生产者和消费者。

1.5.2.生产者与消费者的角色

  • 生产者:负责产生消息。

  • 消费者:负责订阅消息。

  • 那么,生产者这边一旦有消息了,它就会通过消息中心告知消费者说:“哎,有数据了,你可以来处理了。”消费者就可以基于这个事件,去做一些配置管理,或者是做任何你想做的行为。这就是消息发布与订阅的功能。

1.5.3.ETCD 中的消息发布与订阅

那么接下来,我们讲一下 ETCD 中的消息发布与订阅功能。其实,ETCD 的三大核心功能我们已经讲过了,分别是键值存储、服务注册与发现,以及消息发布与订阅。

1.5.3.1.键值存储与 TTL
  • 在 ETCD 中,键值存储可以与 TTL(Time to Live)绑定。TTL 就是它的保活周期时长。
  • 那么,你一旦在写数据的时候,跟这个 TTL 绑定,它就会去确定说:“哎,多久没有续约了?”如果超过你设置的时间,比如你设置了 1 分钟,那如果 1 分钟它还没续约,你这个 key 就会消失。
1.5.3.2.消息发布与订阅的实现
  • 消息发布与订阅其实就是通过 Watch 机制实现的。在 ETCD 中,你可以通过 Watch 监听某个键值对的变化。当键值对发生变化时,ETCD 会通知订阅的客户端。
  • 比如,生产者可以将消息写入某个键值对,消费者通过 Watch 监听该键值对的变化。一旦有新消息写入,消费者会收到通知并处理数据。这其实就是我们上次所说的 Watch 机制。
1.5.3.3.是否可以用 ETCD 代替消息队列
  • 那么,是否可以用 ETCD 代替消息队列呢?
  • 其实,ETCD 的 Watch 机制可以用于实现简单的消息发布与订阅功能,但它的设计初衷并不是作为消息队列使用的。对于高吞吐量或复杂消息处理场景,建议使用专门的消息队列系统,比如 Kafka、RabbitMQ 等。
  • ETCD 更适合用于配置管理、服务注册与发现等场景,消息发布与订阅功能可以作为它的附加能力使用。

1.6.Etcd的安装

在这里插入图片描述

1.6.1.etcd的安装

  • 访问 etcdio 网站 下载 release 版本
  • 解压二进制包,将其放在本地目录下
  • 也可以参考官网的安装方法:https://etcd.io/docs/v3.5/install/

1.6.2.启动 etcd

  • 启动命令示例:
    etcd --listen-client-urls=http://localhost:2379 --advertise-client-urls=http://localhost:2379
    
  • 参数说明:
    • –listen-client-urls:客户端访问的 URL。
    • –advertise-client-urls:集群成员间通信的 URL。

1.6.3.docker安装方式

  • 我这里给出使用docker的安装方法
    • 这里使用sh脚本部署,vim docker-etcd.sh,然后把下面的内容写入文件
    • 注意,如果你安装了kubernetes(已经携带了etcd),这种方式跑不起来,端口会有冲突,可以把下面内容的所有 2379–>2479,2380–>2480
    • 我下面的就是更改为 2479、2480 的。如果执行后容器是关闭了,可以docker start一下,把容器跑起来
    ETCD_VER=v3.5.18
    
    rm -rf /tmp/etcd-data.tmp && mkdir -p /tmp/etcd-data.tmp && \
      docker rmi gcr.io/etcd-development/etcd:${ETCD_VER} || true && \
      docker run \
      -p 2479:2479 \
      -p 2480:2480 \
      --mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data \
      --name etcd-gcr-${ETCD_VER} \
      gcr.io/etcd-development/etcd:${ETCD_VER} \
      /usr/local/bin/etcd \
      --name s1 \
      --data-dir /etcd-data \
      --listen-client-urls http://0.0.0.0:2479 \
      --advertise-client-urls http://0.0.0.0:2479 \
      --listen-peer-urls http://0.0.0.0:2480 \
      --initial-advertise-peer-urls http://0.0.0.0:2480 \
      --initial-cluster s1=http://0.0.0.0:2480 \
      --initial-cluster-token tkn \
      --initial-cluster-state new \
      --log-level info \
      --logger zap \
      --log-outputs stderr
    
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcd --version
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcdctl version
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcdutl version
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcdctl endpoint health
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcdctl put foo bar
    docker exec etcd-gcr-${ETCD_VER} /usr/local/bin/etcdctl get foo
    
  • 不建议在生产环境中使用二进制部署,因为无法确保资源保证,使用容器管理可以更好地进行资源限制和质量隔离

1.7. etcd 的基本使用

在这里插入图片描述

  • etcdctl是用于与etcd键值存储系统进行交互的命令行工具,会把命令转成rest http请求转发到etcd
    • 在使用etcdctl时,指定endpoint是必需的,因为它告诉etcdctl要连接的etcd实例的位置
    • Etcd是一个分布式键值存储系统,可以在多个节点上运行。每个节点都有一个唯一的endpoint,用于标识它在网络上的位置。通过指定endpoint,etcdctl知道要连接到哪个etcd实例以执行命令
    • 因为我上面安装时将端口从2379改成了2479,所以下面我的endpoints都指定为 --endpoints=127.0.0.1:2479
  • 如果不指定endpoint,默认会连接2379,但是我们很多人的机器上都有kubernetes,默认2379给kubernetes的etcd用了,我们连不上的,所以做实验的时候还是都加上 --endpoints

1.7.1.使用 etcdctl 命令行工具

  • 设置环境变量:
    # etcdctl 存在两个主要的 API 版本:API v2 和 API v3。API v3 是较新的版本,引入了一些新的功能和改进
    export ETCDCTL_API=3
    
  • 常用命令:
    # 列出etcd集群的所有成员
    etcdctl --endpoints=127.0.0.1:2479 member list
    42ca3f1db112078d, started, vm-226-235-tencentos, https://xxx.xxx.xxx.xxx:2380, https://xxx.xxx.xxx.xxx:2379, false
    
    # 列出etcd集群的所有成员,以表格方式输出
    etcdctl --endpoints=127.0.0.1:2479 member list --write-out=table
    [root@VM-226-235-tencentos ~/zgy/yamls]# docker exec -it bd0feb758a0b etcdctl --endpoints=127.0.0.1:2479 member list --write-out=table
    +------------------+---------+------+---------------------+---------------------+------------+
    |        ID        | STATUS  | NAME |     PEER ADDRS      |    CLIENT ADDRS     | IS LEARNER |
    +------------------+---------+------+---------------------+---------------------+------------+
    | 9b0b0f15059fc529 | started |   s1 | http://0.0.0.0:2480 | http://0.0.0.0:2479 |      false |
    +------------------+---------+------+---------------------+---------------------+------------+
    
    # 写入键值对:
    etcdctl --endpoints=127.0.0.1:2479 put key value
    
    # 读取键值对:
    etcdctl --endpoints=127.0.0.1:2479 get key
    
    # 监听键值变化:
    etcdctl --endpoints=127.0.0.1:2479 watch key
    
    # 还可以监听某个前缀
    etcdctl --endpoints=127.0.0.1:2479 watch --prefix /api/v1/namespaces/default
    

1.7.2.键值对的规划

  • 键值对可以按照目录结构进行规划

    # 键值对可以按照目录结构进行规划
    sh-5.0# etcdctl --endpoints=127.0.0.1:2479 put /a/b value1
    sh-5.0# etcdctl --endpoints=127.0.0.1:2479 put /a/c value2
    
    # 通过前缀获取键值对:
    sh-5.0# etcdctl --endpoints=127.0.0.1:2479 get --prefix /a
    /a/b
    value1
    /a/c
    value2
    
  • Kubernetes正是利用了 目录结构的键值对规划,保证了资源存储的唯一性和正常存取,比如我们执行一下 kubectl get pods -n default -v9,可以看下default下pods的key设计:

    • 设计为:/api/v1/namespaces/default/pods
    • 则apiserver在查询default命名空间下pod的时候,只需要执行 get --prefix /api/v1/namespaces/default/pods 即可找到所有前缀为这个的pod信息
    [root@VM-226-235-tencentos ~/zgy/yamls]# kubectl get pods -n default -v9
    I0210 14:12:23.230538     766 loader.go:375] Config loaded from file:  /root/.kube/config
    ......
    I0210 14:12:23.267173     766 round_trippers.go:424] curl -k -v -XGET  -H "Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json" -H "User-Agent: kubectl/v1.19.16 (linux/amd64) kubernetes/e37e4ab" 'https://9.135.226.235:6443/api/v1/namespaces/default/pods?limit=500'
    I0210 14:12:23.269413     766 round_trippers.go:444] GET https://9.135.226.235:6443/api/v1/namespaces/default/pods?limit=500 200 OK in 2 milliseconds
    I0210 14:12:23.269443     766 round_trippers.go:450] Response Headers:
    
  • 比如查看一个svc在etcd中的存储内容

    • kubernetes资源在etcd中存储的都是protobuf的格式,所以直接查看是有些乱码的,不过不影响查看
    # 先查看系统中所有的key
    sh-5.0# etcdctl --endpoints 127.0.0.1:2379 get --prefix / --keys-only
    
    # 再获取某个key的值,比如这里获取 kube-system/kube-dns service 的存储内容
    sh-5.0# etcdctl --endpoints 127.0.0.1:2379 get /registry/services/specs/kube-system/kube-dns
    /registry/services/specs/kube-system/kube-dns
    k8s
    
    v1Service�
    �
    kube-dns
            kube-system"*$1aebd608-6aa9-499c-9d3a-9584df9e7cb42����Z
    k8s-apkube-dnsZ%
    kubernetes.io/cluster-servicetrueZ
    kubernetes.io/nameKubeDNSb
    prometheus.io/port9153b
    prometheus.io/scrapetruez�
    
    dnsUDP55(
    
    dns-tcpTCP55(
    
    metricsTCP�G�G(
    k8s-apkube-dns
    10.96.0.10"     ClusterIP:NoneBRZ`h
    "
    

1.7.3.高级操作

  • 仅获取键名:
    # 获取/前缀的所有key,一般使用--prefix的时候都要加--keys-only,否则对象多时同时把value输出就没法看了
    etcdctl --endpoints=127.0.0.1:2479 get --prefix / --keys-only
    
  • debug模式:类似kubectl的-v9,–debug会输出debug信息,包含环境变量、超时时间、endpoint等
    etcdctl --endpoints=127.0.0.1:2479 --debug get key
    # 获取/a的数据,并且启动debug模式,输出细节
    sh-5.0# etcdctl --debug get /a
    ETCDCTL_CACERT=ca.crt
    ETCDCTL_CERT=server.crt
    ETCDCTL_COMMAND_TIMEOUT=5s
    ETCDCTL_DEBUG=true
    ETCDCTL_DIAL_TIMEOUT=2s
    ETCDCTL_DISCOVERY_SRV=
    ETCDCTL_DISCOVERY_SRV_NAME=
    ETCDCTL_ENDPOINTS=[127.0.0.1:2379]
    ETCDCTL_HEX=false
    ETCDCTL_INSECURE_DISCOVERY=true
    ETCDCTL_INSECURE_SKIP_TLS_VERIFY=false
    ETCDCTL_INSECURE_TRANSPORT=true
    ETCDCTL_KEEPALIVE_TIME=2s
    ETCDCTL_KEEPALIVE_TIMEOUT=6s
    ETCDCTL_KEY=server.key
    ETCDCTL_PASSWORD=
    ETCDCTL_USER=
    ETCDCTL_WRITE_OUT=simple
    WARNING: 2025/02/10 02:18:39 Adjusting keepalive ping interval to minimum period of 10s
    WARNING: 2025/02/10 02:18:39 Adjusting keepalive ping interval to minimum period of 10s
    INFO: 2025/02/10 02:18:39 parsed scheme: "endpoint"
    INFO: 2025/02/10 02:18:39 ccResolverWrapper: sending new addresses to cc: [{127.0.0.1:2379  <nil> 0 <nil>}]
    

1.7.4.etcd的数据存储

  • ETCD 的数据默认存储在运行目录下的 etcd 目录中
  • 可以通过指定 --data-dir 来更改存储位置,如果不指定,默认会在运行目录下生成一个 etcd 目录
    在这里插入图片描述

1.7.5.etcd的安全认证

  • ETCD 使用双向 TLS 认证,不仅客户端验证服务器端,服务器端也验证客户端,客户端需要携带 certkeycacert 文件 访问服务器端
  • 这些文件在启动命令中已经指定,只需知道如何获取这些参数,当然有需要时也可以在命令中手动指定
    sh-5.0# etcdctl --endpoints 127.0.0.1:2479 --cacert=ca.crt --cert=server.crt --key=server.key put /a/b value1
    

1.7.6.如何查看k-v的细节

  • 使用-wjson,输出内容包括:集群信息、member信息、版本信息、kv信息等,其中kv是base64加密的
    etcdctl --endpoints 127.0.0.1:2479 get a -wjson
    
    [root@VM-226-235-tencentos ~/zgy/yamls]# docker exec -it bd0feb758a0b etcdctl --endpoints=127.0.0.1:2479 get /a -wjson
    {"header":{"cluster_id":703969991527618354,"member_id":11172039883585733929,"revision":2,"raft_term":3},"kvs":[{"key":"L2E=","create_revision":2,"mod_revision":2,"version":1,"value":"MQ=="}],"count":1}
    

1.8. etcd 的核心机制

在这里插入图片描述

1.8.1.过期时间 TTL(Time To Live)

  • 为键值对设置过期时间,默认单位是秒:
    etcdctl --endpoints 127.0.0.1:2379 put key value --lease=1234
    

1.8.2.乐观锁 CAS(Compare And Swap)

  • 条件写入:
    • 在写入键值对之前,先检查某个条件是否满足的操作。如果条件满足,执行成功请求;如果条件不满足,执行失败请求
    • etcdctl txn 命令执行条件写入操作,并通过 --interactive 参数启用交互模式
    # 进入写入操作
    etcdctl txn --interactive
    
    # 命令格式
    compare:
    value("key") = "old_value"
    
    success requests:
    put key new_value
    
    failure requests:
    put key fail_value
    
    # 使用示例:每输入一行,要进行回车进入下一条命令
    sh-5.0# etcdctl txn --interactive
    compares:
    value("/a/b") = "value1"
    
    success requests (get, put, del):
    put /a/b value-new
    
    failure requests (get, put, del):
    put /a/b value1
    
    SUCCESS
    
    OK
    # 查看CAS更新结果
    sh-5.0# etcdctl get /a/b
    /a/b
    value-new
    
  • etcd使用CAS机制目的就是避免锁的引入,不过etcd还是有锁的,它支持类似于数据库的事务,如果你有多条写操作,可以加起一个transaction,最后一起commit

2.Raft 协议

  • 学习网站:
    • http://thesecretlivesofdata.com/raft
    • https://raft.github.io/

2.1 CAP 原则

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition Tolerance)

一致性和可用性一定要有个取舍,强一致代表效率一定不会很高,弱一致时一般都会有数据同步等问题

2.2 Raft 确保一致性的核心:quorum多数派机制

  • quorum 多数派机制:即当同意当前操作的节点数据 超过半数时,才允许生效,否则不生效
  • Raft确保分布式一致性,其实主要做两件事:
    • Leader Election:leader选举
    • Log Replication:日志复制
  • 这两个过程都可以在这个网站看到动态图
    • http://thesecretlivesofdata.com/raft

2.3.Raft 协议的 Leader Election(leader选举)

2.3.1.节点的3种身份

  • 当使用 Raft 一致性算法时,节点可以扮演三种不同的角色:Leader(领导者)、Follower(跟随者)和Candidate(候选人)。这些角色在 Raft 协议中起着不同的作用和责任。
    • Leader(领导者)
      • Leader 是 Raft 群集中的一种特殊节点角色
      • Leader 负责处理客户端的请求,并将日志条目复制到其他节点(跟随者)。
      • Leader 通过发送心跳消息来维持其领导地位,并防止其他节点成为 Leader。
    • Follower(跟随者)
      • Follower 是 Raft 群集中的普通节点角色
      • Follower 跟随 Leader 的指导,并接受 Leader 发送的日志条目
      • Follower 可以投票给候选人,以帮助选举新的 Leader
    • Candidate(候选人)
      • Candidate 是 Raft 群集中的临时节点角色。
      • 当一个节点超过一定时间(每个节点都维护了一个选举超时时间Election Timeout)没有收到leader的心跳时,将会成为候选人,开始一次新的选举过程,试图成为新的 Leader。
      • 候选人会向其他节点发送选举请求,并等待其他节点的投票。
      • 如果候选人获得了大多数节点的投票,它将成为新的 Leader。
  • Raft 协议通过定期的选举过程来维护 Leader 的稳定性。当 Leader 失败或无法与其他节点通信时,会触发新的选举过程,以选择一个新的 Leader。在选举过程中,节点将转换为候选人角色,并尝试获得其他节点的投票
  • Leader-Follower-Candidate 的角色切换机制确保了 Raft 群集的一致性和可用性
    • Leader 负责处理客户端请求,Follower 跟随 Leader 的指导,而 Candidate 则参与选举过程以确保 Leader 的连续性。

2.3.2.选举过程

  1. 初始状态:

    • 假设我们有一个 Raft 集群,包含 5 个节点:A、B、C、D 和 E。
    • 初始状态下,所有节点都是 Follower。
  2. 选举超时(Election Timeout):

    • 每个 Follower 节点都有一个选举超时计时器。
    • 当选举超时计时器到期时,节点将转变为候选人(Candidate)角色,并开始新的选举过程。
  3. 候选人状态:

    • 节点 A 的选举超时计时器到期,它成为候选人,给自己投一票,同时向其他节点发送选举请求(Request Vote)。
    • 其他节点(B、C、D 和 E)收到选举请求后,会检查自己的状态并决定是否投票给候选人。
  4. 投票过程:

    • 节点 B、C 和 D 向候选人 A 投票,因为它们还没有投票给其他候选人。
    • 节点 E 由于已经投票给了另一个候选人,所以不会再投票给候选人 A。
  5. 选举结果:

    • 如果候选人 A 获得了大多数节点的选票(超过半数),它将成为新的 Leader。
    • 假设节点 B、C 和 D 都投票给了候选人 A,那么 A 将成为新的 Leader。
  6. Leader 宣布:

    • 新的 Leader A 会使用心跳机制 宣布自己的地位,并开始处理客户端的请求。
    • 其他节点(B、C、D 和 E)将转变为 Follower 角色,并跟随新的 Leader。
  • 选举中的一些注意点:
    • 只要一个节点没有leader,且有Candidate向他请求投票,它就一定会投 赞成
    • 当一个节点投了票之后,就会重置自己的超时时间,保证不会在投票后自行又发起选举

2.3.3.选举问题一:选票冲突(Vote Conflict)

  • 当多个候选人同时发起选举时,可能会导致选票冲突。
  • 解决方法:
  • 集群初始化时,默认每个节点都是Follower,此时可以为每个节点初始化 随机的选举超时时间,比如150-300毫秒之间随机值,保证节点不会同时发起选举
  • 集群运行中,如果出现多个Candidate同时选举,Raft 使用递增的任期号(Term)来解决选票冲突。节点会比较候选人的任期号,并投票给任期号更大的候选人。如果发生脑裂也会这样保证老leader会退位让贤
    在这里插入图片描述

2.3.4.选举问题二:无法达到多数票(Majority Votes)

  • 在选举过程中,候选人需要获得多数节点的选票才能成为新的 Leader。
  • 解决方法:
    • 如果没有候选人获得多数票,选举将失败。此时,节点可以增加选举超时时间,并重新发起选举过程,直到有候选人获得多数票。
    • 另外,raft集群一般都要求 奇数个节点,保证投票时不会发生平票

2.3.5.选举问题三:网络分区(Network Partition)导致脑裂

  • 当 Raft 群集中的节点由于网络故障或分区而无法通信时,可能会导致选举过程中断或发生脑裂
  • Raft 使用心跳机制来维持 Leader 的地位,发生网络分区时,每个分区都将产生一个leader,称为脑裂。脑裂是一个常见的分布式系统中的挑战
  • 解决方法:
    • raft集群一般都要求 奇数个节点,比如5个节点ABCDE,其中A为leader,发生分区后产生两个分区:AB、CDE
    • 此时CDE会选举出新的leader,比如C,则此时有两个leader A、C 都在接收客户端请求
    • 当有请求进入A时,A写入本地log,然后复制到B,B返回ack,但是只有一票成功,无法达到大多数,所以到达A的请求无法commit,也就无法生效
    • 当有请求进入C时,C写入本地log,然后复制到DE,DE都返回ack,2票成功,更改commited生效
    • 当网络分区恢复后,A认为自己是leader,向C发心跳,C看到自己的Term更大就会无视A。而C向A发心跳,A发现自己的Term小,就会主动成为C的follower,AB回滚自己未commit的更改,重新比较C的log,同步最新数据,从而解决网络分区带来的脑裂
      在这里插入图片描述
  • 但如果一个网络分区,无法达到多数票,就无法选出leader

4. 选举过程过长(Election Process Takes Too Long)

  • 在某些情况下,选举过程可能需要较长时间才能完成,导致系统的可用性下降。
  • 解决方法:可以调整选举超时时间和心跳间隔,以减少选举过程的时间。另外,使用预投票(Pre-vote)机制可以更快地检测到网络分区并进行选举。

5. 恢复过程中的数据一致性(Data Consistency during Recovery)

  • 当 Leader 失败或重新选举时,可能会导致数据一致性问题。
  • 解决方法:Raft 使用日志复制机制来确保数据一致性。当新的 Leader 当选后,它会将自己的日志条目复制到其他节点,以恢复数据一致性。

2.4.Raft 协议的 Log Replication(日志复制)

在这里插入图片描述

Raft对数据的变更操作,都是通过 日志复制 + 多数派机制 来完成的

  • 每个节点都包括:一致性模块、日志模块、状态机

2.4.1.日志复制原理

  1. Leader 推送日志:

    • Leader 接收客户端的请求,并将请求转化为日志条目(Log Entry)。
    • Leader 将日志条目追加到自己的日志中,并并行地将日志条目发送给其他节点(Followers)。
      • 发给其他节点时,是通过下一次的心跳携带数据过去的
  2. 日志复制过程:

    • Followers 接收到 Leader 发送的日志条目后,将其追加到自己的日志中。
    • Followers 向 Leader 发送确认消息(Append Entries Response)表示已成功复制日志条目。
  3. 多数派确认:

    • Leader 在收到多数派节点的确认消息后,将该日志条目标记为已提交(Committed)。
    • Leader 会通知 Followers 将已提交的日志条目应用到状态机,以实现数据一致性。
  4. 容错性:

    • 如果 Leader 失败,新的 Leader 将被选举出来,并继续进行日志复制过程。
    • 如果 Followers 失败或无法通信,Leader 会重试发送日志条目,直到多数派节点确认复制成功。

2.4.2.日志复制过程描述

  • 假设我们有一个 Raft 集群,包含 Leader(L)和两个 Followers(F1、F2)。

    1. Leader 接收客户端请求,生成日志条目:Log Entry 1

    2. Leader 将 Log Entry 1 追加到自己的日志中,并并行发送给 Followers。

    3. F1 和 F2 接收到 Log Entry 1,将其追加到各自的日志中,并发送确认消息给 Leader。

    4. Leader 收到来自 F1 和 F2 的确认消息,确认 Log Entry 1 已成功复制。

    5. Leader 将 Log Entry 1 标记为已提交,并通知 Followers 将其应用到状态机。

    6. F1 和 F2 将 Log Entry 1 应用到各自的状态机,实现数据一致性。

2.5.Raft协议中的两个超时时间

  • --heartbeat-interval:心跳间隔,leader发送心跳的周期,xxxx ms 向follower发送一次心跳

  • --election-timeout:选举超时,follower能容忍多久没收到心跳的时间,超过后follower就会造反转变成候选人身份,发起新的选举

  • 可以在etcd的命令行参数中配置

2.6.节点新角色:Learner 解决增加集群节点问题

在这里插入图片描述

  • Raft 协议传统角色构成:Leader、Follower、Candidate,此时想要向 Raft 集群中新增一个节点,存在问题:

    • 新节点数据为空,Leader 需要大量数据复制
    • 数据同步可能会占用大量网络带宽,导致leader发送心跳失败,进而触发选举重新投票
    • 而新节点数据太落后,此时触发选举 可能会产生无效投票,进而影响集群的稳定性
  • 为解决上述问题,引入了Learner角色,特性:仅接收数据,不参与投票

  • Learner 引入时间

    • Learner的概念最早在 ETCD 3.4 版本 中实现(2019 年)
    • 后来逐渐被其他 Raft 实现(如 TiKV、Consul 等)采纳,在 Raft 4.2.1引入
    • 因此Learner 是 Raft 协议的扩展功能,并非原始 Raft 论文的一部分,是 Raft 协议在实际工程应用中的一种优化和改进
  • 引入Learner后,增加新节点的优势:

    • Learner不参与投票,所以票数quorum没有变,原健康节点可快速达成共识
    • 避免新节点因数据差异导致无效投票
    • 保持集群稳定性
    • Learner节点支持异步数据同步,同步完成后将自动转为Follower,参与后续投票

2.7.强一致性和弱一致性

  • 弱一致性
    • 主节点 commit 后,会立刻返回客户端确认,不会等待从节点也commit完成。
    • 缺点:数据安全性较低,主节点故障时,未 commit 的数据可能丢
    • 优点:效率高
    • 适用于对性能要求较高,且可以容忍少量数据丢失的场景,如日志系统
  • 强一致性
    • 必须所有 follower 二次确认 commit 后,主节点才返回客户端确认。
    • 缺点:效率较低
    • 优点:数据安全性高
    • 适用于对数据一致性要求极高的场景,如金融系统
  • 配置参数
    • ETCD 启动时可配置强一致性或弱一致性,--quorum-read 参数,默认false弱一致性
    • 默认一般为弱一致性,因为大多数场景下够用了,如果你的场景里对数据安全性要求非常高,可以配置为强一致性。

2.8.Raft的安全性机制详解

Raft 协议通过选举安全性Leader 完整性等机制,确保集群中所有节点执行相同的操作序列,避免数据不一致。以下是详细解释:

2.8.1. 选举安全性(Election Safety)

  • 定义:在同一个任期内(Term),只能选举出一个 Leader

  • 作用:避免多个 Leader 同时存在,导致集群分裂和数据不一致。

  • 实现方式

    • 每个节点在同一任期内只能投一次票(给一个 Candidate)。
    • 只有获得多数票的 Candidate 才能成为 Leader。
    • 如果选举超时,会进入新的任期,重新发起选举。
  • 举例

    • 假设集群有 5 个节点,Term 为 1。
    • 如果有两个 Candidate 同时发起选举,最多只有一个 Candidate 能获得至少 3 票(多数票),成为 Leader。

2.8.2.Leader 完整性(Leader Completeness)

  • 定义:如果一个日志条目在某个任期被提交(Committed),那么后续任期的 Leader 必须包含这个日志条目
  • 作用:确保已经被提交的日志不会被覆盖或丢失。
  • 实现方式
    • Raft 通过选举限制来保证 Leader 完整性:
      • 只有日志足够新的节点(即包含更多已提交日志的节点)才能成为 Leader。
      • 在选举时,Candidate 会携带自己的日志信息(Term 和 Index)。
      • 其他节点会比较 Candidate 的日志是否比自己更新
        • 如果 Candidate 的 Term 更大,或者 Term 相同但 Index 更大,则投票给它。
        • 否则,拒绝投票。
  • 举例
    • 假设 Term 1 的 Leader 提交了一条日志(Log A)。
    • 如果 Term 1 的 Leader 崩溃,Term 2 的选举中,只有那些包含 Log A 的节点才有可能成为 Leader。
    • 这样,Log A 就不会丢失,新 Leader 会继续复制 Log A 给其他节点。

2.8.3. 为什么需要这些机制?

  • 问题场景
    • 假设某个 Follower 在 Leader 提交日志时变得不可用。
    • 稍后,这个 Follower 重新加入集群,并被选举为 Leader。
    • 如果这个新 Leader 的日志不完整,它可能会用新的日志覆盖已经提交的日志,导致数据不一致。
  • 解决方案
    • Raft 通过选举安全性Leader 完整性,确保只有日志足够新的节点才能成为 Leader。
    • 这样,新 Leader 一定包含所有已提交的日志,避免了数据丢失或不一致。

2.8.4. 总结

  • 选举安全性:确保同一任期内只有一个 Leader,避免脑裂。
  • Leader 完整性:确保新 Leader 包含所有已提交的日志,避免数据丢失。
  • 核心思想:通过日志比较和多数投票机制,Raft 保证了集群的一致性和安全性。

2.9.Etcd基于Raft协议实现一致性

2.9.1.Etcd 的 leader选举

在这里插入图片描述

2.9.2.Etcd 的 日志复制(默认弱一致性)

在这里插入图片描述

2.9.3.安全性

在这里插入图片描述

2.9.4.Etcd 的 失效处理

在这里插入图片描述

2.9.5.Etcd 的 wal log日志

在这里插入图片描述

  • Raft任何请求的数据都会先写到本地 wal log日志里,然后同步给follower
  • Etcd wal log 的type早期版本主要有两种,现已扩展到五种
    • EntryNormal:表示普通的数据变更操作。这是最常见的类型,用于记录键值对的插入、更新和删除等操作。
    • EntryConfChange:表示配置变更操作。这种类型的日志记录用于记录集群配置的变更,例如添加或删除节点、更改节点角色等。
    • EntryAddNode:表示添加节点操作。这种类型的日志记录用于记录添加新节点到集群的操作。
    • EntryRemoveNode:表示移除节点操作。这种类型的日志记录用于记录从集群中移除节点的操作。
    • EntryBarrier:表示屏障操作。这种类型的日志记录用于实现线性一致性,确保在屏障操作之前的所有操作都已提交。
  • 使用 etcd-prologue 工具查看 wal log 信息,该工具可以将二进制格式的 wal log 转换为文本格式,便于分析和调试。
  • wal是日志记录技术,在etcd中日志的格式为vlog

2.9.6.Etcd对存储的具体实现

在这里插入图片描述
在这里插入图片描述

  • Etcd对存储的具体实现,可以分为4部分:
    • KV store
      • 包含 KV index,基于 betree 数据结构,在内存中对所有的key做了一个索引,用于加速查询
      • 用于数据索引,提高读写性能。
    • boltDB
      • 数据最终存储到backend,目前backend其实用的是另外一个数据库 boltDB。
      • boltDB 是谷歌开发的一个轻量级的键值存储数据库,适用于小型数据集。在设计里也可以使用其他db,目前用的是boltDB
    • watchable store
      • 提供监听机制,监听数据变化。
      • 通过监听机制,客户端可以实时获取数据变更通知。
    • lease
      • 控制数据的 TTL(生命周期),到期自动删除。
      • 通过 TTL 机制,可以实现数据的自动清理和过期管理。

3.etcd常见问题

3.1.etcdctl 执行报错

  • 进入kubernetes携带安装的etcd中,执行etcdctl命令,可能会报错:
    [root@VM-226-235-tencentos ~]# kubectl exec -it etcd-vm-226-235-tencentos -n kube-system -- /bin/sh
    sh-5.0#
    sh-5.0# etcdctl get --prefix / --keys-only
    {"level":"warn","ts":"2025-02-10T02:05:21.349Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-cd94a73a-3967-450f-83f0-ec2744418038/127.0.0.1:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: all SubConns are in TransientFailure, latest connection error: connection closed"}
    Error: context deadline exceeded
    
  • github issues:https://github.com/etcd-io/etcd/issues/12234
  • 这种是证书没有读取成功,可以手动指定证书来执行etcdctl命令
    # 先ps -ef查看你的etcd启动命令
    [root@VM-226-235-tencentos ~]# ps -ef | grep etcd
    root     24115 24089  2  2024 ?        7-15:58:52 etcd --advertise-client-urls=https://xxx.xxx.xxx.xxx:2379 --cert-file=/etc/kubernetes/pki/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/etcd --initial-advertise-peer-urls=https://xxx.xxx.xxx.xxx:2380 --initial-cluster=vm-226-235-tencentos=https://xxx.xxx.xxx.xxx:2380 --key-file=/etc/kubernetes/pki/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://xxx.xxx.xxx.xxx:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://xxx.xxx.xxx.xxx:2380 --name=vm-226-235-tencentos --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/etc/kubernetes/pki/etcd/peer.key --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    
    # 从启动参数中获取到证书位置,写在etcdctl的命令中
    [root@VM-226-235-tencentos ~]# etcdctl --endpoints 127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key put /a/b value1
    OK
    

3.2.etcd和consul如何选择

  • consul主要应用场景:服务发现和负载均衡。因此如果你有服务发现的需求,还是优先推荐使用consul,它是专业的
  • etcd:也可以做服务发现,且抱着kubernetes的大腿,如果再云原生有一些小的场景需求,使用etcd就足够了,不需要再引入其他组件了,还有学习成本
    • 比如spring cloud现在支持native的service discovery,其实就是查询ETCD的

3.3.ETCD 和 Zookeeper 的区别

  • zookeeper:使用 Paxis 协议,复杂度较高,性能和一致性维护上存在一些问题。
  • etcd:使用Raft协议,是对 Paxos 协议的简化和增强

3.4.ETCD 的一些其他知识点

3.4.1.数据持久化

  • 数据存储位置
    • ETCD 的数据存储在 /var/lib/etcd 目录下。
    • 该目录通过 volume 挂载到 ETCD Pod 中。
  • 数据持久化
    • 即使 Pod 重启或主机重启,只要 /var/lib/etcd 目录未被删除,数据不会丢失。
    • 建议将数据存储在主机目录或独立的 SSD 盘上,而不是容器内的 Overlay FS

3.4.2.ETCD 部署模式

  • 独立部署 vs. Pod 部署
    • 独立部署:ETCD 集群与 Kubernetes 控制平面分离,适用于大规模集群。
    • Pod 部署:ETCD 与 Kubernetes 控制平面部署在同一节点,适用于小型集群。
  • 读请求优化
    • ETCD 的读请求不一定要经过 leader,每个 member 都可以处理读请求。
    • 如果 ETCD 和 API Server 部署在同一节点,读请求的效率会更高。

3.4.3.ETCD 扩容

  • 扩容操作
    • 通过 etcdctladd-member 命令添加新 member。
    • 扩容时需确保集群处于健康状态。

3.4.4.ETCD 高可用性

  • 3 个 ETCD member 挂掉 1 个
    • 集群仍可正常工作,因为剩余 2 个 member 可以满足多数投票条件。
    • 如果挂掉 2 个 member,集群将无法正常工作。

3.4.5.ETCD 的亲和性

  • 亲和性
    • ETCD 和 API Server 之间具有亲和性,因为 API Server 是 ETCD 的主要调用方。
    • 在调度系统中,亲和性可以提高服务的稳定性和性能。
  • 反亲和性
    • 反亲和性是指将服务的多个实例部署到不同节点,以提高容错性。

3.4.6.ETCD 数据恢复

  • 数据恢复
    • 只要有集群的备份数据,即使 3 个 master 节点同时宕机,也可以通过备份恢复集群。
    • 使用 etcdctl snapshot 命令进行数据备份和恢复。

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

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

相关文章

外贸网站源码 助力企业抢占蛇年市场先机!

在竞争激烈的外贸市场中&#xff0c;蛇年无疑是企业寻求突破与增长的关键一年。外贸网站源码为企业提供了快速搭建专业外贸网站的解决方案&#xff0c;助力企业在新的一年抢占市场先机。 快速上线 时间就是商机&#xff0c;尤其是在蛇年这样充满变数和机遇的年份。外贸网站源码…

verilog练习:i2c slave 模块设计

文章目录 前言1.结构2.代码2.1 iic_slave.v2.2 sync.v2.3 wr_fsm.v2.3.1 状态机状态解释 2.4 ram.v 3. 波形展示4. 建议5. 资料总结 前言 首先就不啰嗦iic协议了&#xff0c;网上有不少资料都是叙述此协议的。 下面将是我本次设计的一些局部设计汇总&#xff0c;如果对读者有…

项目6:基于大数据校园一卡通数据分析和可视化

1、项目简介 本项目是基于大数据的清华校园卡数据分析系统&#xff0c;通过Hadoop&#xff0c;spark等技术处理校园卡交易、卡号和商户信息数据。系统实现消费类别、男女消费差异、学院消费排行和年级对比等分析&#xff0c;并通过Web后端和可视化前端展示结果。项目运行便捷&…

Datawhale 组队学习 Ollama教程 task1

一、Ollama 简介 比喻&#xff1a;Ollama 就像是一个“魔法箱子”&#xff0c;里面装满了各种大型语言模型&#xff08;LLM&#xff09;。你不需要懂复杂的魔法咒语&#xff08;配置&#xff09;&#xff0c;只需要轻轻一按&#xff08;一条命令&#xff09;&#xff0c;就能让…

大模型基本原理(二)——ChatGPT的工作原理

如何得到一个ChatGPT&#xff1f; 1、无监督预训练&#xff1a;通过大量的文本数据集进行无监督训练&#xff0c;得到一个基座模型&#xff08;只会续写文本&#xff09; 2、监督微调&#xff1a;通过一些人类撰写的高质量对话数据对基座模型进行监督微调&#xff0c;得到一个…

成为高能量体质:从身体神庙到精神圣殿的修炼之路

清晨五点&#xff0c;当城市还在沉睡&#xff0c;瑜伽垫上的汗水已经折射出第一缕阳光。这不是苦行僧的自虐&#xff0c;而是高能量体质者的日常仪式。在这个能量稀缺的时代&#xff0c;如何把自己修炼成一座小型核电站&#xff1f;答案就藏在身体的每个细胞里。 一、能量管理…

51c自动驾驶~合集50

我自己的原文哦~ https://blog.51cto.com/whaosoft/13280022 #VLA 主流方案全解析 旨在让智能体在物理世界中通过感知、决策和行动来实现目标&#xff0c;而视觉 - 语言 - 动作&#xff08;VLA&#xff09;模型作为其中的关键技术&#xff0c;近年来备受关注。VLA 模型能够…

论文阅读:MGMAE : Motion Guided Masking for Video Masked Autoencoding

MGMAE:Motion Guided Masking for Video Masked Autoencoding Abstract 掩蔽自编码&#xff08;Masked Autoencoding&#xff09;在自监督视频表示学习中展现了出色的表现。时间冗余导致了VideoMAE中高掩蔽比率和定制的掩蔽策略。本文旨在通过引入运动引导掩蔽策略&#xff0…

【嵌入式Linux应用开发基础】文件I/O基础编程

目录 一、文件I/O简介 二、文件描述符 2.1. 唯一性 2.2. 抽象性 2.3. 有限性 三、文件操作函数 四、标准文件I/O函数 五、文件执行权限 5.1. 权限类型 5.2. 权限分配对象 5.3. 权限表示方法 5.4. 权限设置命令 5.5. 权限设置的重要性 5.6. 实例说明 六、设备文件…

【JS】实现一个hexo插件并发布

hexo插件生成 在你的 hexo blog 目录&#xff0c;找到 node_modules. 新建一个文件夹。然后执行 npm init npm 会引导你生成 package.json 这是你的包的描述文件。需要注意的是&#xff0c;所有的 hexo 插件必须以 hexo - 开头&#xff0c;否则 hexo 不会加载。 如果hexo g中没…

【Java 面试 八股文】MySQL 篇

MySQL 篇 1. MySQL中&#xff0c;如何定位慢查询&#xff1f;2. 那这个SQL语句执行很慢&#xff0c;如何分析呢&#xff1f;3. 了解过索引吗&#xff1f;&#xff08;什么是索引&#xff09;4. 索引的底层数据结构了解过吗&#xff1f;5. B树和B树的区别是什么呢&#xff1f;6.…

vue中使用高德地图自定义掩膜背景结合threejs

技术架构 vue3高德地图2.0threejs 代码步骤 这里我们就用合肥市为主要的地区&#xff0c;将其他地区扣除&#xff0c;首先使用高德的webapi的DistrictSearch功能&#xff0c;使用该功能之前记得检查一下初始化的时候是否添加到plugins中&#xff0c;然后搜索合肥市的行政数据…

Python----PyQt开发(PyQt高级:图像显示,定时器,进度条)

一、图像显示 1.1、增加图标 1.直接创建setWindowIcon(QIcon(灯泡.jpg)) import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton from PyQt5.QtGui import QIconclass MainWindow(QMainWindow):def __init__(self):super(MainWindow, self).__init_…

Tomcat添加到Windows系统服务中,服务名称带空格

要将Tomcat添加到Windows系统服务中&#xff0c;可以通过Tomcat安装目录中“\bin\service.bat”来完成&#xff0c;如果目录中没有service.bat&#xff0c;则需要使用其它方法。 打到CMD命令行窗口&#xff0c;通过cd命令跳转到Tomcat安装目录的“\bin\”目录&#xff0c;然后执…

2025.2.10 每日学习记录3:技术报告只差相关工作+补实验

0.近期主任务线 1.完成小论文准备 目标是3月份完成实验点1的全部实验和论文。 2.准备教资笔试 打算留个十多天左右&#xff0c;一次性备考笔试的三个科目 1.实习申请技术准备&#xff1a;微调、Agent、RAG 据央视财经&#xff0c;数据显示&#xff0c;截至2024年12月…

微生物学术语和定义 | 微生物学词汇表

​ 微生物学作为一门研究微生物及其与环境、宿主和其他生物相互作用的科学&#xff0c;涵盖了广泛的学科领域和专业术语。然而&#xff0c;由于微生物学的快速发展和跨学科融合&#xff0c;许多术语的定义和使用在不同领域中可能存在差异甚至混淆。 随着新冠疫情的全球蔓延&am…

Java集合List详解(带脑图)

允许重复元素&#xff0c;有序。常见的实现类有 ArrayList、LinkedList、Vector。 ArrayList ArrayList 是在 Java 编程中常用的集合类之一&#xff0c;它提供了便捷的数组操作&#xff0c;并在动态性、灵活性和性能方面取得了平衡。如果需要频繁在中间插入和删除元素&#xf…

分层解耦-ioc引入

内聚: 软件中各个功能模块内部的功能联系。 耦合: 衡量软件中各个层/模块之间的依赖、关联的程度。 软件设计原则: 高内聚低耦合。

如何利用DeepSeek开源模型打造OA系统专属AI助手

利用DeepSeek开源模型打造OA系统专属AI助手&#xff0c;可以显著提升办公效率&#xff0c;增强信息检索和管理能力。 注册与登录DeepSeek平台 访问DeepSeek官网 访问DeepSeek的官方网站DeepSeek。使用电子邮件或手机号码注册账号并登录。 获取API Key 登录DeepSeek平台&am…

uni getLocation 公众号h5获取定位坐标没有返回

先看代码 //获取经纬度getLocation() {console.log("111")uni.getLocation({type: wgs84,success: function (res) {console.log(当前位置的经度&#xff1a; res.longitude);console.log(当前位置的纬度&#xff1a; res.latitude);},fail: function(err) {conso…