Raft协议

news2024/11/26 18:24:46

Raft协议先行了解

总体过程速览

  • 假设我们只使用一个节点,可以很容易的达成协议或者共识。

  • 但是现在我们思考,假如有多个节点呢?

  • 多个节点之间达成协议或者共识就叫做分布式共识

  • Raft就是一个实现分布式共识的协议

  • 一个节点可以有3个状态:

    • 追随者
    • 候选人
    • 领导者
  • 所有节点都以跟随者状态开始

  • 如果追随者没有收到领导者的消息(心跳包机制),那么他们就可以成功候选人

  • 候选人向其他节点发送投票

  • 节点将用他们的投票来回复

  • 如果候选人获得了大多数节点的选票,则候选人成为领导者

  • 这个过程叫做领导选举

  • 领导被选举出来之后,系统的所有更改现在都经过领导者

  • 每一个更改都作为一个条目添加到节点的日志中

  • 此日志条目当前未提交,因此不会更新节点的值

  • 要提交条目,节点首先将其复制到跟随着节点

  • 然后领导者等待,直到大多数节点都写入了条目

  • 该条目现在已在领导节点上提交,节点状态是5

在这里插入图片描述

  • 然后领导者通知追随者该条目已经提交了

  • 集群现在已经就系统状态达成了共识

在这里插入图片描述

  • 这个过程叫做日志复制

领导选举

  • 在Raft中有两个超时设置来控制选举

在这里插入图片描述

Term:任期,每次开始一次新的选举,term++

  • 第一个超时时间是选举超时

  • 选举超时是追随者等待成为候选人的时间,意思就是假如150ms - 300ms的时间之内没有人进行选举,也没有候选人,那么追随者就可以成功候选人进行选举。

  • 选举超时随机在 150 毫秒到 300 毫秒之间

  • 选举超时后,跟随者成为候选人并开始新的选举任期

  • 候选人可以给自己投票,并且向其他节点发送请求投票消息

在这里插入图片描述

  • 如果接收节点在这个任期内还没有投票,那么它将投票给候选人

  • 然后节点重置其选举超时

  • 一旦候选人获得多数选票,它就成为领导者

  • 领导者开始向其追随者发送附加条目消息

在这里插入图片描述

  • 这些消息按照心跳超时指定的时间间隔发送

  • 追随者收到消息之后响应每个附加条目的消息

  • 这个选举任期将一直持续到跟随者停止接收心跳并成为候选人为止

  • 让我们停止领导者并观察重新选举的发生(B宕机了)

在这里插入图片描述

  • 要求多数选票保证每个任期只能选出一名领导人

  • 如果两个节点同时成为候选者,则可能会发生分裂投票

  • 两个节点都开始了同一任期的选举

在这里插入图片描述

  • 并且每个都先于另一个到达一个跟随者节点

  • 现在每个候选人有 2 票,并且不能再获得这个任期

在这里插入图片描述

  • 此时无法进行选举,节点将等待新的选举并重试

  • 节点 C 在第 5 期获得多数票,因此成为领导者

在这里插入图片描述

日志复制

  • 一旦我们选出了领导者,我们需要将对我们系统的所有更改复制到所有节点

  • 这是通过使用用于心跳的相同附加条目消息来完成的

  • 我们来看一下这个过程:

    • 首先客户端发送一条更改请求

在这里插入图片描述

  • 这个更改会附加到领导者的日志当中

在这里插入图片描述

  • 然后在下一次心跳的时候将更改发送给关注者
    在这里插入图片描述

  • 一旦大多数追随者承认,条目就会被提交

  • 提交成功之后,向客户端发送响应

  • Raft甚至可以在网络分区时保持一致

在这里插入图片描述

  • 由于我们的分区,现在又两位不同任期的领导人

在这里插入图片描述

  • 让我们添加另一个客户端并尝试更新两个领导者

