当前位置: 首页 > news >正文

ETCD详解

一、etcd概念

ETCD 是一个高可用的分布式键值key-value数据库,可用于服务发现。

ETCD 采用raft 一致性算法,基于 Go语言实现。

etcd作为一个高可用键值存储系统,天生就是为集群化而设计的。由于Raft算法在做决策时需要多数节点的投票,所以etcd一般部署集群推荐奇数个节点,推荐的数量为3、5或者7个节点构成一个集群。

二、etcd应用场景

  • 配置管理中心

  • 服务注册发现

用户可以在 etcd 中注册服务,并且对注册的服务配置 key TTL,定时保持服务的心跳以达到监控健康状态的效果

  • 消息订阅发布

这类场景的使用方式通常是:

  • 应用在启动的时候主动从etcd获取一次配置信息。

  • 同时,在etcd节点上注册一个Watcher并等待

  • 以后每次配置有更新的时候,etcd都会实时通知订阅者,以此达到获取最新配置信息的目的

  • 选主、应用调度

  • 分布式队列

  • 分布式锁 ​

三、etcd核心API

  • KV 服务,创建,更新,获取和删除键值对

  • 监视,监视键的更改。

  • 租约,消耗客户端保持活动消息的基元

  • 锁,etcd 提供分布式共享锁的支持,通过lock ttl更新锁的租约时长让锁保活

  • 选举,暴露客户端选举机制

四、etcd架构

HTTP Server:接受客户端发出的 API 请求以及其它 etcd 节点的同步与心跳信息请求。

Store:kv数据的存储引擎,v3支持不同的后端存储,当前采用boltdb。通过boltdb支持事务操作。用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是 etcd 对用户提供的大多数 API 功能的具体实现。

Raft:强一致性算法的具体实现,是 etcd 的核心算法。

WAL(Write Ahead Log,预写式日志):是 etcd 的数据存储方式,etcd 会在内存中储存所有数据的状态以及节点的索引,此外,etcd 还会通过 WAL 进行持久化存储。WAL 中,所有的数据提交前都会事先记录日志。

Snapshot 是为了防止数据过多而进行的状态快照

Entry 表示存储的具体日志内容。

通常,一个用户的请求发送过来,会经由 HTTP Server 转发给 Store 进行具体的事务处理,如果涉及到节点数据的修改,则交给 Raft 模块进行状态的变更、日志的记录,然后再同步给别的 etcd 节点以确认数据提交,最后进行数据的提交,再次同步。

五、etcd详细架构及数据流:

从大体上可以将其划分为以下4个模块

- http:负责对外提供http访问接口和http client

- raft 状态机:根据接受的raft消息进行状态转移,调用各状态下的动作。

- wal 日志存储:持久化存储日志条目。

- kv数据存储:kv数据的存储引擎,v3支持不同的后端存储,当前采用boltdb。通过boltdb支持事务操作。

写入数据流程:

  1. 客户端向Etcd集群发送写入请求,请求中包含要写入的K-V数据。

  2. Etcd集群的任意一个节点接收到请求后,将其转发给Leader节点。

  3. Leader节点根据配置决定是否需要生成WAL Log预写式日志。如果需要,它会向所有的Follower节点发送预写式日志条目(WAL Log Entry),并等待超过半数的Follower节点返回接收日志条目成功的信息。

  4. 如果预写式日志生成成功,Leader节点会将K-V数据写入到内存中的PageCache中,并通知客户端写入成功。

  5. 如果配置为"沃尔夫法则"(W+L=N)且N大于一半的节点,在超过半数的Follower节点返回接收Entry日志条目成功的信息后,Etcd会将K-V数据写入到磁盘中的Blot DB中。

  6. 写入磁盘中的Blot DB后,Etcd会生成一个唯一的Snapshot快照,并保存在指定的存储路径中。

需要注意的是,Etcd的写入流程是异步的,也就是说,写入请求的响应不是立即返回的。Etcd会使用后台线程或者异步操作来完成K-V数据写入磁盘和Snapshot快照的生成。这样可以提高系统的吞吐量和性能。

六、etcd部分组件详解

1. Store

ETCD的store是ETCD中的核心组件之一,负责管理键值对数据的存储和访问,并提供API接口供客户端进行读写操作。Store通过基于Raft算法的分布式一致性协议,确保配置信息的正确性和一致性。

