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

【分布式数据一致性算法】Gossip协议详解

在分布式系统中,多个节点同时提供服务时,数据一致性是核心挑战。在多个节点中,若其中一个节点的数据发生了修改,其他节点的数据都要进行同步。

一种比较简单粗暴的方法就是 集中式发散消息,简单来说就是一个主节点同时共享最新信息给其他所有节点,比较适合中心化系统。这种方法的缺陷也很明显,节点多的时候不光同步消息的效率低,还太依赖于中心节点,存在单点风险问题。

背景

分布式系统由多个节点组成,这些节点通常需要共享信息以保持一致性和协同工作。因此,不同的节点之间需要有一种机制来进行 数据/信息共享

集中式发散消息

  • 集中式发散消息 是一种简单粗暴的方式。在这种方式中,系统中有一个 主节点(也称为中心节点),它负责向其他所有节点广播最新的信息或数据。
  • 在这种模式下,中心节点是 数据的源头,所有其他节点依赖这个中心节点来接收数据。

集中式发散消息的缺点

这种方法的缺陷也很明显:

  1. 效率低:如果节点数量非常多,中心节点需要将消息传递给每一个节点。这在节点多时会导致 消息传递效率低,并且可能造成网络瓶颈。
  2. 单点故障问题:所有的节点都依赖中心节点,若中心节点出现故障或者网络中断,整个系统的消息传递就会受到影响,造成 单点风险(即单个节点故障会影响整个系统的正常运行)。

Gossip协议的诞生

为了解决集中式发散消息的缺点,分布式系统引入了 Gossip 协议。Gossip 协议是一种 分散式发散消息 机制,它的基本思想是:

  • 每个节点都参与信息的传播,节点之间相互传播数据。
  • 信息的传播类似于“八卦、闲话、流言蜚语”(Gossip),节点间通过随机选择的方式与其他节点交换信息,最终全网节点都会共享到最新的信息。

Gossip 协议的优点

  1. 去中心化:不像集中式发散消息依赖于一个中心节点,Gossip 协议是 分散式的,每个节点都可以进行信息传播。
  2. 容错性好:如果某个节点故障,其他节点仍然可以通过其他途径获取到信息,因此避免了单点故障的风险。
  3. 扩展性好:Gossip 协议非常适合大规模的分布式系统,随着节点数量增加,信息传播的速度不会像集中式那样受到中心节点的限制。

Gossip协议介绍

Gossip 直译过来就是闲话、流言蜚语的意思。流言蜚语有什么特点呢?容易被传播且传播速度还快,你传我我传他,然后大家都知道了

Gossip 协议 也叫 Epidemic 协议(流行病协议)或者 Epidemic propagation 算法(疫情传播算法),别名很多。不过,这些名字的特点都具有 随机传播特性 (联想一下病毒传播、癌细胞扩散等生活中常见的情景),这也正是 Gossip 协议最主要的特点。

Gossip 协议最早是在 ACM 上的一篇 1987 年发表的论文 《Epidemic Algorithms for Replicated Database Maintenance》中被提出的。根据论文标题,我们大概就能知道 Gossip 协议当时提出的主要应用是在分布式数据库系统中各个副本节点同步数据

正如 Gossip 协议其名一样,这是一种随机且带有传染性的方式将信息传播到整个网络中,并在一定时间内,使得系统内的所有节点数据一致。

在 Gossip 协议下,没有所谓的中心节点,每个节点周期性地随机找一个节点互相同步彼此的信息,理论上来说,各个节点的状态最终会保持一致。

下面我们来对 Gossip 协议的定义做一个总结:Gossip 协议是一种允许在分布式系统中共享状态的去中心化通信协议,通过这种通信协议,我们可以将信息传播给网络或集群中的所有成员

Gossip协议应用

NoSQL 数据库 Redis 和 Apache Cassandra、服务网格解决方案 Consul 等知名项目都用到了 Gossip 协议,学习 Gossip 协议有助于我们搞清很多技术的底层原理。

我们这里以 Redis Cluster 为例说明 Gossip 协议的实际应用。

我们经常使用的分布式缓存 Redis 的官方集群解决方案(3.0 版本引入) Redis Cluster 就是基于 Gossip 协议来实现集群中各个节点数据的最终一致性。


Redis Cluster

Redis Cluster 是一种基于分布式架构的 Redis 部署方式,它通过将数据分片到多个节点上来实现高可用和扩展性。在这种架构中,节点之间需要进行通信以同步状态和共享信息,Redis 使用 Gossip 协议 来保证节点间的有效通信。

Redis Cluster 是 Redis 的 分片集群部署方式,它使得 Redis 能够实现水平扩展,即通过将数据分散到多个节点上来提升集群的存储容量和处理能力。

1. 数据分片

  • Redis Cluster 通过将数据 分片(Sharding)到不同的节点来扩展 Redis 的存储容量。每个节点只存储数据的一部分,而不是全部数据。
  • Redis Cluster 使用 哈希槽(Hash Slots) 来决定数据的分布。共有 16384 个哈希槽(默认),每个节点负责一部分哈希槽。客户端将数据的键进行哈希计算,然后根据结果找到对应的哈希槽,再通过哈希槽定位到具体的节点。

2. 水平扩展

由于 Redis Cluster 支持分片,它可以通过增加更多的节点来 横向扩展(增加集群的节点数),进而提高集群的存储容量和吞吐量。这意味着,当系统负载增加时,可以简单地通过添加更多节点来扩展容量,而不需要替换现有的硬件。

3. 主从复制

  • 在 Redis Cluster 中,每个分片不仅有一个 主节点(Master),还有多个 从节点(Slave)进行复制。主节点负责处理请求和存储数据,而从节点则复制主节点的数据,用于数据备份、读请求分担以及故障转移。
  • 当主节点发生故障时,从节点可以自动提升为主节点,保证集群的高可用性。