在这里插入图片描述

  • 一个客户端将尝试将节点 B 的值设置为“3”

在这里插入图片描述

  • 节点 B 无法复制到多数,因此它的日志条目保持未提交状态

  • 另一个客户端将尝试将节点 D 的值设置为“8”

  • 这会成功,因为它可以复制到大多数

在这里插入图片描述

  • 现在恢复网络分区

  • 节点 B 将看到更高的选举任期并下台

  • 节点 A 和 B 都将回滚它们未提交的条目并匹配新领导者的日志

在这里插入图片描述

  • 我们的日志现在在整个集群中是一致的

Raft协议进阶

特此声明,从这里开始,笔者参考了博客《深度解析 Raft 分布式一致性协议》 -Q的博客

Raft是什么?

在分布式系统中,为了消除单点提高系统可用性,通常使用副本来进行容错,但是这样会带来另外一个问题,即如何保证多个副本之间的一致性?

共识算法就是用来做这个事情的,它保证即使在小部分节点故障的情况下,系统仍然可以正常的对外提供服务。

谁在使用Raft协议?

典型的有:

  • etcd
  • consul

Raft总体描述

Raft使用法定人数的机制来实现共识和容错,我们将对Raft集群的操作成为提案,每当发起一个提案,必须得到大多数节点的同意才可以提交。

这里的“提案”我们可以先狭义地理解为对集群的读写操作,“提交”理解为操作成功。

首先Raft必须存在一个主节点,我们作为客户端集群发起的所有操作都必须经由主节点处理。所以Raft核心算法中的第一部分就是选主

那么主节点需要承载什么工作呢?它会负责接收客户端发过来的请求,将操作包装成日志同步给其他节点,在保证大部分节点都同步了本次操作之后,就可以安全的给客户端回应响应了。这一部分工作在Raft核心算法中叫做日志复制

可以看到主节点起到了核心的作用。所以在选主的时候一定要谨慎,要有符合条件的节点才可以成为主节点。此外,主节点在处理操作日志的时候也要谨慎,为了保证集群对外展现的一致性,不可以覆盖或者删除前任主节点已经处理成功的操作日志。这一部分叫做安全性

所以Raft核心算法无非就是由这三个子问题组成的:

  • 选主
  • 日志复制
  • 安全性

除了核心算法之外,Raft还提供了几个工程实践中必须要面对的问题的解决方案。

  • 日志无限制增长的问题。我们必须有一些方法来避免无休止的磁盘占用和过久的日志重放。这部分叫做日志压缩
  • 集群成员变更的问题。一个Raft集群不可能永远是固定的几个节点,总有扩容和缩容的需求,或者是节点需要替换的时候。直接更换集群成员可能会导致严重的脑裂问题。因此Raft给出了一种安全变更集群成员的方式。这一部分叫做集群成员变更

Tips:几个分布式的重要概念:‘

网络分区:网络分区是一种故障类型。通常情况下,网络分区指的是在分布式集群中,节点之间由于网络不通,导致集群中节点形成不同的子集,子集中节点间的网络相通,而子集和子集间网络不通。网络分区是子集与子集之间在网络上相互隔离了。

脑裂问题:以redis为例子:在哨兵架构中,redis的集群脑裂是某个master所在机器突然脱离了正常的网络,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master,这个时候集群里就会有两个master,也就是所谓的脑裂。

选主过程

节点角色

  • Leader: 所有请求的处理者,接收客户端发起的操作请求,写入本地日志后同步至集群其它节点。
  • Follower: 请求的被动更新者,从 leader 接收更新请求,写入本地文件。如果客户端的操作请求发送给了 follower,会首先由 follower 重定向给 leader。
  • Candidate: 如果 follower 在一定时间内没有收到 leader 的心跳,则判断 leader 可能已经故障,此时启动 leader election 过程,本节点切换为 candidate 直到选主结束。

任期

每次开启一次新的选举,称为一个任期,每一个任期都由一共严格递增的整数与之关联。