Store的主要作用包括:

  1. 数据存储:Store负责存储键值对数据,可以将数据存储在内存中或者持久化到磁盘上。为了保证数据的正确性和一致性,Store会根据Raft协议进行数据同步和一致性检查。

  2. 键值查询:Store提供API接口,客户端可以通过这些接口对键值对进行查询操作。Store会根据客户端提供的键进行查询,并返回对应的值。

  3. 事件通知:当配置信息发生变化时,Store会通过事件通知机制及时通知相关的客户端进行更新。客户端可以通过订阅事件来接收配置信息的变更通知。

  4. 节点状态变更:Store还负责管理ETCD集群中节点的状态变更,包括节点的加入和离开。当有节点加入或离开时,Store会更新相应的节点状态信息,并通知其他相关的客户端。

  5. 监控与反馈:Store还提供一些监控和反馈功能,可以收集ETCD集群的运行状态和性能指标,并进行统计和报告。这些信息可以帮助管理员及时发现和解决问题。

2. WAL

etcd 的数据存储分为两个部分:

内存存储:内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。

持久化(硬盘)存储:持久化则使用 WAL(Write Ahead Log,预写式日志)进行记录存储。

WAL 日志是二进制的,解析出来后是以上数据结构 LogEntry。其中:

  1. 第一个字段 type,只有两种:

  2. 0 表示 Normal

  3. 1 表示 ConfChange,ConfChange 表示 etcd 本身的配置变更同步,比如有新的节点加入等。

  4. 第二个字段是 term,每个 term 代表一个 Leader 的任期,每次 Leader 变更 term 就会变化。

  5. 第三个字段是 index,这个序号是严格有序递增的,代表变更序号。

  6. 第四个字段是二进制的 data,将 Raft Request 对象的 pb 结构整个保存下。

etcd 源码下有个 tools/etcd-dump-logs 脚本工具,可以将 WAL 日志 dump 成文本查看,可以协助分析 Raft 协议。

Raft 协议本身不关心应用数据,也就是 data 中的部分,一致性都通过同步 WAL 日志来实现,每个 Node 将从 Leader 收到的 data apply 到本地的存储,Raft 只关心日志的同步状态,如果本地存储实现的有 Bug,比如没有正确的将 data apply 到本地,也可能会导致数据不一致。

在 WAL 的体系中,所有的数据在提交之前都会进行日志记录。在 etcd 的持久化存储目录中,有两个子目录:

一个是 WAL:存储着所有事务的变化记录;

另一个是 Snapshot:存储着某一个时刻 etcd 所有目录的数据。

通过 WAL 和 Snapshot 相结合的方式,etcd 可以有效的进行数据存储和节点故障恢复等操作。

为什么需要 Snapshot(快照)?

因为随着使用量的增加,WAL 存储的数据会暴增,为了防止磁盘很快就爆满,etcd 默认每 10000 条记录做一次 Snapshot,经过 Snapshot 以后的 WAL 文件就可以删除。所以,通过 API 可以查询的操作历史记录默认为 1000 条。

首次启动时,etcd 会把启动的配置信息存储到 data-dir 配置项指定的目录路径下。配置信息包括 Local Node ID、Cluster ID 和初始时的集群信息。用户需要避免 etcd 从一个过期的数据目录中重新启动,因为使用过期的数据目录启动的 Node 会与 Cluster 中的其他 Nodes 产生不一致性,例如:之前已经记录并同意 Leader Node 存储某个信息,重启后又向 Leader Node 申请这个信息。所以,为了最大化集群的安全性,一旦有任何数据损坏或丢失的可能性,你就应该把这个 Node 从 Cluster 中移除,然后加入一个不带数据目录的 New Node。

WAL(Write Ahead Log)最大的作用是记录了整个数据变化的全部历程。在 etcd 中,所有数据的修改在提交前,都要先写入到 WAL 中。使用 WAL 进行数据的存储使得 etcd 拥有两个重要功能:

故障快速恢复: 当你的数据遭到破坏时,就可以通过执行所有 WAL 中记录的修改操作,快速从最原始的数据恢复到数据损坏前的状态。

数据回滚(undo)或重做(redo):因为所有的修改操作都被记录在 WAL 中,需要回滚或重做,只需要方向或正向执行日志中的操作即可。

WAL 和 Snapshot 的命名规则?

在 etcd 的数据目录中,WAL 文件以 $seq-$index.wal 的格式存储。最初始的 WAL 文件是 0000000000000000-0000000000000000.wal,表示这是所有 WAL 文件中的第 0 个,初始的 Raft 状态编号为 0。运行一段时间后可能需要进行日志切分,把新的条目放到一个新的 WAL 文件中。