4. 自动故障转移与重新分片

  • Redis Cluster 支持 自动故障转移,如果某个主节点出现故障,集群会自动选举一个从节点提升为主节点,保证系统的高可用性。
  • 重新分片(resharding)机制允许在集群中添加或移除节点时,自动将数据迁移到新的节点上,无需手动干预。

5. 无中心化设计

  • 与传统的 Redis 部署不同,Redis Cluster 不依赖一个单独的 中心节点 来管理集群中的所有节点。每个节点在集群中都是平等的,它们都能处理客户端的请求。

总结

  • Redis Cluster 是 Redis 的 分片集群部署方式,旨在提供 水平扩展性高可用性
  • 它将数据分散到多个节点,通过哈希槽分配数据,同时支持 主从复制自动故障转移数据迁移,以保证数据的可靠性和系统的稳定性。

Redis Cluster 的节点间通信

Redis Cluster 是由多个 Redis 节点 组成的,每个节点负责一个数据分片。为了保持集群的健康和一致性,节点需要定期地交换状态信息,确保集群中的每个节点都能知道其他节点的情况。这就是 Gossip 协议 的作用,它提供了一种 分散式 的通信机制,确保集群节点之间能够共享和同步信息。

Gossip协议在Redis Cluster 中的作用

在 Redis Cluster 中,Gossip 协议用于 节点间的状态同步,使得集群中的每个节点都能够及时获知其他节点的状态,并对发生的事件做出相应的调整。每个 Redis 节点都维护着一份集群的状态信息,并根据这些信息做出决策,例如将某个节点标记为下线或故障。

节点间的状态同步

为了保证集群的健康和一致性,Redis Cluster 中的每个节点都需要知道其他节点的状态信息。这样一来,当集群中某个节点的状态发生变化时,其他节点能够及时了解并作出响应。

状态同步的内容包括:

  • 节点是否在线
  • 节点是否故障(例如,节点未响应或不可达)
  • 节点是否处于下线状态(PFAIL)
  • 节点是否已经下线(FAIL)

每个节点维护集群的状态信息

在 Redis Cluster 中,每个节点会维持一份关于整个集群的 状态信息这份信息记录了集群中所有节点的健康状况、是否故障等信息每个节点通过 Gossip 协议与其他节点通信,交换这些状态信息。具体来说,节点会定期向其他节点发送 PING 消息,以获取其他节点的健康状态。

例如,如果节点 A 发现节点 B 变得不可达或出现故障,它会将此信息通过 Gossip 协议广播给其他节点,通知其他节点更新对节点 B 的状态。这使得集群中的每个节点能够获得准确的集群信息。

做出决策(节点状态的调整)

当节点收到其他节点的状态信息后,它们可以基于这些信息做出决策。例如:

  • 如果一个节点被标记为故障节点(FAIL),其他节点会进行相应的处理,如将其排除在外,避免将请求发送到故障节点。
  • 如果节点的状态是 PFAIL(疑似下线),其他节点可能会开始 故障转移,将该节点的任务迁移到其他健康节点上。
  • 如果某个节点从集群中下线,集群会通过 Gossip 协议通知其他节点,让集群调整数据分布和路由信息。

Redis Cluster 中常见的 Gossip 消息

Redis Cluster 中的节点会交换几种不同类型的 Gossip 消息:

1. MEET 消息

  • MEET 消息用于 将新的 Redis 节点加入集群
  • 在 Redis Cluster 中,如果要将一个新的节点添加到集群,可以通过执行 CLUSTER MEET ip port 命令将新的节点 IP 和端口信息发送给集群中的某个节点。
  • 该节点会广播一条 MEET 消息,通知其他节点有新的节点加入集群。

2. PING/PONG 消息

  • PINGPONG 消息用于 节点间的状态检查和交换信息
  • 每个节点定期向集群中的其他节点发送 PING 消息,询问对方的健康状态。
  • 接收到 PING 消息的节点会发送 PONG 消息作为回应,表示自己仍然在线并且可以正常工作。
  • 这种机制用于检查节点的存活状态,并帮助集群监控节点是否出现故障。

3. FAIL 消息

  • FAIL 消息用于 标记节点为故障节点
  • 当一个节点(比如 A)发现另一个节点(比如 B)可能出现故障时,A 节点会向集群广播一条 FAIL 消息,告知其他节点将 B 节点标记为故障节点。
  • 在 Redis Cluster 中,如果一个节点(B)被标记为 PFAIL(疑似下线),并且在一段时间内没有恢复,那么集群中的半数以上节点会将其正式标记为 FAIL
  • 这种机制帮助集群快速识别故障节点并采取相应的措施,例如将故障节点的数据迁移到其他正常节点,保证数据的高可用性。

这张图片展示了 Redis Cluster(Redis 集群)的架构。以下是对其的详细解释:

 
  • 组件说明
    • Clients:表示客户端,是与 Redis 集群进行交互的主体,可以向集群发起读写请求。
    • Redis Master:图中红色标识的节点(M1、M2、M3),为主节点。主节点负责处理客户端的读写操作,能够对数据进行写入和读取处理。
    • Redis Slave:图中灰色标识的节点(S1、S2、S3),为从节点。从节点主要用于处理客户端的读操作,同时通过主从复制机制(图中黑色箭头表示)同步主节点的数据,当主节点故障时,部分从节点有可能被选举为新的主节点。
  • 通信方式
    • Redis gossip protocol:图中虚线表示节点之间通过 gossip 协议进行通信。每个 Redis 节点都会通过集群总线(cluster bus)与其他所有节点进行通信,以此来交换节点状态、槽(slot)信息等,从而维护集群的状态和数据分布的一致性。
  • 客户端操作
    • 如右下角文字所述,客户端的写操作会发送到主节点;读操作既可以发送到主节点,也可以发送到从节点 。 这种设计可以在一定程度上分散读请求的压力,提高系统的并发处理能力。