每当 candidate 触发 leader election 时都会增加 term,如果一个 candidate 赢得选举,他将在本 term 中担任 leader 的角色。但并不是每个 term 都一定对应一个 leader,有时候某个 term 内会由于选举超时导致选不出 leader,这时 candicate 会递增 term 号并开始新一轮选举。

img

来自《深度解析Raft分布式一致性协议》 -Q的博客

term有点儿像一个逻辑时钟,有了它,就可以发现哪些节点的状态已经过期。

节点之间通过RPC来通信,主要有两类RPC请求:

  • 用于candidate拉票选举
  • 用于leader向其他节点复制日志以及同步心跳

Raft的选主过程是基于一种心跳机制,集群中每个节点刚启动的时候都是follower身份,leader会周期性的向所有节点发送心跳包来维持自己的权威。但是如果一个follower在一段时间之内没有接收到leader发送的心跳包,那么它就会发起新的选举。

那么这个的时间该如何设定呢?如果所有节点在同一时刻启动,经过同样的超时时间后同时发起选举那么就很难选择出来一个节点,效率十分底下。Raft巧妙的使用了一个随机化的定时器,让每一个节点在一定的范围内随机生成。

具体过程我们在上面已经讲过一次了,所以这里就不讲了,可以看这个网站里面的动画。

https://thesecretlivesofdata.com/raft/

https://raft.github.io/

这里考虑一种选举失败的场景:

Candidate 在等待投票回复的时候,可能会突然收到其它自称是 leader 的节点发送的心跳包,如果这个心跳包里携带的 term 不小于 candidate 当前的 term,那么 candidate 会承认这个 leader,并将身份切回 follower。这说明其它节点已经成功赢得了选举,我们只需立刻跟随即可。但如果心跳包中的 term 比自己小,candidate 会拒绝这次请求并保持选举状态。

再考虑一种选举超时的场景:

第三种可能的结果是 candidate 既没有赢也没有输。如果有多个 follower 同时成为 candidate,选票是可能被瓜分的,如果没有任何一个 candidate 能得到大多数节点的支持,那么每一个 candidate 都会超时。此时 candidate 需要增加自己的 term,然后发起新一轮选举。如果这里不做一些特殊处理,选票可能会一直被瓜分,导致选不出 leader 来。这里的“特殊处理”指的就是前文所述的随机化选举超时时间

日志复制

状态机复制:状态机复制是实现容错服务的一种常规方法,主要通过复制服务器,并协调客户端和这些服务器镜像间的交互来达到目标。这个方法也同时提供了理解和设计复制管理协议的一套基本框架。

来自维基百科

共识算法通常基于状态复制机Replicated State Machine)模型,所有节点从同一个 state 出发,经过一系列同样操作 log 的步骤,最终也必将达到一致的 state。也就是说,只要我们保证集群中所有节点的 log 一致,那么经过一系列应用(apply)后最终得到的状态机也就是一致的。

具体步骤如下:

  • Leader 为客户端提供服务,客户端的每个请求都包含一条即将被状态复制机执行的指令。
  • Leader 把该指令作为一条新的日志附加到自身的日志集合,然后向其它节点发起附加条目请求AppendEntries RPC),来要求它们将这条日志附加到各自本地的日志集合。
  • 当这条日志已经确保被安全的复制,即大多数(N/2+1)节点都已经复制后,leader 会将该日志 apply 到它本地的状态机中,然后把操作成功的结果返回给客户端。

整个集群的日志模型可以宏观表示为下图(x ← 3 代表 x 赋值为 3):

img

每条日志除了存储状态机的操作指令外,还会拥有一个唯一的整数索引值log index)来表明它在日志集合中的位置。此外,每条日志还会存储一个 term 号(日志条目方块最上方的数字,相同颜色 term 号相同),该 term 表示 leader 收到这条指令时的当前任期,term 相同的 log 是由同一个 leader 在其任期内发送的。