假设,当集群运行到 Raft 状态为 20 时,需要进行 WAL 文件的切分时,下一份 WAL 文件就会变为 0000000000000001-0000000000000021.wal。如果在 10 次操作后又进行了一次日志切分,那么后一次的 WAL 文件名会变为 0000000000000002-0000000000000031.wal。可以看到 “-” 符号前面的数字是每次切分后自增 1,而 “-” 符号后面的数字则是根据实际存储的 Raft 起始状态来定。

而 Snapshot 的存储命名则比较容易理解,以 $term-$index.wal 格式进行命名存储。term 和 index 就表示存储 Snapshot 时数据所在的 Raft 节点状态,当前的任期编号以及数据项位置信息。

etcd 的数据模型

etcd 的设计目的是用来存放非频繁更新的数据,提供可靠的 Watch 插件,它暴露了键值对的历史版本,以支持低成本的快照、监控历史事件。这些设计目标要求它使用一个持久化的、多版本的、支持并发的数据数据模型。

当 etcd 键值对的新版本保存后,先前的版本依然存在。从效果上来说,键值对是不可变的,etcd 不会对其进行 in-place 的更新操作,而总是生成一个新的数据结构。为了防止历史版本无限增加,etcd 的存储支持压缩(Compact)以及删除老旧版本。

七、Raft协议算法原理

Raft算法为目标设计的一致性共识算法,涉及到共识算法就必然会提到Paxos,但是Paxos的实现和理解起来都非常复杂。Raft协议采用分治的思想,把分布式协同的问题分为3个问题:

选举: 一个新的集群启动时,或者老的leader故障时,会选举出一个新的leader。

日志同步: leader必须接受客户端的日志条目并且将他们同步到集群的所有机器。

安全: 保证任何节点只要在它的状态机中生效了一条日志,就不会在相同的key上生效另一条日志条目。

7.1 Raft 定义

首先,Raft是一种“算法”;其次,Raft 是一种为了管理“复制日志”算法;最后,Raft 是一种为了管理复制日志“一致性”算法。

那么问题来了,什么是一致性?

一致性是分布式系统容错的基本问题。一组机器像一个整体一样工作,即使其中小半部分机器(不大于N/2)出现故障也能够继续工作下去, 一旦他们就状态做出决定,该决定就是最终决定。 例如,即使2台服务器发生故障,5台服务器的集群也可以继续运行。 如果更多服务器失败,它们将停止进展(但永远不会返回错误的结果)

7.2 Raft 三种角色

根据 “拜占庭将军问题” ,我们可以提取出三种状态的角色:

1. 追随者:将军B和C愿意投票给将军A, 将军B和C成为将军A的跟随者

2. 候选者:将军A倒计时结束,成为大将军候选者

3. 大将军:将军A收到大多数将军的投票后,成为大将军

在包含若干节点Raft集群中,其实也存在着相似的角色:Leader、Candidate、Follower。每种角色负责的任务也不一样,正常情况下,集群中的节点只存在 Leader 与 Follower 两种角色。

1. Leader(领导者):处理所有客户端交互,日志复制等,一般一次只有一个Leader;

2. Follower(追随者):响应 Leader 的日志同步请求,响应 Candidate 的邀票请求,以及把客户端请求到 Follower 的事务转发(重定向)给 Leader;

3. Candidate(候选者):负责选举投票,集群刚启动或者 Leader 宕机时,角色为 Follower 的节点将转为 Candidate 并发起选举,选举胜出(获得超过半数节点的投票)后,从 Candidate 转为 Leader 角色;

Raft把时间划分为任期(Term)(如下图所示),任期是一个递增的整数,一个任期是从开始选举leader到leader失效的这段时间。有点类似于一届总统任期,只是它的时间是不一定的,也就是说只要leader工作状态良好,它可能成为一个独裁者,一直不下台。

7.3 Raft 算法问题分解

根据上面的介绍,我们知道通常Raft集群中只有一个Leader,其它节点都是Follower。Follower都是被动的:他们不会发送任何请求,只是简单的响应来自Leader或者Candidate的请求。Leader负责处理所有的客户端请求(如果一个客户端和Follower联系,Follower会把请求重定向给Leader)。为了简化逻辑和实现,Raft将一致性问题分解成三个独立的子问题:

1. Leader election:当leader宕机或者集群创建时,需要选举一个新的Leader

2. Log replication:Leader接收来自客户端的请求并将其以日志的形式复制到集群中的其它节点,并且强制要求其它节点的日志和自己保持一致

3. Safety:如果有任何节点已经应用了一个确定的日志条目到它的状态机中,那么其它服务节点不能在同一个日志索引位置应用一个不用的指令

7.5 Raft 算法原理

上面讲了这么多,其实都是伏笔 。接下来,本文的核心知识点来了。