有了 Redis Cluster 之后,不需要专门部署 Sentinel 集群服务了。Redis Cluster 相当于是内置了 Sentinel 机制,Redis Cluster 内部的各个 Redis 节点通过 Gossip 协议共享集群内信息

在 Redis Cluster 中,由于它内置了节点间的通信和状态同步机制(通过 Gossip 协议),每个 Redis 节点都能及时了解集群内其他节点的健康状态。如果某个节点发生故障,集群会自动进行故障转移,将一个从节点提升为主节点,确保系统的高可用性。这样一来,Redis Cluster 已经具备了类似 Redis Sentinel 的功能,不再需要单独部署 Sentinel 集群 来进行监控和故障恢复。

  • Redis Sentinel 主要负责监控 Redis 主从节点的状态,处理故障转移和高可用性。
  • Redis Cluster 则提供了 数据分片高可用性,并且通过 Gossip 协议实现节点之间的状态同步和故障检测,自动执行故障转移,因此它不再依赖外部的 Sentinel 集群。

简而言之,Redis Cluster 自带了类似 Sentinel 的高可用性功能,所以你不需要单独配置 Redis Sentinel

Gossip协议消息传播模式

Gossip 设计了两种可能的消息传播模式:反熵(Anti-Entropy) 和 传谣(Rumor-Mongering)

反熵

根据维基百科:

熵的概念最早起源于物理学,用于度量一个热力学系统的混乱程度。熵最好理解为不确定性的量度而不是确定性的量度,因为越随机的信源的熵越大。

在这里,你可以把反熵中的熵理解为节点之间数据的混乱程度/差异性,反熵就是指消除不同节点中数据的差异,提升节点间数据的相似度,从而降低熵值。

具体是如何反熵的呢?集群中的节点,每隔段时间就随机选择某个其他节点,然后通过互相交换自己的所有数据来消除两者之间的差异,实现数据的最终一致性。

主要步骤

  • 节点选择:每个节点会随机选择一个目标节点进行数据交换。这个选择并不是固定的,而是基于某些策略(如随机选择)。
  • 数据交换:选定的节点之间会交换数据,以更新彼此的数据副本。
  • 数据一致性:通过不断地交换信息,系统中的各个节点的数据最终趋于一致。

在实现反熵的时候,主要有推、拉和推拉三种方式:

  • 推(Push)方式:节点主动将自己的数据发送给目标节点,修复目标节点的数据。
    • 例如,节点 A 将自己存储的数据发送给节点 B,B 用 A 的数据更新自己的副本。
  • 拉(Pull)方式:节点主动向目标节点请求数据,拉取对方的数据来修复自己的副本。
    • 例如,节点 A 向节点 B 请求其存储的数据,A 使用 B 的数据更新自己的副本。
  • 推拉(Push-Pull)方式:节点与目标节点进行双向数据交换,既推送自己的数据,又拉取对方的数据,以实现双方数据一致性。
    • 例如,节点 A 和节点 B 相互交换数据,A 和 B 都更新自己的副本。

应用场景

  • 在某些场景下,不采用完全随机的节点进行反熵,而是可以设计成一个 闭环,使得各节点在一个确定的时间范围内实现数据一致性。这种方式减少了随机性的影响,可以更高效地实现数据的一致性。
  • InfluxDB 就是采用了闭环方式来实现反熵,保证了集群数据的最终一致性。

  1. 节点 A 推送数据给节点 B,节点 B 获取到节点 A 中的最新数据。
  2. 节点 B 推送数据给 C,节点 C 获取到节点 A,B 中的最新数据。
  3. 节点 C 推送数据给 A,节点 A 获取到节点 B,C 中的最新数据。
  4. 节点 A 再推送数据给 B 形成闭环,这样节点 B 就获取到节点 C 中的最新数据。

优点:

  • 最终一致性:反熵传播可以确保系统中的所有节点最终达成数据一致性。
  • 减少差异性:通过不断交换数据,节点之间的差异性会逐渐减少,提升系统的整体一致性。
  • 灵活性:节点选择的方式灵活,可以根据实际场景设计优化策略。

缺点:

  • 带宽消耗:由于每次交换数据时,可能会涉及到大量的数据传输,可能消耗大量带宽。
  • 延迟:反熵传播是通过定期的交换来完成数据同步,可能会导致数据一致性更新的延迟,尤其在节点数目较多时。

谣言传播

定义:

谣言传播是一种轻量级的消息传播机制,适用于节点数量庞大的分布式系统。在谣言传播模式下,节点之间只传播新增或变化的数据,而不是整个数据集,目的是减小信息传播的开销和带宽消耗。

工作原理:

在谣言传播模式中,节点之间不进行全量数据交换,而是只传播增量数据(即新增或修改的数据)。每个节点会将自己所知的变化或新增的数据以“谣言”的形式传播给其他节点。其他节点接收到这些“谣言”后,会根据这些信息更新自己的数据。

主要步骤:

  • 节点选择:每个节点会向其他节点传播自己所知的新增或变更的数据,这通常是通过 传播谣言 的方式进行的。
  • 数据传播:节点通过传递包含新增数据的消息,通知其他节点。其他节点收到这些信息后,更新自己的副本。
  • 数据一致性:随着时间的推移,节点间会传播更多的变更信息,最终所有节点都会同步到一致的数据状态。