当一条日志被 leader 节点认为可以安全的 apply 到状态机时,称这条日志是 committed(上图中的 committed entries)。那么什么样的日志可以被 commit 呢?答案是:当 leader 得知这条日志被集群过半的节点复制成功时。因此在上图中我们可以看到 (term3, index7) 这条日志以及之前的日志都是 committed,尽管有两个节点拥有的日志并不完整。

Raft 保证所有 committed 日志都已经被持久化,且“最终”一定会被状态机apply。

注:这里的“最终”用词很微妙,它表明了一个特点:Raft 保证的只是集群内日志的一致性,而我们真正期望的集群对外的状态机一致性需要我们做一些额外工作,这一点在《线性一致性与读性能优化》一章会着重介绍。

日志复制流程图解

我们通过 Raft 动画 来模拟常规日志复制这一过程:

img

如上图,S1 当选 leader,此时还没有任何日志。我们模拟客户端向 S1 发起一个请求。

img

S1 收到客户端请求后新增了一条日志 (term2, index1),然后并行地向其它节点发起 AppendEntries RPC。

img

S2、S4 率先收到了请求,各自附加了该日志,并向 S1 回应响应。

img

所有节点都附加了该日志,但由于 leader 尚未收到任何响应,因此暂时还不清楚该日志到底是否被成功复制。

img

当 S1 收到2个节点的响应时,该日志条目的边框就已经变为实线,表示该日志已经安全的复制,因为在5节点集群中,2个 follower 节点加上 leader 节点自身,副本数已经确保过半,此时 S1 将响应客户端的请求

img

leader 后续会持续发送心跳包给 followers,心跳包中会携带当前已经安全复制(我们称之为 committed)的日志索引,此处为 (term2, index1)。

img

所有 follower 都通过心跳包得知 (term2, index1) 的 log 已经成功复制 (committed),因此所有节点中该日志条目的边框均变为实线。

对日志一致性的保证

前边我们使用了 (term2, index1) 这种方式来表示一条日志条目,这里为什么要带上 term,而不仅仅是使用 index?原因是 term 可以用来检查不同节点间日志是否存在不一致的情况,阅读下一节后会更容易理解这句话。

Raft协议做出了两个保证:

  • 如果不同的节点日志集合中的两个日志条目拥有相同的term和index,那么他们一定存储了相同的指令。为什么可以作出这种保证?因为 Raft 要求 leader 在一个 term 内针对同一个 index 只能创建一条日志,并且永远不会修改它。
  • 如果不同的节点日志集合中的两个日志条目拥有相同的term和index,那么他们之前的所有的日志条目也全部相同。这是因为 leader 发出的 AppendEntries RPC 中会额外携带上一条日志的 (term, index),如果 follower 在本地找不到相同的 (term, index) 日志,则拒绝接收这次新的日志

如何处理日志不一致的问题

实际上,真实的集群情况是很复杂的,各种时间点的宕机等复杂的情况会导致很多日志不一致的场景。那么如何处理这种场景呢?

解决方法非常的简单粗暴:
Raft强制要求follower必须复制leader的日志集合来解决不一致的问题

也就是说,follower 节点上任何与 leader 不一致的日志,都会被 leader 节点上的日志所覆盖。这并不会产生什么问题,因为某些选举上的限制,如果 follower 上的日志与 leader 不一致,那么该日志在 follower 上一定是未提交的。未提交的日志并不会应用到状态机,也不会被外部的客户端感知到。

要使得 follower 的日志集合跟自己保持完全一致,leader 必须先找到二者间最后一次达成一致的地方。因为一旦这条日志达成一致,在这之前的日志一定也都一致(回忆下前文)。这个确认操作是在 AppendEntries RPC 的一致性检查步骤完成的。