7.5.1 Raft 角色选举

根据 Raft 协议,一个应用 Raft 协议的集群在刚启动时,所有节点状态都是Follower态,由于没有Leader,Follower 无法与 Leader 保持心跳(heart beat),Follower等待心跳超时(每个Follower的心跳超时时间不一样),Followers 会认为 Leader 已经 down 掉。最先超时的Follower进而转为 Candidate 状态,然后,Candidate 将向集群中的其它节点请求投票,同意自己升级为 Leader,如果 Candidate 收到超过半数节点的投票(N/2+1),它将获胜成为 Leader。

角色选举详细流程如下:

第一阶段:都是 Follower 状态

一个应用Raft协议的集群在刚开始启动时(或者 Leader 宕机重启时),所有的节点都是 Follower 状态,初始任期(Term,即某次选举的唯一标识)都是0。同时启动选举定时器,每个节点的选举定时器都不一致且都在100~500ms之间(避免同时发起选举)。

第二阶段:从 Follower 状态转换为Candidate,并发起投票

由于没有 Leader,Followers 无法与 Leader 保持心跳(heart beat),节点启动后在一个选举定时器周期内未收到心跳和投票请求,则状态转变为 Candidate 状态、Term 自增,并向集群中所有节点发送投票请求并且重置选举定时器。

注意:每个节点选举定时器超时时间都在 100 ~ 500 ms之内,且不一致。因此,可以避免所有的Follower同时转化为 Candidate状态,换言之,最先转为 Candidate 并发起投票请求的节点将具有成为 Leader 的先发优势。

第三阶段:投票策略

Follower 节点收到投票请求后会根据以下情况决定是否接受投票请求:

  • 请求节点的 Term 大于自己的 Term,且自己尚未投票给其它节点,则接受请求,把票投给 Candidate 节点;

  • 请求节点的 Term 小于自己的 Term,且自己尚未投票,则拒绝请求,将票投给自己。

第四阶段:Candidate 转换为 Leader

经过一轮选举后,正常情况,会有一个 Candidate 节点收到超过半数(N/2+1)其它节点的投票,那么它将胜出并升级为 Leader 节点,然后定时发送心跳给其它节点,其它节点会转化为 Follower 节点并与 Leader 保持同步,如此,本轮选举结束。如果一轮选举中,Candidate 节点收到的投票没有超过半数,那么将进行下一轮选举。

7.5.2 Raft 日志同步

一个 Raft 集群中只有 Leader 节点能够处理客户端的请求(如果客户端的请求发到了 Follower 节点,Follower 将会把请求重定向到 Leader),客户端的每一个请求都包含一条被复制到状态机执行的指令。Leader 把这条指令作为一条新的日志条目(Entry)附加到日志中去,然后并行的将附加条目发送给 Followers,让它们复制这条日志条目。当这条日志条目被 Followers 安全的复制,Leader 会应用这条日志条目到它的状态机中,然后把执行的结果返回给客户端。如果 Follower 崩溃或者运行缓慢,再或者是网络丢包,Leader 会不断的重复尝试附加日志条目(尽管已经回复了客户端)直到所有的 Follower 最终都存储了所有的日志条目,确保强一致性。

日志复制详细流程如下:

第一阶段:客户端请求提交到 Leader

Leader 收到客户端请求:如存储一个数据:5;Leader 收到请求后,会将它作为日志条目(Entry)写入本地日志中。此时该 Entry 是未提交状态(uncommitted),Leader并不会更新本地数据,因此它是不可读的。

第二阶段:Leader 将 Entry 发送到其它Follower

Leader 与 Followers 之间保持心跳联系,跟心跳 Leader 将追加的 Entry(AppendEntries) 并行的发送到其它 Follower 节点,并让它们复制这条日志条目,这一过程我们称为:复制(Replication)。

  1. 为什么 Leader 向 Follower 发送的 Entry 是 AppendEntries 呢?

因为 Leader 与 Follower 的心跳是周期性的,而一个周期 Leader 可能接收到客户端的多个请求,因此,随 心跳向 Followers 发送的大概率是多个 Entry,即 AppendEntries。在本例中为了简单,只有一条请求,自然 只有一个 Enrety。

2. Leader 向 Followers 发送的不仅仅是追加的 Entry (AppendEntries)

在发送追加日志条目的时候,Leader 会把新日志条目之前的条目索引(前一个日志条目)位置(prevLogIndex)和Leader任期号(term)包含在里边。如果 Follower 在它的日志中找不到包含相同索引位置和任期号的条目,那么它会拒接这个新的日志条目。因为出现这种情况说明 Follower 和 Leader 是不一致的。