优点:

  • 低开销:由于只传播新增的数据,而不是全量数据,所以每次消息传播的开销较低,特别适合大规模系统。
  • 高效性:适合于节点数量较多且节点变化频繁的场景,谣言传播能够高效地将信息传递给系统中的所有节点。
  • 去中心化:没有中心节点,所有节点都是平等的,信息可以迅速传播到集群中的每个节点。

缺点:

  • 一致性延迟:由于仅传播增量数据,可能需要多个传播周期才能确保数据一致性,因此一致性收敛可能需要更长时间。
  • 数据冗余:在某些情况下,多个节点可能会同时传播相同的数据,造成冗余的消息传输。

应用场景:

  • 节点数量较多的场景,尤其适用于 节点动态变化频繁节点数目庞大的分布式系统,例如社交网络、在线游戏服务器等。
  • 适用于对一致性要求稍微宽松的场景,能够容忍较短时间内的一致性不完全。

总结与对比

特性反熵传播(Anti-Entropy)谣言传播(Rumor-Mongering)
传播内容所有数据仅新增或修改的数据
适用场景小规模节点集群或对一致性要求严格的场景大规模节点集群,节点动态变化频繁
一致性收敛时间较快,较容易保证最终一致性较慢,可能需要多个周期才能达到一致性
带宽消耗较高,因需要传输所有数据较低,因只传输新增数据
优点数据最终一致性高,适合小规模节点集群,能够较快速同步数据节省带宽,高效适应大规模动态系统
缺点带宽消耗大,延迟较高,适用于节点较少的场景一致性延迟较大,可能导致数据冗余,适用于数据一致性要求较低的场景

总结:

  • 反熵传播适合于节点数目较少且对数据一致性要求较高的场景,它能高效地消除节点之间的数据差异,但可能消耗较多带宽,且一致性收敛较慢。
  • 谣言传播则适合于节点数量较多、节点变化动态的系统,它能够高效地将新增数据传播给所有节点,带宽消耗低,但一致性收敛较慢,适合于数据一致性要求不那么严格的场景。

Gossip协议的优势与缺陷

优势

  1. 理解简单

    • 与其他复杂的分布式协议相比,Gossip 协议的设计和实现相对简单。它的核心思想是节点之间定期交换状态信息,通过多轮传播达到最终一致性,而没有复杂的协调机制。因此,开发者更容易理解和实现。
  2. 容忍节点的增加、减少、宕机或重启

    • Gossip 协议本身是 去中心化的,也就是说没有一个固定的中心节点来负责信息传播,每个节点都是平等的。
    • 如果有新的节点加入或现有节点宕机、重启,只要在理想情况下(节点能够正常通信),这些节点最终都会和其他节点同步状态。Gossip 协议的这种容错性和灵活性使得它特别适用于动态变化的分布式环境。
  3. 速度相对较快

    • 在节点数量较多的情况下,Gossip 协议通过 多播(每个节点向多个节点传播信息)而不是通过一个中心节点传播消息,这种方式比传统的中心化信息传播要更快。每个节点都能并行地向其他节点发送信息,从而加速了信息的扩散过程。

缺陷

  1. 消息传播需要多轮才能传播到整个网络

    • Gossip 协议是 最终一致性 的,它并不保证即时一致性。即,节点状态的传播需要通过多轮的信息交换才能传播到整个网络。这意味着,某些节点在短时间内可能会处于不一致的状态,直到信息最终传递到所有节点。
    • 因此,不一致性 是 Gossip 协议的一个固有问题,无法完全避免,只有在一段时间内信息能达到一致性。
  2. 不允许存在恶意节点(拜占庭将军问题)

    • 在 Gossip 协议中,拜占庭将军问题(即网络中存在恶意节点,可能会发送错误或虚假的消息)会导致信息传播的混乱。如果有恶意节点在 Gossip 协议中传递错误信息,可能导致整个系统的不一致。
    • 因此,Gossip 协议假设所有节点都是 诚实 的,并且不容忍恶意节点的存在。
  3. 可能会出现消息冗余

    • 由于 Gossip 协议的消息传播是随机的,节点之间并不总是按最优路径传播信息。这样,在传播过程中,可能会出现重复的信息。一个节点可能会收到相同的消息多次,造成 冗余
    • 这种冗余可能会导致 带宽浪费资源消耗,尤其在节点数目非常多的情况下,冗余信息可能成为一个问题。

Go语言实现简单的Gossip协议模拟