Leader 针对每个 follower 都维护一个 next index,表示下一条需要发送给该follower 的日志索引。当一个 leader 刚刚上任时,它初始化所有 next index 值为自己最后一条日志的 index+1。但凡某个 follower 的日志跟 leader 不一致,那么下次 AppendEntries RPC 的一致性检查就会失败。在被 follower 拒绝这次 Append Entries RPC 后,leader 会减少 next index 的值并进行重试。

最终一定会存在一个 next index 使得 leader 和 follower 在这之前的日志都保持一致。极端情况下 next index 为1,表示 follower 没有任何日志与 leader 一致,leader 必须从第一条日志开始同步。

针对每个 follower,一旦确定了 next index 的值,leader 便开始从该 index 同步日志,follower 会删除掉现存的不一致的日志,保留 leader 最新同步过来的。

整个集群的日志会在这个简单的机制下自动趋于一致。此外要注意,leader 从来不会覆盖或者删除自己的日志,而是强制 follower 与它保持一致。

因此我们可以看到日志的正确性是非常重要的,所以我们一定要保证日志的正确性,所以我们接下来来讲安全性和正确性

安全性和正确性

前面的章节我们讲述了 Raft 算法是如何选主和复制日志的,然而到目前为止我们描述的这套机制还不能保证每个节点的状态机会严格按照相同的顺序 apply 日志。想象以下场景:

  1. Leader 将一些日志复制到了大多数节点上,进行 commit 后发生了宕机。
  2. 某个 follower 并没有被复制到这些日志,但它参与选举并当选了下一任 leader。
  3. 新的 leader 又同步并 commit 了一些日志,这些日志覆盖掉了其它节点上的上一任 committed 日志。
  4. 各个节点的状态机可能 apply 了不同的日志序列,出现了不一致的情况。

因此我们需要对“选主+日志复制”这套机制加上一些额外的限制,来保证状态机的安全性,也就是 Raft 算法的正确性。

对选举的限制

我们再来分析下前文所述的 committed 日志被覆盖的场景,根本问题其实发生在第2步。Candidate 必须有足够的资格才能当选集群 leader,否则它就会给集群带来不可预料的错误。Candidate 是否具备这个资格可以在选举时添加一个小小的条件来判断,即:

每个 candidate 必须在 RequestVote RPC 中携带自己本地日志的最新 (term, index),如果 follower 发现这个 candidate 的日志还没有自己的新,则拒绝投票给该 candidate。

Candidate 想要赢得选举成为 leader,必须得到集群大多数节点的投票,那么它的日志就一定至少不落后于大多数节点。又因为一条日志只有复制到了大多数节点才能被 commit,因此能赢得选举的 candidate 一定拥有所有 committed 日志

因此前一篇文章我们才会断定地说:Follower 不可能比 leader 多出一些 committed 日志。

比较两个 (term, index) 的逻辑非常简单:如果 term 不同 term 更大的日志更新,否则 index 大的日志更新。

对提交的限制

Leader 只允许 commit 包含当前 term 的日志。

集群成员变更

这个东西主要解决的问题是:如何安全的改变集群的节点数据。

在前文的理论描述中我们都假设了集群成员是不变的,然而在实践中有时会需要替换宕机机器或者改变复制级别(即增减节点)。一种最简单暴力达成目的的方式就是:停止集群、改变成员、启动集群。这种方式在执行时会导致集群整体不可用,此外还存在手工操作带来的风险。

为了避免这样的问题,Raft 论文中给出了一种无需停机的、自动化的改变集群成员的方式,其实本质上还是利用了 Raft 的核心算法,将集群成员配置作为一个特殊日志从 leader 节点同步到其它节点去。

未完待续。。。

日志压缩

我们知道 Raft 核心算法维护了日志的一致性,通过 apply 日志我们也就得到了一致的状态机,客户端的操作命令会被包装成日志交给 Raft 处理。然而在实际系统中,客户端操作是连绵不断的,但日志却不能无限增长,首先它会占用很高的存储空间,其次每次系统重启时都需要完整回放一遍所有日志才能得到最新的状态机。