3. 如何解决 Leader 和 Follower 不一致的问题?

在正常情况下,Leader 和 Follower 的日志保持一致,所以追加日志的一致性从来不会失败。然后,Leader 和 Follower 的一系列崩溃情况下会使它们的日志处于不一致的状态。Follower 可能会丢失一些在新的 Leader 中有的日志条目,它也可能拥有一些 Leader 没有的日志条目,或者两者都有发生。丢失或者多出的日志条目可能会持续多个任期。

要使 Follower 的日志与 Leader 恢复一致,Leader 必须找到最后两者达成一致的地方,然后删除从那个节点之后的所有日志,发送自己的日志给 Follower。所有的这些操作都在进行附加日志一致性检查时完成。

Leader 节点针对每个 Follower 节点维护了一个 nextIndex,这表示下一个需要发送给 Follower 的日志条目的索引地址。当一个 Leader 刚获得权力的时候,它初始化所有的 nextIndex 值为自己的最后一条日志的 index + 1。如果一个 Follower 日志和 Leader 不一致,那么在下一次附加日志的时候就会检查失败。在被 Follower 拒绝之后,Leader 就会减小该 Follower 对应的 nextIndex 值并进行重试(即回溯)。

最终 nextIndex 会在某个位置使得 Leader 和 Follower 的日志达成一致。当这种情况发生,附加日志就会成功,这时就会把 Follower 冲突的日志条目全部删除并且附加上 Leader 的日志。一旦附加成功,那么 Follower 的日志就会和 Leader 保持一致,并且在接下来的任期里一致继续保持。

第三阶段:Leader 等待 Followers 回应

Followers 接收到 Leader 发来的复制请求后,有两种可能的回应:

  • 写入本地日志,返回 Success

  • 一致性检查失败,拒绝写入,返回 false。原因和解决办法上面已经详细说明。

注:此时该 Entry 的状态也是未提交(uncommitted)。完成上述步骤后,Followers 会向 Leader 发出回应 - success,当 Leader 收到大多数 Followers 的回应后,会将第一阶段写入的 Entry 标记为提交状态(committed),并把这条日志条目应用到它的状态机中。

第四阶段:Leader回应客户端

完成前三个阶段后,Leader 会回应客户端 - OK,写操作成功。

第五阶段:Leader 通知 Followers Entry 已提交

Leader 回应客户端后,将随着下一个心跳通知 Followers,Followers 收到通知后也会将 Entry 标记为提交状态。至此,Raft 集群超过半数节点已经达到一致状态,可以确保强一致性。需要注意的是,由于网络、性能、故障等各种原因导致的“反应慢”、“不一致”等问题的节点,也会最终与 Leader 达成一致。

7.5.3 Raft 安全性保证

前面的章节里描述了 Raft 算法是如何选举 Leader 和 日志复制。然而,到目前为止描述的机制并不能充分保证每一个状态机会按照相同的顺序执行相同的指令。例如:一个 Follower 可能处于不可用的状态,同时 Leader 已经提交了若干的日志条目;然后这个 Follower 恢复(尚未与 Leader 达成一致)而 Leader 故障,如果该 Follower 被选举为 Leader 并且覆盖这些日志条目,就会出现问题:不同的状态机执行不同的指令序列。

鉴于此,在 Leader 选举的时候需要增加一些限制来完善 Raft 算法。这些限制可保证任何的 Leader 对于给定的任期号(Term),都拥有之前任期的所有被提交的日志条目(所谓 Leader 的完整特性)。

7.5.3.1 选举限制

对于所有基于 Leader 机制一致性算法,Leader 都必须存储所有已经提交的日志条目。为了保障这一点,Raft 使用了一种简单而有效的办法,以保证之前任期号中已提交的日志条目在选举的时候都会出现在新的Leader中。换言之,日志条目的传送是单向的,只从 Leader 传给 Follower, 并且 Leader 从不会覆盖自身本地日志中已经存在的条目。

Raft 使用投票的方式来阻止一个 Candidate 赢得选举,除非这个 Candidate 包含了所有已经提交的日志条目。Candiate 为了赢得选举必须联系集群中的大部分节点,这意味着每一个已经提交的日志条目都在这些服务器节点中肯定存在于至少一个节点上。如果 Candidate 的日志至少和大多数的服务器节点一样新,那么它一定持有了所有已经提交的日志条目。投票请求的限制:请求中包含了 Candidate 的日志信息,然后投票人会拒绝那些日志没有自己日志新的投票请求。