package mainimport ("encoding/json"  // 用于序列化和反序列化 JSON 数据"fmt"            // 用于格式化输出"math/rand"      // 用于生成随机数"net/http"       // 用于实现 HTTP 服务"sync"           // 用于实现并发控制"time"           // 用于时间相关操作
)// 节点配置结构体,表示一个节点的基本信息
type Node struct {Name       string            // 节点的名称State      map[string]string // 节点的状态(例如版本号等数据)Members    map[string]string // 已知的其他节点(键为地址,值为名称)ListenAddr string            // 当前节点监听的地址mu         sync.Mutex        // 用于保护状态的并发访问
}// 创建一个新节点
func NewNode(name, addr string) *Node {return &Node{Name:       name,           // 设置节点名称State:      make(map[string]string), // 初始化状态信息Members:    make(map[string]string), // 初始化成员列表ListenAddr: addr,           // 设置节点监听地址}
}// 启动HTTP服务,处理来自其他节点的Gossip请求
func (n *Node) Start() {http.HandleFunc("/gossip", n.handleGossip) // 当接收到 /gossip 请求时调用 handleGossip 方法go http.ListenAndServe(n.ListenAddr, nil)   // 启动HTTP服务,异步执行
}// 处理Gossip请求,接收其他节点的状态,并与当前节点的状态合并
func (n *Node) handleGossip(w http.ResponseWriter, r *http.Request) {var remoteState map[string]string// 解码来自请求的状态数据if err := json.NewDecoder(r.Body).Decode(&remoteState); err != nil {http.Error(w, err.Error(), http.StatusBadRequest) // 解码错误时返回 400 错误return}n.mu.Lock()           // 锁定节点状态,以便并发安全地修改defer n.mu.Unlock()   // 确保在函数结束时释放锁// 合并远程节点的状态for k, v := range remoteState {// 如果当前节点的状态中没有这个数据,或者远程数据的版本更高,更新状态if current, exists := n.State[k]; !exists || v > current {n.State[k] = v}}
}// 随机选择一个其他节点,周期性地向其发送Gossip消息
func (n *Node) Gossip() {for {time.Sleep(2 * time.Second) // 每隔2秒进行一次状态传播n.mu.Lock() // 锁定节点状态members := make([]string, 0, len(n.Members)) // 创建一个空切片存储已知节点的地址for addr := range n.Members {members = append(members, addr) // 将成员地址添加到切片中}stateCopy := make(map[string]string, len(n.State)) // 创建当前状态的副本for k, v := range n.State {stateCopy[k] = v // 复制当前状态}n.mu.Unlock() // 释放锁if len(members) == 0 {continue // 如果没有已知成员,跳过本次循环}// 随机选择一个目标节点target := members[rand.Intn(len(members))]// 启动一个 goroutine 向目标节点发送Gossip消息go func() {resp, err := http.Post("http://"+target+"/gossip", "application/json", json.NewEncoder(struct{ Data map[string]string }{Data: stateCopy})) // 向目标节点发送POST请求,携带状态数据if err == nil {defer resp.Body.Close() // 确保响应体在处理后关闭}}()}
}// 主程序,创建并启动多个节点
func main() {rand.Seed(time.Now().UnixNano()) // 初始化随机数生成器// 创建三个节点nodes := []*Node{NewNode("Node1", ":8080"), // 创建并初始化节点1NewNode("Node2", ":8081"), // 创建并初始化节点2NewNode("Node3", ":8082"), // 创建并初始化节点3}// 初始化每个节点的成员列表for _, node := range nodes {for _, other := range nodes {if other.ListenAddr != node.ListenAddr {node.Members[other.ListenAddr] = other.Name // 将其他节点加入当前节点的成员列表}}node.Start()  // 启动节点的HTTP服务go node.Gossip() // 启动节点的Gossip机制}// 在Node1上设置初始值nodes[0].mu.Lock() nodes[0].State["version"] = "1.0.0" // 设置Node1的状态为version: 1.0.0nodes[0].mu.Unlock()// 监控各个节点的状态for {time.Sleep(3 * time.Second) // 每3秒打印一次状态for _, node := range nodes {node.mu.Lock()  // 锁定节点状态fmt.Printf("[%s] State: %v\n", node.Name, node.State) // 打印当前节点的状态node.mu.Unlock() // 释放锁}fmt.Println("------------------") // 分隔符,便于输出查看}
}

输出示例:

[Node1] State: map[version:1.0.0]
[Node2] State: map[]
[Node3] State: map[]
------------------
[Node1] State: map[version:1.0.0] 
[Node2] State: map[version:1.0.0]
[Node3] State: map[version:1.0.0]
------------------

注释说明

  1. Node 结构体

    • Node 结构体表示每个节点的基本配置,包括节点名称、节点状态(State)、已知的成员(Members)、监听的地址等。
  2. NewNode 函数

    • 创建一个新的节点并初始化它的状态和成员列表。
  3. Start 方法

    • 启动节点的 HTTP 服务,并指定当接收到 /gossip 请求时调用 handleGossip 方法处理。
  4. handleGossip 方法

    • 处理其他节点的 Gossip 请求,接收远程节点的状态并将其与当前节点的状态合并。只有在远程节点的数据版本比当前节点更高时才会更新。
  5. Gossip 方法

    • 每隔 2 秒钟,节点随机选择一个成员节点,通过 HTTP 请求将自己的状态发送给它。这是节点与其他节点交换状态的核心逻辑。
  6. main 函数

    • 创建并初始化 3 个节点,每个节点都会启动 HTTP 服务并进行 Gossip。初始化时,第一个节点 (Node1) 设置一个初始的版本信息。
    • 每 3 秒钟打印每个节点的状态信息,以监控状态同步情况。

相关文章:

【分布式数据一致性算法】Gossip协议详解

在分布式系统中,多个节点同时提供服务时,数据一致性是核心挑战。在多个节点中,若其中一个节点的数据发生了修改,其他节点的数据都要进行同步。 一种比较简单粗暴的方法就是 集中式发散消息,简单来说就是一个主节点同时…...

一、初始爬虫

1.爬虫的相关概念 1.1 什么是爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人)就是模拟浏览器发送网络请求,接收请求响应,一种按照一定的规则,自动地爬取互联网信息的程序。 原则上,只要是浏览器…...

C语言番外篇(3)------------>break、continue

看到我的封面图的时候,部分读者可能认为这和编程有什么关系呢? 实际上这个三个人指的是本篇文章有三个部分组成。 在之前的博客中我们提及到了while循环和for循环,在这里面我们学习了它们的基本语法。今天我们要提及的是关于while循环和for…...

ipad连接电脑断断续续,不断弹窗的解决办法

因为ipad air 屏幕摔坏,换了一个内外屏,想用爱思检验一下屏幕真伪, 连接电脑时,断断续续,连上几秒钟然后就断开,然后又连上 然后又断开,不断地弹出信任的弹窗。 刚开始以为是数据线问题&#x…...

dockerfile构建haproxy

1. 结构目录 [rootlocalhost ~]# tree haproxy/ haproxy/ ├── dockerfile └── files├── haproxy-2.5.0.tar.gz├── haproxy.cfg├── install.sh└── start.sh1 directory, 5 files [rootlocalhost ~]# [rootlocalhost ~]# cd haproxy/ [rootlocalhost haproxy]…...

创建第一个 Maven 项目(一)

一、引言 在 Java 开发的广袤天地中,Maven 宛如一位全能的管家,发挥着举足轻重的作用。它是一个基于项目对象模型(POM)的项目管理和构建自动化工具,极大地简化了 Java 项目的开发流程。 Maven 的核心优势之一在于其强…...

深度学习pytorch之19种优化算法(optimizer)解析

提示:有谬误请指正 摘要 本博客详细介绍了多种常见的深度学习优化算法,包括经典的LBFGS 、Rprop 、Adagrad、RMSprop 、Adadelta 、ASGD 、Adamax、Adam、AdamW、NAdam、RAdam以及SparseAdam等,通过对这些算法的公式和参数说明进行详细解析…...

C#贪心算法

贪心算法:生活与代码中的 “最优选择大师” 在生活里,我们常常面临各种选择,都希望能做出最有利的决策。比如在超市大促销时,面对琳琅满目的商品,你总想用有限的预算买到价值最高的东西。贪心算法,就像是一…...

SSH无密登录配置

SSH无密登录配置 1、在用户目录下创建.ssh目录 mkdir /home/atguigu/.ssh2、在.ssh目录下生成ssh秘钥(需要切换到Hadoop集群使用的用户,再运行命令) ssh-keygen -t rsa然后敲(三个回车),就会生成两个文件…...

Rust并发编程实践:10分钟入门系统级编程

目录 学前一问:Rust为何而出现? 摘要 引言 正文解析: 一、Rust中的并发编程基础 1.1 线程 1.2 协程 二、Rust并发编程的高级特性 2.1 通道 2.2 原子操作 2.3 锁 三、实例展示:优化并发编程性能 1. 并行计算 2. 异步…...

Linux 命令大全完整版(06)

2. 系统设置命令 pwunconv 功能说明:关闭用户的投影密码。语法:pwunconv补充说明:执行 pwunconv 指令可以关闭用户投影密码,它会把密码从 shadow 文件内,重回存到 passwd 文件里。 rdate(receive date) 功能说明&a…...

VSCode 中设置 Git 忽略仅因时间戳修改导致的文件变更【使用deepseek生成的一篇文章】

在 VSCode 中设置 Git 忽略仅因时间戳修改导致的文件变更&#xff0c;可通过以下步骤实现&#xff1a; 确认是否为纯时间戳修改 首先确认文件的修改是否仅涉及时间戳&#xff0c;使用终端运行&#xff1a; git diff -- <file>若输出为空但 Git 仍提示修改&#xff0c;可…...

echarts找不到了?echarts社区最新地址

前言&#xff1a;在之前使用echarts的时候&#xff0c;还可以通过上边的导航栏找到echarts社区&#xff0c;但是如今的echarts变更之后&#xff0c;就找不到echarts社区了。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 如今…...

Git-速查

Git 安装 Git 之后&#xff0c;你可以… 配置全局用户信息&#xff08;推荐&#xff09; 全局设置&#xff0c;创建本地仓库时默认分支名称为 main&#xff08;你需要什么名称就该什么名称&#xff09;【推荐配置为 main 】 git config --global init.defaultBranch main全…...

AxiosError: Network Error

不知怎么的&#xff0c;项目还在开发阶段&#xff0c;之前还好好的&#xff0c;玩儿了两天再一打开发现页面无法显示数据了&#xff0c;报错如下&#xff1a; 我以为是后端出问题了&#xff0c;但是后端控制台无报错&#xff0c;又用postman测试了一下&#xff0c;可以获取到数…...

ImGui 学习笔记(三)—— 隐藏主窗口窗口关闭检测

ImGui 的主窗口是平台窗口&#xff0c;默认是可见的&#xff0c;这会影响视觉效果。那么怎么隐藏 ImGui 的主窗口呢&#xff1f; 这很简单&#xff0c;但是需要针对后端做一些修改。 本文仅介绍在 glfwopengl3 和 win32dx11 两种实现上如何修改。 在 win32dx11 实现上&#…...

周末总结(2024/02/22)

工作 人际关系核心实践&#xff1a; 要学会随时回应别人的善意&#xff0c;执行时间控制在5分钟以内 坚持每天早会打招呼 遇到接不住的话题时拉低自己&#xff0c;抬高别人(无阴阳气息) 朋友圈点赞控制在5min以内&#xff0c;职场社交不要放在5min以外 职场的人际关系在面对利…...

SOME/IP--协议英文原文讲解11

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 4.2.6 Er…...

Spring Boot 概要(官网文档解读)

Spring Boot 概述 Spring Boot 是一个高效构建 Spring 生产级应用的脚手架工具&#xff0c;它简化了基于 Spring 框架的开发过程。 Spring Boot 也是一个“构件组装门户”&#xff0c;何为构件组装门户呢&#xff1f;所谓的“构件组装门户”指的是一个对外提供的Web平台&#x…...

图像处理篇---图像处理中常见参数

文章目录 前言一、分贝&#xff08;dB&#xff09;的原理1.公式 二、峰值信噪比&#xff08;PSNR, Peak Signal-to-Noise Ratio&#xff09;1.用途2.公式3.示例 三、信噪比&#xff08;SNR, Signal-to-Noise Ratio&#xff09;1.用途2.公式3.示例 四、动态范围&#xff08;Dyna…...

Win11更新系统c盘爆满处理

1.打开磁盘管理 2.右击c盘选择属性&#xff0c;进行磁盘管理&#xff0c;选择详细信息。 3.选择以前安装的文件删除即可释放c盘空间。...

C++关键字之mutable

1.介绍 在C中&#xff0c;mutable是一个关键字&#xff0c;用于修饰类的成员变量。它的主要作用是允许在常量成员函数或常量对象中修改被标记为mutable的成员变量。通常情况下&#xff0c;常量成员函数不能修改类的成员变量&#xff0c;但有些情况下&#xff0c;某些成员变量的…...

vue3 采用xlsx库实现本地上传excel文件,前端解析为Json数据

需求&#xff1a;本地上传excel 文件&#xff0c;但需要对excel 文件的内容进行解析&#xff0c;然后展示出来 1. 安装依赖 首先&#xff0c;确保安装了 xlsx 库&#xff1a; bash复制 npm install xlsx 2. 创建 Vue 组件 创建一个 Vue 组件&#xff08;如 ExcelUpload.v…...

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解 1 冯诺依曼体系结构1.1 基本概念理解1.2 CPU只和内存打交道1.3 为什么冯诺依曼是这种结构1.4 理解数据流动 2 操作系统2.1 什么是操作系统2.2 设计OS的目的2.3 操作系统小知识点2.4 如何理解"管理"2.5 系统调用和…...

易语言模拟真人鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…...

PHP旅游门票预订系统小程序源码

&#x1f30d; 旅游门票预订系统&#xff1a;一站式畅游新体验&#xff0c;开启您的梦幻旅程 &#x1f31f; 一款基于ThinkPHPUniapp精心雕琢的旅游门票预订系统&#xff0c;正翘首以待&#xff0c;为您揭开便捷、高效、全面的旅游预订新篇章&#xff01;它超越了传统预订平台…...

侯捷 C++ 课程学习笔记:内存管理的每一层面

目录 一、C 应用程序的内存管理架构 二、C 内存原语 三、内存管理的实际应用 四、学习心得 一、C 应用程序的内存管理架构 C 应用程序的内存管理架构可以分为多个层次&#xff0c;从应用程序到操作系统 API&#xff0c;每一层都提供了不同的内存管理功能。 架构图&#xf…...

Python开发Django面试题及参考答案

目录 Django 的请求生命周期是怎样的? Django 的 MTV 架构中的各个组件分别是什么? Django 的 URL 路由是如何工作的? Django 的视图函数和视图类有什么区别? Django 的模板系统是如何渲染 HTML 的? Django 的 ORM 是如何工作的? Django 的中间件是什么?它的作用是…...

前端js进阶,ES6语法,包详细

进阶ES6 作用域的概念加深对js理解 let、const申明的变量&#xff0c;在花括号中会生成块作用域&#xff0c;而var就不会生成块作用域 作用域链本质上就是底层的变量查找机制 作用域链查找的规则是:优先查找当前作用域先把的变量&#xff0c;再依次逐级找父级作用域直到全局…...

【三十四周】文献阅读:DeepPose: 通过深度神经网络实现人类姿态估计

目录 摘要AbstractDeepPose: 通过深度神经网络实现人类姿态估计研究背景创新点方法论归一化网络结构级联细化流程 代码实践局限性实验结果总结 摘要 人体姿态估计旨在通过图像定位人体关节&#xff0c;是计算机视觉领域的核心问题之一。传统方法多基于局部检测与图模型&#x…...

将 Vue 项目打包后部署到 Spring Boot 项目中的全面指南

将 Vue 项目打包后部署到 Spring Boot 项目中的全面指南 在现代 Web 开发中&#xff0c;前后端分离架构已经成为主流。然而&#xff0c;在某些场景下&#xff0c;我们可能需要将前端项目&#xff08;如 Vue&#xff09;与后端项目&#xff08;如 Spring Boot&#xff09;集成部…...

Linux 权限系统和软件安装(二):深入理解 Linux 权限系统

在 Linux 的世界里&#xff0c;权限系统犹如一位忠诚的卫士&#xff0c;严密守护着系统中的文件与目录&#xff0c;确保只有具备相应权限的用户才能进行操作。与其他一些操作系统不同&#xff0c;Linux 并不依据文件后缀名来标识文件的操作权限&#xff0c;而是构建了一套独特且…...

计算机网络常考大题

运输层的主要功能 运输层为应用进程之间提供端到端的逻辑通信。 运输层还要对收到的报文进行差错检测。 运输层需要有两种不同的运输协议&#xff0c;即面向连接的 TCP 和无连接的 UDP 传输控制协议 TCP 概述 TCP 是面向连接的运输层协议。 每一条 TCP 连接只能有两个端点…...

百度首页上线 DeepSeek 入口,免费使用

大家好&#xff0c;我是小悟。 百度首页正式上线了 DeepSeek 入口&#xff0c;这一重磅消息瞬间在技术圈掀起了惊涛骇浪&#xff0c;各大平台都被刷爆了屏。 百度这次可太给力了&#xff0c;PC 端开放仅 1 小时&#xff0c;就有超千万人涌入体验。这速度&#xff0c;简直比火…...

《跟李沐学 AI》AlexNet论文逐段精读学习心得 | PyTorch 深度学习实战

前一篇文章&#xff0c;使用 AlexNet 实现图片分类 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于学习 9年后重读深度学习奠基作之一&#xff1a;AlexNet【下】【论文精读】】的心得。 《跟李沐…...

Linux搭建Nginx直播流媒体服务RTMP/RTSP转Http-flv视频浏览器在线播放/Vue/Java/ffmpeg

参考文章&#xff1a; https://blog.csdn.net/whatareyouding/article/details/144317654 https://www.cnblogs.com/Gredae/p/18362900 https://www.cnblogs.com/kn-zheng/p/17422707.html https://blog.51cto.com/u_16099344/10281495 https://www.tulingxueyuan.cn/tlzx/jsp…...

Node.js高频面试题精选及参考答案

目录 什么是 Node.js?它的主要特点有哪些? Node.js 的事件驱动和非阻塞 I/O 模型是如何工作的? 为什么 Node.js 适合处理高并发场景? Node.js 与传统后端语言(如 Java、Python)相比,有哪些优势和劣势? 简述 Node.js 的运行原理,包括 V8 引擎的作用。 什么是 Nod…...

公开整理-最新中国城市统计NJExcel+PDF版本(1985-2024年)

数据简介&#xff1a;《中国城市统计NJ》从1985年开始&#xff0c;本NJ内容共分四个部分:第一部分是全国城市行政区划,列有不同区域、不同级别的城市分布情况;第二、三部分分别是地级以上城市统计资料和县级城市统计资料,具体包括人口、劳动力及土地资源、综合经济、工业、交通…...

ModuleNotFoundError: No module named ‘xgboost‘

问题&#xff1a; --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) Cell In[1], line 64 import pickle5 from sklearn.metrics import mean_squared_error, r2_…...

应用层协议HTTP

应用层协议HTTP 引言 应用层协议是程序员自己制定的&#xff0c;但是良好的协议是保证网络通信的基础&#xff0c;前代的计算工程师已经帮助我们制定了一些很好用的应用层协议&#xff0c;http(hybertext transfer protocol)(超文本传输协议)就是其中之一。 http协议是客户端…...

常见的“锁”有哪些?

悲观锁 悲观锁认为在并发环境中&#xff0c;数据随时可能被其他线程修改&#xff0c;因此在访问数据之前会先加锁&#xff0c;以防止其他线程对数据进行修改。常见的悲观锁实现有&#xff1a; 1.互斥锁 原理&#xff1a;互斥锁是一种最基本的锁类型&#xff0c;同一时间只允…...

PAT 甲级 1091 Acute Stroke

一开始只是简单的递归&#xff08;bfs&#xff09;&#xff0c;导致最后两个没法通过&#xff08;爆栈了&#xff09; //最后两个案例没有通过&#xff0c;只是最简单的bfs暴力算法 #include<cstdio> using namespace std; int v[62][1288][130]{0}; int find(int i,int…...

flowable适配达梦数据库

文章目录 适配相关问题无法从数据库产品名称“DM DBMS”中推断数据库类型分析解决 构建ibatis SqlSessionFactory时出错&#xff1a;inStream参数为null分析解决 liquibase相关问题问题一&#xff1a;不支持的数据库 Error executing SQL call current_schema: 无法解析的成员访…...

Git入门:数据模型 to 底层原理

版本控制系统&#xff08;VCS&#xff09;是软件开发中不可或缺的工具&#xff0c;而Git作为现代版本控制的事实标准&#xff0c;其底层设计远比表面命令更加优雅。本文将从数据模型的角度&#xff0c;揭示Git的核心工作原理。 Git的核心概念 1. 快照&#xff08;Snapshot&am…...

Bootstrap Blazor UI 中 <Table> 组件 <TableColumn> 使用备忘01:EF Core 外码处理

应用场景&#xff1a;将外码转换为对应的文本进行显示、编辑。 例如&#xff0c;有一个【用户】表&#xff0c;其中有一个【用户类型ID】字段&#xff1b;另有一个【用户类型】表&#xff0c;包含【ID】、【名称】等字段。现在要求在 <Table> 组件显示列表中&#xff0c…...

Redis过期数据处理

Redis缓存过期后数据还能恢复吗&#xff1f; Redis缓存过期后&#xff0c;数据通常会被删除&#xff0c;但可以通过以下几种方法尝试恢复数据&#xff1a; 1. 数据备份恢复 RDB 持久化恢复&#xff1a;Redis 提供了 RDB&#xff08;Redis Database Backup&#xff09;持久化…...

零基础学C/C++160——字符串

题目描述 给定两个由小写字母组成的字符串A和B&#xff0c;判断B中的字符是否全部在A中出现。 输入 输入为多组测试数据。 输入数据只有一行&#xff0c;包含2个字符串A和B&#xff0c;每个字符串后面有一个#字符标记&#xff08;#不属于A或B&#xff09;&#xff0c;其中B…...

Spring Boot+Vue项目从零入手

Spring BootVue项目从零入手 一、前期准备 在搭建spring bootvue项目前&#xff0c;我们首先要准备好开发环境&#xff0c;所需相关环境和软件如下&#xff1a; 1、node.js 检测安装成功的方法&#xff1a;node -v 2、vue 检测安装成功的方法&#xff1a;vue -V 3、Visu…...

Linux 命令大全完整版(13)

5.文件管理命令 patch 功能说明&#xff1a;修补文件。语  法&#xff1a;patch [-bceEflnNRstTuvZ][-B <备份字首字符串>][-d <工作目录>][-D <标示符号>][-F <监别列数>][-g <控制数值>][-i <修补文件>][-o <输出文件>][-p &l…...

MySQL面试学习

MySQL 1.事务 事务的4大特性 事务4大特性&#xff1a;原子性、一致性、隔离性、持久性 原⼦性&#xff1a; 事务是最⼩的执⾏单位&#xff0c;不允许分割。事务的原⼦性确保动作要么全部完成&#xff0c;要么全不执行一致性&#xff1a; 执⾏事务前后&#xff0c;数据保持⼀…...