因此 Raft 提供了一种机制去清除日志里积累的陈旧信息,叫做日志压缩

快照Snapshot)是一种常用的、简单的日志压缩方式,ZooKeeper、Chubby 等系统都在用。简单来说,就是将某一时刻系统的状态 dump 下来并落地存储,这样该时刻之前的所有日志就都可以丢弃了。所以大家对“压缩”一词不要产生错误理解,我们并没有办法将状态机快照“解压缩”回日志序列。

注意,在 Raft 中我们只能为 committed 日志做 snapshot,因为只有 committed 日志才是确保最终会应用到状态机的。

img

上图展示了一个节点用快照替换了 (term1, index1) ~ (term3, index5) 的日志。

快照一般包含以下内容:

  1. 日志的元数据:最后一条被该快照 apply 的日志 term 及 index
  2. 状态机:前边全部日志 apply 后最终得到的状态机

当 leader 需要给某个 follower 同步一些旧日志,但这些日志已经被 leader 做了快照并删除掉了时,leader 就需要把该快照发送给 follower。

同样,当集群中有新节点加入,或者某个节点宕机太久落后了太多日志时,leader 也可以直接发送快照,大量节约日志传输和回放时间。

同步快照使用一个新的 RPC 方法,叫做 InstallSnapshot RPC

可以参考这一篇文章:

https://juejin.cn/post/6907151199141625870#heading-22

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

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

相关文章

数字化转型的十大好处

前言: 在过去的几年中,“适者生存”对企业来说是至关重要的。不能适应环境变化的企业,也将会加速被淘汰的进程。只有从数字化转型中受益的企业才能更好的参与管理和快速调整,这样一来,员工便能够在更高效、更安全的状…

黑盒测试用例设计 - 判定表法

什么是判定表? 判定表法也叫判定驱动法,是分析和表达多逻辑条件下执行不同操作的情况的工作。 应用场合:只要适用于多条件的内容组合与结果分析 它由以下几个内容组成: 条件桩(condition stub)&#xff1…

LwIP带操作系统的移植

目录 LwIP移植前期准备 LwIP移植流程 修改lwipopts.h 修改lwip_comm.c文件 修改ethernetif.c/h文件 修改ethernetif_input函数 修改ethernet.c文件 添加应用程序 LwIP是支持操作系统的,在操作系统的支持下我们可以使用LwIP提供的另外两种API编程接口编程。没…

使用动态代理+Netty+Zookeeper+Protobuff手撸一个RPC框架

RPC是什么 RPC(Remote Procedure Call)远程过程调用,一种计算机之间的远程调用技术,客户端能够在不知道服务器底层的通信架构的情况下调用服务器端的方法,就像调用自身的方法一样。 举个例子: 老婆自己去…

Uni-app 实现md5加密

写下这篇文章,记录自己走过的坑 第一次尝试:参照博客uniapp使用md5_清雨小竹的博客-CSDN博客_uniapp md5 引入md5.js后,在main.js中import后,无法使用md5.hex_md5("需要加密的字符串"),vue页面无法打开&…

【捕风捉影】Vue项目报错,点击浏览器报错信息定位不到报错代码,该如何优雅地调试代码?

【捕风捉影】Vue项目如何优雅地调试代码一、背景二、调试时开启productionSourceMap三、devtool几种模式一、背景 通过vue-cli服务运行项目,项目运行一切正常。但打包后,通过nginx部署运行,大屏展示模块报echarts typeError 错误。但是点击浏…

如何使用Docker创建自定义网络

目录 网络模式 1.bridge模式(默认模式--桥接模式) 初识网络模式 查看桥接模式的特点 2.host模式(仅主机模式) 使用守护进程的方式创建并启动且进入容器 查看仅主机模式下的网络配置 端口映射 :​ 3.如何创建自定义网络 网络模式 Docker…