Raft 通过比较两份日志中最后一条日志条目的索引值和任期号,确定谁的日志比较新。如果两份日志最后的条目和任期号不同,那么任期号大的日志更加新一些。如果两份日志最后的任期号相同,那么日志比较长的那个就更加新。

7.5.3.2 提交之前任期内的日志条目

Leader 知道一条当前任期内的日志记录是可以被提交的,只要它被复制到了大多数 Follower 节点上。如果一个Leader 在提交日志条目之前崩溃了,继任的 Leader 会继续尝试复制这条日志记录。然而,一个 Leader 并不能断定之前任期里的日志条目被保存到大多数 Follower 上就一定已经提交了。很明显,从日志复制的过程可以看出。

鉴于上述情况,Raft 算法不会通过计算副本数的方式去提交一个之前任期内的日志条目。只有 Leader 当前任期里的日志条目通过计算副本数目可以被提交;一旦当前任期的日志条目以这种方式提交,由于日志匹配特性,之前的日志条目也都会被间接提交。在某些情况下, Leader 可以安全的知道一个老的日志条目是否已经被提交(只需判断该条目是否存储到所有的节点上),但是 Raft 为了简化问题使用一种更加保守的方式。

当 Leader 复制之前任期里的日志时,Raft 会为所有的日志保留原始任期号,这在提交规则上产生了额外的复杂性。但是,这种策略更加容易辨别出日志,因为它可以随着时间和日志变化对日志维护着同一个任期号。此外,该策略使得新 Leader 只需要发送较少的日志条目。

八、etcd集群搭建

详见:ETCD集群搭建

九、etcd运维

  1. 磁盘打满:

  1. 调整etcd配置扩大容量,重启服务

  2. 压缩老版本数据清理

etcd磁盘打满,参考:ETCD故障排查,ETCD存储满了如何处理

  1. 节点宕机:

正常来说,宕机机器重新拉起服务即可,raft会同步最新的节点数据

  1. 内存爆满:

查看etcd的日志,哪个目录有异常,或者是否有异常日志

相关文章:

ETCD详解

一、etcd概念 ETCD 是一个高可用的分布式键值key-value数据库,可用于服务发现。 ETCD 采用raft 一致性算法,基于 Go语言实现。 etcd作为一个高可用键值存储系统,天生就是为集群化而设计的。由于Raft算法在做决策时需要多数节点的投票&…...

React笔记(五)hook

一、函数组件 1、函数组件的创建 函数组件:使用JS的函数(或箭头函数)创建的组件称为函数组件,函数组件有如下约定 函数名称必须以大写字母开头 函数组件必须有返回值,返回JSX表达式 渲染函数组件:用函数…...

vue3中使用viewerjs实现图片预览效果