启发式算法 之 模拟退火原理及实践

一、初窥其貌 1.1 启发式算法和元启发式算法 启发式算法是求解优化问题的一类方法,因为经典优化方法存在局限性,有时无法得到最优解,只能得到一个可以接受的近似最优解,启发式算法就适合求解这类问题。启发式算法就是专家的推测…

Redis框架(七):大众点评项目 缓存穿透、缓存击穿、缓存雪崩

大众点评项目 缓存穿透、缓存击穿、缓存雪崩需求:缓存穿透、缓存击穿、缓存雪崩处理策略缓存穿透处理缓存雪崩缓存击穿总结SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评 主要依照以下几个原则…

吉时利Keithley静电计程控上位机软件-摩擦纳米发电机测试软件NS-EM

1、产品简介 NS-EM 静电计程控系统可实现对吉时利静电计的程控,通过此系统软件您可以单独程控静电计进行数据的采集的同时还可以利用告诉信号采集卡对测试获取的电压、电流等信号进行高频率采样并实时显示采集信号的波形图。 2、产品特点 ◆可远程进行仪器控制&am…

QF state machine 介绍

转型Qt小半年了,看到项目组用的Qt state machine signal和匿名函数满天飞,就想之前用的C#里的QF state machine 能不能做转到Qt平台。这样可以省去使用Qt状态机的信号,在这过程中学习借鉴了QF state machine 的鼻祖 QP框架,不知道…

编译原理笔记

第一课: 《编译原理求语法树的短语和直接短语等等》 二义性是什么? 如果最左推导和最右推导的结果不一致,那么说明文法有二义性 短语是什么? 找短语就是找能长叶子的结点,有五个如图圆圈标号1 2 3 4 5 直接短语&#x…

c语言:联合体—union

联合体一.基本认识1.一个联合体的基本样式2.内部成员的访问3.具体的内存分配二.大小端对联合体的影响三.一个问题一.基本认识 1.一个联合体的基本样式 看得出来其实跟我们定义结构体是一样的(如果还不大了解结构体的可以看看这篇博客什么是结构体)&…

[附源码]计算机毕业设计港口集团仓库管理系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis MavenVue等等组成,B/S模式…

MoveIT1 Assistant 总结

文章目录环境步骤备注故障问题解决参考接上一篇,生成URDF后,在MoveIT Assistant生成配置用于运动规划。https://blog.csdn.net/woshigaowei5146/article/details/128237105?spm1001.2014.3001.5501 环境 Ubuntu20.04;ROS1 Noetic;VMware 步…

生成模型(一):GAN

生成对抗网络 (GAN)在许多生成任务中显示出很好的结果,以复制真实世界的丰富内容,例如图像、文字和语音。它受到博弈论的启发:一个生成器和一个判别器,在互相竞争的同时让彼此变得更强大。然而,训练 GAN 模型相当具有挑…

一篇解析Linux paging_init

说明: Kernel版本:4.14ARM64处理器,Contex-A53,双核使用工具:Source Insight 3.5, Visio 1. 介绍 从详细讲解Linux物理内存初始化中,可知在paging_init调用之前,存放Kernel Image和…

java计算机毕业设计ssm幼儿英语学习平台的设计与实现yofnu(附源码、数据库)

java计算机毕业设计ssm幼儿英语学习平台的设计与实现yofnu(附源码、数据库) 项目运行 环境配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支…

注册微信小程序

文章目录1. 项目结构2. 页面组成3. json配置文件4. 认识页面5. WXML6. WXSS7. js文件8. 宿主环境9. 组件10. API11. 协同工作与发布跟公众号平台不共用一个账号,需要用其它邮箱另行注册,填写身份证信息(姓名、身份证号码)&#xf…

[附源码]Node.js计算机毕业设计电商后台管理系统Express

项目运行 环境配置: Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境:最好是Nodejs最新版,我…