vue3中使用viewerjs实现图片预览效果 1、前言2、实现效果3、在vue3项目中使用viewer.js3.1 安装3.2 在main.js中引入3.3 组件中使用 1、前言 viewer.js是一款开源的图片预览插件,功能十分强大: 支持移动设备触摸事件支持响应式支持放大/缩小支持旋转(类…...

Erlang:Linux下使用observer、debugger进行调试

之前写了一篇文章Erlang:使用observer连接远程服务器进行调试,内容是绕过Linux服务器缺失’wxe_driver.so’的wxWidgets环境,启动observer远程连接实现observer调试。 本文则讨论在Linux环境下通过编译安装的方式,保证wxWidgets环境可用性&am…...

2023 年高教社杯全国大学生数学建模竞赛-E 题 黄河水沙监测数据分析详解+思路+Python代码

2023 年高教社杯全国大学生数学建模竞赛-E 题 黄河水沙监测数据分析 十分激动啊啊啊题目终于出来了!!官网6点就进去了结果直接卡死现在才拿到题目,我是打算A-E题全部做一遍。简单介绍一下我自己:博主专注建模四年,参与…...

一生一芯10——verilator v5.008环境搭建

搜索 verilator 官网,得到网址如下: https://www.veripool.org/verilator/ 点击download 找到 git quick install 可以看到git快捷安装所需命令行 可以看到,需要预先安装下面的包文件,去掉前面的#注释符号进行安装 直接进行下面…...

信息化发展27

关键技术一云安全技术 云安全研究主要包含: 一是云计算技术本身的安全保护工作, 涉及相应的数据完整性及可用性、隐私保护性以及服务可用性等方面的内容; 二是借助于云服务的方式来保障客户端用户的安全防护需求, 通过云计算技术…...

leetcode做题笔记129. 求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字: 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节点到叶节点生成的 所有数字之和 。…...

任务管理系统所需功能概述

"任务管理需要有哪些功能?清晰的任务创建与编辑、智能分类和标签系统、提醒与通知功能、进度跟踪与报告、协作与共享功能、集成与兼容性。" 一款优秀的任务管理工具可以帮助我们有效地规划、执行和监控各项任务,提高工作效率。本文将探讨一款理…...

一文学会K8s集群搭建

环境准备 节点数量:2台虚拟机 centos7硬件配置:master节点内存至少3G(2G后面在master节点初始化集群时会报错,内存不够),node节点可以2G,CPU至少2个,硬盘至少30G网络要求&#xff1…...

Win10右键 nvidia rtx desktop manager 怎么删除(最新)

在更新了最新的 nvidia后原来的隐藏鼠标右键菜单后不行了,新方法如下: 步骤一:在键盘“WINR”键同时操作下,启动运行框,在框内输入“regedit”,打开深度系统win7 的注册表编辑器。 步骤二:为防…...

MySQL加密的几种常见方式

MySQL提供了多种加密方式来保护数据的安全性。下面是几种常见的MySQL加密方式: 密码加密: MySQL5.7及以上版本使用SHA-256算法对密码进行加密。这种加密方式更安全,可以防止密码泄露。 之前的MySQL版本使用SHA-1算法进行密码加密。这种加密方…...

Android文字识别-阿里云OCR调用

0,阿里云OCR有在线识别接口,直接用httpPOST调用就能实现,开发起来很快捷。识别率还蛮好,摄像头斜着拍也能识别出来。实测识别时间单次在2s左右,普通使用使能满足需求的。 1,在阿里云页面先注册申请免费试用…...

度矩阵、邻接矩阵

度矩阵(degree matrix) 度矩阵是对角阵,对角上的元素为各个顶点的度,顶点vi的度表示和该顶点相关联的变得数量。 在无向图中,顶点vi的度d(vi)N(i)(即与顶点相连的边的数目)有向图中&#xff0…...

20个经典巧妙电路合集

1、防反接保护(二极管) 在实际电子设计中,防反接保护电路非常重要,不要觉得自己肯定不会接错,实际上无论多么小心,还是会犯错误...... 最简单的就是利用二极管了,利用二极管的单向导电性&#…...

2023全国大学生数学建模ABCDE选题建议,思路模型,小白要怎么选?难度怎么样

首先最重要的&#xff0c;难度C<B<A&#xff0c;D、E题推荐选E题 大家可以查看我们的视频讲解&#xff0c;在这里&#xff1a;【2023全国大学生数学建模竞赛选题建议&#xff0c;难度分析&#xff0c;小白应该怎么选】 https://b23.tv/S6O26uc 选题建议视频播放​b23.t…...

【力扣每日一题】2023.9.5 从两个数字数组里生成最小数字

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们两个数字数组&#xff0c;要我们用这两个数组里的元素组成一个数字&#xff0c;这个数字里需要同时拥有两个数组里的至少一个元…...

跳出Lambda表达式forEach()循环解决思路

背景 在一次需求开发时&#xff0c;发现使用Lambda的forEach()跳不出循环。如下示例代码&#xff0c;想在遍历满足条件时跳出循环。 public static void main(String[] args) {List<Integer> list Arrays.asList(1, 4, 5, 7, 9, 11);list.forEach(e -> {if (e % 2 …...

2023年度AWS SAP直冲云霄训练营学习分享

AWS在公有云市场一直处于行业领先地位&#xff0c;其培训认证体系也是非常的完善的。而且经常在国内组织一些技术论坛&#xff0c;技术分享&#xff0c;公开课&#xff0c;训练营等技术活动。 AWS训练营适合希望学习和考取AWS助理级架构师/专家级架构师&#xff08;AWS SAA/AW…...

2023高教社杯 国赛数学建模E题思路 - 黄河水沙监测数据分析

1 赛题 E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c; 以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位…...

06_快速入门案例实战之电商网站商品管理:集群健康检查,文档CRUD

document数据格式电商网站商品管理案例&#xff1a;背景介绍简单的集群管理商品的CRUD操作&#xff08;document CRUD (3) 集群的管理 1.快速检查集群的健康状态: cat api 可以查看es中各种各样的数据 GET /_cat/health?v epoch timestamp cluster status node…...

机车整备场数字孪生 | 图扑智慧铁路

机车整备场是铁路运输系统中的重要组成部分&#xff0c;它承担着机车的维修、保养和整备工作&#xff0c;对保障铁路运输的运维和安全起着至关重要的作用。 随着铁路运输的发展、机车技术的不断进步&#xff0c;以及数字化转型的不断推进&#xff0c;数字孪生技术在机车整备场…...

Fair|Fur —— Geometry Nodes

目录 Groom Blend Groom Fetch Groom Pack Groom Unpack Groom Switch Guide Advect Guide Collide With VDB Guide Deform Guide Draw Guide Groom Guide Group Guid Grow to Surface Guide Initialize Guide Mask Guide Partition Guide Process Guide Skin…...

java八股文面试[设计模式]——行为型模式

目录 策略模式 观察者模式 责任链模式 模板方法模式 状态模式 行为型模式关注的是各个类之间的相互作用&#xff0c;将职责划分清楚&#xff0c;使得我们的代码更加地清晰。 策略模式 策略模式太常用了 下面设计的场景是&#xff0c;我们需要画一个图形&#xff0c;可选…...

【送书活动】网络安全(黑客)自学

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…...

如何让数据成为企业的生产力?

为什么有的企业投入大量的人力、物力、财力做数字化转型建设最终做了个寂寞&#xff01;企业领导没看到数字化的任何价值&#xff01; 如果要问企业数字化转型建设最核心的价值体现是什么&#xff0c;大部分人都会说是&#xff1a;数据&#xff01; 然而&#xff0c;不同的人…...

监控 -- linux中的一些系统性能状态指令、Prometheus

目录 监控查看性能相关命令Prometheus1、安装和配置2、将 NFS服务器和LB服务器作为exporter采集数据3、在prometheus server里添加安装exporter程序的服务器 grafana出图工具 监控 监控的目的是获取数据&#xff0c;通过数据分析了解机器是否正常运行 查看性能相关命令 查看c…...

跳槽面试:如何转换工作场所而不失去优势

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

TINA如何导入spice模型

本文介绍如何使用TINA仿真运算放大器电路。TINA是TI公司自己的spice仿真软件&#xff0c;各个大厂为了更好的让客户使用自己的器件&#xff0c;都纷纷推出自己的仿真软件&#xff0c;ADI也有类似的软件&#xff0c;有机会我们介绍&#xff0c;这期我们主要简单介绍下如何使用TI…...

C. MEX Repetition Pinely Round 2 (Div. 1 + Div. 2)

Problem - C - Codeforces 题目大意&#xff1a;有一个长度为n的数组&#xff0c;数组中每个数字互不相同&#xff0c;范围都是0到n&#xff0c;每次操作将每一个数字从左到右依次变成当前数组的MEX&#xff0c;问k次操作后的数组 1<n<1e5&#xff1b;1<k<1e9 思…...

俄文视频网站开发/品牌线上推广方案

因为没有打开本地api set httpd port 2812 and use address localhost allow localhost...

企业网站排名/seo咨询解决方案

使用情景区别 listenTo用于监听自身意外的对象 on用于监听自身 listenTo和on中的回调函数里的this的区别 listener.listenTo(object, eventName, function(){//此处的this指向listener})object.on(eventName, function(){//此处的this指向object})object.on(eventName, functio…...

如何进行域名注册/seo推广技巧

1在准备安装配置PHP前&#xff0c;首先我们先要把下载相关的软件&#xff0c;需要用到PHP包和fcgisetup_1.5_x86_rtw组件&#xff0c;这两个软件都可以通过百度搜索来下载&#xff0c;为保证软件质量建议到官方网站进行下载&#xff0c;这里以php-5.4.3-Win32-VC9-x86为例&…...

网站的成功案例/达州seo

昨天是一个值得庆祝的日子&#xff0c;妈妈出院了。昨天晚上忙着看纪录片《圆明园》&#xff0c;所以没写下什么。 到现在还记得95年的那个夏天&#xff0c;我住在姐姐家&#xff0c;后来妈妈住院了&#xff0c;得了风心病&#xff0c;当时插了氧气&#xff0c;很严重。到冬天的…...

网站除了域名还要什么/seo管理工具

#PS&#xff1a;请尊重原创&#xff0c;不喜勿喷 #PS&#xff1a;要转载请注明出处&#xff0c;本人版权所有 #PS:这个只是 《 我自己 》理解&#xff0c;如果和你的原则相冲突&#xff0c;请 谅解&#xff0c;勿喷 前述&#xff1a; 懒人一个&#xff0c;闲的无聊。上网…...

淄博企业网站建设有限公司/seo托管服务

一、关于Nginx的负载均衡 在服务器集群中&#xff0c;Nginx起到一个代理服务器的角色&#xff08;即反向代理&#xff09;&#xff0c;为了避免单独一个服务器压力过大&#xff0c;将来自用户的请求转发给不同的服务器。 二、Nginx负载均衡策略 负载均衡用于从“upstream”模…...