微服务常见面试题及解答_微服务面试题(1),2024年最新完美收官

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

ConsulEtcd都是分布式键值存储系统,用于配置管理和服务发现,但它们有一些区别,包括设计目标、一致性模型、生态系统等。以下是ConsulEtcd之间的主要区别:

  1. 设计目标
  • ConsulConsul最初设计为一个功能丰富的服务发现和配置管理工具。它提供了服务注册、服务发现、健康检查、负载均衡和分布式配置等功能。Consul旨在为微服务架构提供全面的解决方案。
  • EtcdEtcd的主要设计目标是提供分布式键值存储,通常用于配置存储和协调。EtcdCoreOS项目的一部分,最初是为了支持容器编排系统(如Kubernetes)而创建的。
  1. 一致性模型
  • ConsulConsul使用Raft协议作为其一致性模型,这意味着它提供强一致性,确保数据在不同节点之间的一致性。这对于服务发现和配置管理非常重要。
  • EtcdEtcd也使用Raft协议来实现一致性,因此同样提供强一致性。Etcd的主要关注点是可靠的键值存储,确保数据一致性和可用性。
  1. 功能
  • ConsulConsul在服务发现方面非常强大,提供了多种服务发现模式(DNSHTTPgRPC等)。它还支持健康检查和负载均衡。此外,Consul还提供了分布式配置存储和分片查询等功能。
  • EtcdEtcd的主要焦点是分布式键值存储,通常用于配置存储和协调。它支持事务操作和触发器,并提供了强大的数据一致性保证。
  1. 生态系统
  • ConsulConsul拥有一个较大的生态系统,与许多云原生项目和工具(如KubernetesDockerNomad等)集成紧密。它还支持多种编程语言的客户端库。
  • EtcdEtcd的生态系统相对较小,主要集中在与Kubernetes的集成上,但它也有多种客户端库可用。
  1. 使用场景
  • ConsulConsul通常用于构建包括服务发现、负载均衡和分布式配置管理等功能的复杂微服务架构。它适用于大型、多节点的环境。
  • EtcdEtcd主要用于配置存储和协调,通常与容器编排系统(如Kubernetes)一起使用。它适用于需要高度一致性和可用性的场景。

总结:
ConsulEtcd都是优秀的分布式键值存储系统,但它们的设计目标和主要功能略有不同。选择哪个取决于具体需求,如果需要更多的服务发现和负载均衡功能,Consul可能更适合;如果主要关注分布式配置存储和协调,那么Etcd可能更适合。

3.6 Golang微服务的健康检查

健康检查可以使用监控和警报工具,以实时监视服务的状态并采取适当的措施。一些常见的监控工具包括 Prometheus、Grafana。

3.7 Golang微服务的负载均衡

参考1:go微服务-Micro负载均衡组件–Selector
答:go-micro的负载均衡主要是使用的Selector组件。

下面是一些用于实现负载均衡的常见方法:

  1. 反向代理负载均衡:使用反向代理服务器来分发流量到多个微服务实例。常见的反向代理服务器包括NginxHAProxyEnvoy。这些代理服务器可以配置为根据不同的负载均衡算法(如轮询、加权轮询、最小连接数等)将请求分发到后端的微服务实例。
  2. 服务网格:使用服务网格框架,如IstioLinkerd,来处理微服务之间的通信和负载均衡。服务网格提供了对流量管理、故障恢复、安全性等方面的高级控制。这些框架通常使用代理(如Envoy)来处理流量分发。
  3. 客户端负载均衡:在微服务客户端中实现负载均衡逻辑。这意味着客户端代码会选择要调用的服务实例,通常通过发现服务的方式来获取可用实例列表,然后使用一种负载均衡算法选择目标实例。一些Go中的库,如"github.com/afex/hystrix-go/hystrix""github.com/Netflix/ribbon",可以帮助实现客户端负载均衡逻辑。
  4. 云提供商负载均衡:如果微服务部署在云上,云提供商通常提供了负载均衡服务,如AWS Elastic Load Balancing(ELB)Google Cloud Load Balancing。这些服务可以自动将流量分发到多个实例,并提供了监控和自动缩放功能。
  5. 自定义负载均衡器:如果需要更高级的负载均衡逻辑,可以编写自定义的负载均衡器。在Go中,可以使用库如"github.com/eapache/go-resiliency""github.com/valyala/fasthttp"来构建自定义负载均衡逻辑。
  6. 集成框架:一些Go微服务框架,如Go kitMicro,提供了内置的负载均衡支持,可简化负载均衡的实现。

4 Etcd

4.1 Etcd如何保证数据的一致性

Etcd通过使用分布式一致性协议来确保数据的一致性。Etcd使用的一致性协议是Raft协议,它是一种用于构建强一致性分布式系统的协议。下面是Etcd如何通过Raft协议来保证数据一致性的工作原理:

  1. 领导者选举:在Etcd集群中,有一个节点被选为领导者(leader),其余节点为跟随者(followers)。领导者负责接收客户端请求并将其复制到其他节点上。
  2. 日志复制:当客户端发送写入请求(如设置键值对)时,领导者首先将该请求添加到自己的日志中,并确保大多数跟随者也将该请求添加到它们的日志中。一旦大多数节点都成功复制了该请求,领导者将该请求提交到状态机。
  3. 一致性决策:在提交请求后,领导者会向所有节点广播提交的消息。跟随者接收到消息后,会根据领导者的提交决策来执行相同的操作,以确保所有节点的状态机保持一致。
  4. 故障容忍性Raft协议设计了节点选举机制,以处理领导者节点的故障。如果领导者节点宕机或出现故障,剩余的节点将通过选举过程选择新的领导者,确保系统的持续可用性和一致性。
  5. 强一致性保证Raft协议提供了强一致性保证,确保只有在大多数节点都同意的情况下,写操作才会成功提交。这意味着即使在节点故障或网络分区的情况下,Etcd也能保持数据的一致性。

总结:
Etcd使用Raft协议来保证数据的一致性和可用性。Raft协议通过领导者选举、日志复制、一致性决策和故障容忍性等机制,确保所有节点在数据操作方面保持一致。这使得Etcd成为一个可靠的分布式键值存储系统,适用于配置存储、服务发现和分布式协调等用例。

4.2 Etcd的Raft协议是什么

Etcd使用Raft协议来实现分布式一致性,确保在分布式环境中的多个节点之间达成共识,以维护数据的一致性和可用性。Raft是一种共识算法,旨在简化分布式系统中的领导者选举和日志复制等任务。以下是Raft协议的核心原理:

  1. 领导者选举:在Raft中,每个节点可以处于以下三种状态之一:领导者(leader)、跟随者(follower)和候选人(candidate)。在初始状态下,所有节点都是跟随者。领导者负责处理客户端的请求,并驱动日志的复制。当没有活动的领导者时,跟随者可以成为候选人,并发起领导者选举。
  2. 日志复制:领导者负责维护日志,并将客户端请求的变更操作附加到日志条目中。一旦一条日志条目被多数节点接受,它就会被提交,并且所有节点都会将该日志条目应用到它们的状态机中,从而确保所有节点上的数据一致。
  3. 保证一致性Raft确保在正常运行和节点故障的情况下都能保持一致性。只有大多数节点(半数加一)同意并接受一条日志条目,它才会被提交。这意味着在任何时候,一个分区最多只能包含少数节点,从而确保系统在面临网络分区时仍能正常运行。
  4. 选举超时:为了触发领导者选举,Raft节点会周期性地(通常是随机的时间间隔)发送心跳消息。如果一个跟随者在一定时间内没有收到领导者的心跳消息,它会成为候选人并发起选举。
  5. 领导者选举:在选举中,候选人会向其他节点发送投票请求,并请求其他节点投票支持自己成为领导者。一旦候选人收到多数节点的投票,它就会成为领导者。领导者选举的规则确保了只有一个节点最终会成为领导者。

Etcd使用Raft协议来维护分布式键值存储的一致性和可用性。Raft的设计和实现使得Etcd能够处理节点故障、网络分区和数据复制等复杂的分布式系统问题,确保数据的可靠性和一致性。这使得Etcd成为许多分布式系统中的关键组件,用于配置管理、服务发现和分布式协调等任务。

4.3 Etcd消息过期策略是什么

Etcd中的消息过期策略涉及到键值对的TTL(Time-to-Live)或租约(Lease)机制,用于自动删除过时的数据,以确保存储空间的有效使用和数据的一致性。以下是Etcd中的消息过期策略的主要概念和工作原理:

  1. TTL(Time-to-Live)TTL是一个与键值对相关的时间限制,它指定了数据在存储中的保留时间。当键值对的TTL过期时,Etcd将自动将其删除。TTL是通过设置键值对的TTL属性来定义的。
  2. 租约(Lease): 租约是Etcd中管理TTL的一种机制。客户端可以创建一个租约并将其关联到键值对。当租约过期时,关联的键值对也会被删除。租约机制使客户端可以更灵活地管理TTL,可以更新租约的过期时间或撤销租约,以延长或提前删除键值对。
  3. 自动续约Etcd允许客户端创建自动续约的租约,以确保租约不会过期。客户端可以定期续约租约,以保持其关联的键值对有效。如果续约失败或客户端不再续约租约,租约过期时键值对将被删除。
  4. Watcher 通知:当键值对的TTL过期并且数据被删除时,与该键关联的Watcher会收到通知,以便客户端能够及时知道数据已经过期。

消息过期策略的主要目标是确保Etcd存储中的数据保持一致性,并在不再需要数据时将其删除,以释放存储空间。客户端可以使用TTL、租约和自动续约等机制来管理键值对的生命周期,从而实现数据的自动过期和删除。这对于配置管理、服务发现和分布式协调等场景非常有用。

4.4 ETCD的监听机制

Etcd提供了监听(Watch)机制,允许客户端订阅对数据存储的更改。当某个键值对在Etcd中发生更改时,已订阅的客户端会收到通知。这是实现分布式系统中的观察者模式的一种方式,可以用于实时监控和响应数据更改。

以下是Etcd监听机制的基本原理和使用方法:

  1. 创建Watcher:客户端通过Etcd提供的API创建一个Watcher对象,用于监听一个或多个键。Watch操作是一个持久性操作,它会保持连接并等待与已注册的键相关的事件。
  2. 监听事件类型:客户端可以指定要监听的事件类型,包括创建(create)、更新(update)、删除(delete)以及过期(expire)等。客户端可以选择监听所有事件类型或仅监听特定事件类型。
  3. 订阅键:客户端通过Watcher对象订阅一个或多个键。每个键可以有一个相关的Watcher,多个Watcher可以同时监听多个键。
  4. 处理事件:当某个键的值发生更改或与该键关联的事件发生时,Etcd将通知订阅了该键的Watcher。客户端可以收到事件通知,并执行相应的操作。
  5. 超时和错误处理:客户端应该处理可能的超时和错误情况,以确保Watcher操作的可靠性。例如,客户端可能需要重新连接或重新注册Watcher,以继续监听。

以下是Go中使用Etcd的监听机制的简单示例:

package main

import (
“context”
“fmt”
“go.etcd.io/etcd/clientv3”
)

func main() {
config := clientv3.Config{
Endpoints: []string{“http://localhost:2379”}, // Etcd集群地址
}

client, err := clientv3.New(config)
if err != nil {
fmt.Println(err)
return
}
defer client.Close()

ctx := context.Background()
watchChan := client.Watch(ctx, “myKey”) // 监听键 “myKey”

for watchResponse := range watchChan {
for _, event := range watchResponse.Events {
fmt.Printf(“Event Type: %s, Key: %s, Value: %s\n”, event.Type, event.Kv.Key, event.Kv.Value)
}
}
}

在此示例中,客户端创建一个Watcher对象来监听键"myKey",并在发生更改时打印相关的事件信息。

Etcd的监听机制对于构建实时监控、自动配置更新和协调分布式系统非常有用。客户端可以通过监听来保持对数据的实时了解,并根据数据的变化来触发相应的操作。

4.5 微服务CAP原则

参考1:微服务CAP原则
参考2:微服务中的CAP定律

微服务架构与CAP原则(ConsistencyAvailabilityPartition Tolerance)密切相关,CAP原则描述了分布式系统在面临网络分区(Partition)时如何在一致性(Consistency)和可用性(Availability)之间做出权衡。微服务架构通常也需要考虑CAP原则,以确保系统的可靠性和性能。三者不可能同时保证,最多只能满足其中的两者。

  1. Consistency(一致性):一致性要求分布式系统中的所有节点在同一时间点看到相同的数据。在微服务架构中,这意味着当一个微服务的某个实例修改了数据时,其他微服务的实例应该能够立即看到这一更改。维护一致性可能需要使用分布式事务或采用强一致性模型,但这可能会导致延迟和性能问题。
  2. Availability(可用性):可用性要求分布式系统在任何时间点都能够响应请求,即系统处于可用状态。在微服务架构中,可用性是至关重要的,因为微服务可能会随时处理请求。为了提高可用性,可以使用负载均衡、故障恢复机制和自动伸缩等技术。
  3. Partition Tolerance(分区容忍性):分区容忍性要求分布式系统在面临网络分区时仍然能够正常运行。网络分区可能导致某些节点无法与其他节点通信,因此分布式系统必须能够容忍这种情况。微服务架构通常要求在面临网络故障或分区时继续提供服务,因此分区容忍性是微服务架构的一个基本要求。

总的来说,数据存放的节点数越多,分区容忍性就越高,但是要复制更新的次数就越多,一致性就越难保证。同时为了保证一致性,更新所有节点数据所需要的时间就越长,那么可用性就会降低。

所以说,只能存在以下三种方案:

  1. 可用性、一致性(AC)
    要同时保证可用性和一致性,代表着某个节点数据更新之后,需要立即将结果通知给其他节点,并且要尽可能的快,这样才能及时响应保证可用性,这就对网络的稳定性要求非常高,但是实际情况下,网络很容易出现丢包等情况,并不是一个可靠的传输,如果需要避免这种问题,就只能将节点全部放在一起,但是这显然违背了分布式系统的概念,所以对于我们的分布式系统来说,很难接受。
  2. 一致性、分区容错性(CP)
    为了保证一致性,那么就得将某个节点的最新数据发送给其他节点,并且需要等到所有节点都得到数据才能进行响应,同时有了分区容错性,那么代表我们可以容忍网络的不可靠问题,所以就算网络出现卡顿,那么也必须等待所有节点完成数据同步,才能进行响应,因此就会导致服务在一段时间内完全失效,所以可用性是无法得到保证的。
  3. 可用性、分区容错性(AP)
    既然CP可能会导致一段时间内服务得不到任何响应,那么要保证可用性,就只能放弃节点之间数据的高度统一,也就是说可以在数据不统一的情况下,进行响应,因此就无法保证一致性了。虽然这样会导致拿不到最新的数据,但是只要数据同步操作在后台继续运行,一定能够在某一时刻完成所有节点数据的同步,那么就能实现最终一致性,所以AP实际上是最能接受的一种方案。

比如我们实现的Eureka集群,它使用的就是AP方案,Eureka各个节点都是平等的,少数节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka客户端在向某个Eureka服务端注册时如果发现连接失败,则会自动切换至其他节点。只要有一台Eureka服务器正常运行,那么就能保证服务可用(A),只不过查询到的信息可能不是最新的(C)。

4.6 Etcd的CAP原则是CP

参考1:etcd raft cap 理解

etcd的过期策略是基于每个键值对的TTL设置,而不是整个etcd存储系统的过期策略。在TTL时间内,可以通过更新键值对的值以及重置TTL,来延长该键值对在etcd中的存活时间。但一旦TTL到期,该键值对将被自动清理。

4.7 我现在想做一个对应的功能,就是我希望ETCD,一个服务多久没注册了,或者说它的服务是异常了,给我告警。通过注册中心的形式,这个你会怎么考虑呢

etcd它是有数据版本的概念的,为什么不使用数据版本的这种东西去做呢?

面试官提示:可以使用etcd的数据版本实现报警。

GPT回答:
使用etcd的数据版本(revision)来实现报警是一种有效的方式,因为它可以帮助监视数据的更改并及时采取相应的措施。etcd是一个高度可用的键值存储数据库,通常用于分布式系统的配置管理和服务发现。以下是一种简单的方法来使用etcd的数据版本来实现报警:

  1. 监控特定键的数据版本:首先,需要决定要监控的etcd键,并获取该键的初始数据版本。可以使用etcd的客户端库来执行此操作。假设要监控键/config/alert_threshold

// 导入 etcd 客户端库
import (
“context”
“github.com/coreos/etcd/clientv3”
)

// 创建 etcd 客户端
client, err := clientv3.New(clientv3.Config{
Endpoints: []string{“etcd-server-address”},
})

if err != nil {
// 处理错误
}

// 获取初始数据版本
response, err := client.Get(context.Background(), “/config/alert_threshold”)
if err != nil {
// 处理错误
}

initialRevision := response.Header.Revision

  1. 定期检查数据版本:在一个循环中,可以定期检查etcd键的数据版本是否发生了变化。可以使用Watch API来实现这一点:

// 在循环中定期检查数据版本
for {
response, err := client.Get(context.Background(), “/config/alert_threshold”)

if err != nil {
// 处理错误
}

currentRevision := response.Header.Revision

if currentRevision > initialRevision {
// 数据版本发生了变化,触发报警逻辑
// 可以发送警报通知,执行操作等
// 更新初始数据版本以便下一次检查
initialRevision = currentRevision
}

// 休眠一段时间后再次检查
time.Sleep(time.Second * 10)
}

  1. 报警逻辑:在检测到数据版本变化后,可以触发报警逻辑,例如发送警报通知、记录事件、执行自动化操作等。

这个示例中,我们使用etcd的数据版本来监视特定键的变化,并在变化时触发报警。这可以用于监控配置更改、服务状态等。请注意,此示例是一个简化的示例,实际场景中可能需要更多的错误处理和安全性措施。此外,确保etcd服务器配置安全,以免未经授权的访问。

4.8 怎么使用Etcd做分布式锁?

参考1:基于Etcd的分布式锁实现原理及方案

4.8.1 分布式锁的基本原理

分布式环境下,多台机器上多个进程对同一个共享资源(数据、文件等)进行操作,如果不做互斥,就有可能出现“余额扣成负数”,或者“商品超卖”的情况。为了解决这个问题,需要分布式锁服务。首先,来看一下分布式锁应该具备哪些条件。

  1. 互斥性:在任意时刻,对于同一个锁,只有一个客户端能持有,从而保证一个共享资源同一时间只能被一个客户端操作;
  2. 安全性:即不会形成死锁,当一个客户端在持有锁的期间崩溃而没有主动解锁的情况下,其持有的锁也能够被正确释放,并保证后续其它客户端能加锁;
  3. 可用性:当提供锁服务的节点发生宕机等不可恢复性故障时,“热备” 节点能够接替故障的节点继续提供服务,并保证自身持有的数据与故障节点一致。
  4. 对称性:对于任意一个锁,其加锁和解锁必须是同一个客户端,即客户端A不能把客户端B加的锁给解了。
4.8.2 Etcd 实现分布式锁的基础

Etcd的高可用性、强一致性不必多说,前面章节中已经阐明,本节主要介绍Etcd支持的以下机制:Watch机制、Lease机制、Revision机制和Prefix机制,正是这些机制赋予了Etcd实现分布式锁的能力。

  1. Lease机制:即租约机制(TTL,TimeToLive),Etcd可以为存储的Key-Value对设置租约,当租约到期,Key-Value将失效删除;同时也支持续约,通过客户端可以在租约到期之前续约,以避免Key-Value对过期失效。Lease机制可以保证分布式锁的安全性,为锁对应的Key配置租约,即使锁的持有者因故障而不能主动释放锁,锁也会因租约到期而自动释放。
  2. Revision机制:每个Key带有一个Revision号,每进行一次事务便加一,因此它是全局唯一的,如初始值为0,进行一次put(key,value)KeyRevision变为1,同样的操作,再进行一次,Revision变为2;换成key1进行put(key1,value)操作,Revision将变为3;这种机制有一个作用:通过Revision的大小就可以知道写操作的顺序。在实现分布式锁时,多个客户端同时抢锁,根据Revision号大小依次获得锁,可以避免“羊群效应”(也称“惊群效应”),实现公平锁。
  3. Prefix机制:即前缀机制,也称目录机制,例如,一个名为/mylock的锁,两个争抢它的客户端进行写操作,实际写入的Key分别为:key1=“/mylock/UUID1”key2=“/mylock/UUID2”,其中,UUID表示全局唯一的ID,确保两个Key的唯一性。很显然,写操作都会成功,但返回的Revision不一样,那么,如何判断谁获得了锁呢?通过前缀“/mylock”查询,返回包含两个Key-Value对的Key-Value列表,同时也包含它们的Revision,通过Revision大小,客户端可以判断自己是否获得锁,如果抢锁失败,则等待锁释放(对应的Key被删除或者租约过期),然后再判断自己是否可以获得锁。
  4. Watch机制:即监听机制,Watch机制支持监听某个固定的Key,也支持监听一个范围(前缀机制),当被监听的Key或范围发生变化,客户端将收到通知;在实现分布式锁时,如果抢锁失败,可通过Prefix机制返回的Key-Value列表获得Revision比自己小且相差最小的Key(称为Pre-Key),对Pre-Key进行监听,因为只有它释放锁,自己才能获得锁,如果监听到Pre-KeyDELETE事件,则说明Pre-Key已经释放,自己已经持有锁。
4.8.3 Etcd 实现分布式锁
4.8.3.1 基于Etcd的分布式锁业务流程

下面描述了使用Etcd实现分布式锁的业务流程,假设对某个共享资源设置的锁名为:/anyrtc/mylock

  1. 准备:客户端连接Etcd,以/anyrtc/mylock为前缀创建全局唯一的Key,假设第一个客户端对应的Key=“/anyrtc/mylock/UUID1”,第二个为Key=“/anyrtc/mylock/UUID2”;客户端分别为自己的Key创建租约Lease,租约的长度根据业务耗时确定,假设为15s。
  2. 创建定时任务作为租约的“心跳”:在一个客户端持有锁期间,其它客户端只能等待,为了避免等待期间租约失效,客户端需创建一个定时任务作为“心跳”进行续约。此外,如果持有锁期间客户端崩溃,心跳停止,Key将因租约到期而被删除,从而锁释放,避免死锁。
  3. 客户端将自己全局唯一的Key写入Etcd:进行Put操作,将步骤1中创建的Key绑定租约写入Etcd,根据EtcdRevision机制,假设两个客户端Put操作返回的Revision分别为1、2,客户端需记录Revision用以接下来判断自己是否获得锁。
  4. 客户端判断是否获得锁:客户端以前缀/anyrtc/mylock读取Key-Value列表(Key-Value中带有Key对应的Revision),判断自己KeyRevision是否为当前列表中最小的,如果是则认为获得锁;否则监听列表中前一个Revision比自己小的Key的删除事件,一旦监听到删除事件或者因租约失效而删除的事件,则自己获得锁。
  5. 执行业务:获得锁后,操作共享资源,执行业务代码。
  6. 释放锁:完成业务流程后,删除对应的Key释放锁。
4.8.3.2 基于Etcd的分布式锁的原理图

根据业务流程,基于Etcd的分布式锁示意图如下:
在这里插入图片描述

4.9 Etcd常用的几个接口?

  1. gRPCAPIEtcd的主要接口是gRPC接口,它提供了对Etcd的完整功能访问。通过gRPC接口,可以执行诸如设置键值对、获取键值对、触发事务操作等操作。gRPC是一种高性能的远程过程调用(RPC)框架,可以用多种编程语言来使用EtcdAPI
  2. HTTP/gRPC代理接口Etcd还提供了HTTPgRPC代理接口,允许通过HTTPgRPC请求来访问Etcd。这使得使用EtcdRESTfulAPI变得更加方便,特别是在不支持gRPC的环境中。
  3. etcdctl命令行工具Etcd还附带了一个命令行工具称为etcdctl,它提供了一个命令行界面,允许与Etcd集群交互。可以使用etcdctl来设置、获取、删除键值对,以及执行其他管理和查询操作。
  4. WatchAPIEtcdWatchAPI允许监视键值对的更改。可以设置观察器以接收有关特定键的更改通知,这对于实时应用程序和配置管理非常有用。
  5. 事务APIEtcd支持事务操作,可以使用事务API执行一系列操作,要么全部成功,要么全部失败。这确保了多个操作的一致性。
  6. 健康检查接口Etcd提供了健康检查接口,允许检查Etcd集群的运行状况和健康状态。这对于监控和运维非常有用。
  7. 授权和认证接口Etcd支持基于TLS的安全通信,并提供了授权和认证接口,可以配置访问控制策略,以限制对Etcd数据的访问。

这些接口组合在一起,使Etcd成为一个强大的分布式数据存储系统,适用于多种用例,包括服务发现、配置管理、分布式锁、选举等。不同的接口提供了不同级别的灵活性和功能,以满足不同应用程序的需求。

※4.10 Etcd的过期策略是什么?

Etcd的过期策略基于TTLTime-To-Live,生存时间)机制,它允许为存储在Etcd中的键值对设置生存时间,当键值对的生存时间到期时,它将自动从Etcd中删除。这个机制是Etcd中处理过期的主要策略。

具体来说,当使用Etcdput操作来设置一个键值对时,可以为该键值对指定TTL值,如下所示:

etcdctl put key value --ttl 60

上述命令将keyvalue存储在Etcd中,并设置了60秒的TTL。当60秒钟过去后,如果不更新该键值对的TTL或删除该键值对,Etcd将自动将其删除。

过期策略的主要用途之一是在配置管理中,以确保配置数据在一定时间后自动失效,从而触发配置的刷新或重新加载。过期键值对的删除是Etcd自身的维护任务,不需要用户手动干预。

需要注意的是,EtcdTTL机制仅适用于具有TTL设置的键值对,如果未设置TTL,键值对将永久保存在Etcd中。此外,Etcd可以配置不同的自动清理策略来处理过期数据的清理,例如自动清理已过期的键值对以释放磁盘空间。

总结:
Etcd的过期策略基于TTL机制,允许设置键值对的生存时间,当生存时间到期时,Etcd将自动删除这些键值对。这对于自动数据管理和配置刷新非常有用。

4.11 Etcd中的数据格式是什么样的?

参考:etcdv3与etcdv2特性比较

etcd存储的数据是一个Key-Value格式的存储,etcdv2key是一个递归的文件目录结构,在v3版本中的键改成了扁平化的数据结构,更加简洁,v3中支持前缀查询,在etcdctl get key时可以加上前缀查询选项--prefix,从而达到v2的目录结构查询效果。

5 go-zero

5.1 go-zero框架的架构是什么样的?

在这里插入图片描述

5.2 go-zero的api层主要的作用是什么?

API里有grpc-gateway,可以将HTTP协议转为GRPC,同时还有鉴权,加解密,其他的和RPC端一致。

5.3 go-zero的负载均衡

参考1:负载均衡

负载均衡:
go-zero使用的负载均衡是P2C(Power of Two Choices)P2C(Power of Two Choices)算法是一种基于随机化的负载均衡算法,由Jeff DeanLuiz Andre Barroso在2001年提出。

P2C算法是一种改进的随机算法,它可以避免最劣选择和负载不均衡的情况。P2C算法的核心思想是:从所有可用节点中随机选择两个节点,然后根据这两个节点的负载情况选择一个负载较小的节点。这样做的好处在于,如果只随机选择一个节点,可能会选择到负载较高的节点,从而导致负载不均衡;而选择两个节点,则可以进行比较,从而避免最劣选择。

P2C算法的实现步骤如下:

  1. 将所有可用节点按照负载大小排序,从小到大排列。
  2. 随机选择两个节点。
  3. 选择两个节点中负载较小的节点,作为负载均衡器选择的节点。 P2C算法的优点在于,它可以在保证负载均衡的前提下,选择负载更小的节点,从而提高系统的性能和可靠性。此外,P2C算法的实现简单,不需要太多的计算和存储资源,因此在实际应用中被广泛采用。

其他常见负载均衡算法:

  1. 基于轮询​:基于轮询的负载均衡算法是一种简单的负载均衡算法,将请求依次分发给每个节点,循环重复这个过程。这种算法适用于系统中的节点处理能力相同的情况,但是在实际应用中,由于节点的处理能力可能不同,所以需要进行改进。
  2. 基于权重:基于权重的负载均衡算法是一种考虑节点处理能力的算法,将请求分配给具有更高权重的节点。权重可以根据节点的处理能力进行设置。例如,处理能力更强的节点可以设置更高的权重,从而处理更多的请求。
  3. 基于最少连接数:基于最少连接数的负载均衡算法是一种考虑节点负载情况的算法,将请求分配给连接数最少的节点。这种算法适用于连接时间较长的应用场景,因为连接时间较长的节点可能会影响其他节点的连接数。
  4. 基于IP(HASH)散列:基于IP散列的负载均衡算法是一种根据请求的源IP地址进行分配的算法。它将请求的源IP地址进行散列,然后根据散列结果将请求分配给相应的节点。这种算法可以确保来自同一IP地址的请求被分配到同一节点上,从而保持会话的一致性

5.4 go-zero的网关

参考1:网关
go-zero使用的网关是gRPC gatewaygo-zero中的gRPC网关是一个HTTP服务器,它将RESTful API转换为gRPC请求,然后将gRPC响应转换为RESTful API。大致流程如下:

  1. proto文件中解析出gRPC服务的定义。
  2. 从 配置文件中解析出gRPC服务的HTTP映射规则。
  3. 根据gRPC服务的定义和HTTP映射规则,生成gRPC服务的HTTP处理器。
  4. 启动HTTP服务器,处理 HTTP请求。
  5. HTTP请求转换为gRPC请求。
  6. gRPC响应转换为HTTP响应。
  7. 返回HTTP响应。

5.5 go-zero断路器

参考1:断路器

断路器又叫熔断器,是一种保护机制,用于保护服务调用链路中的服务不被过多的请求压垮。当服务调用链路中的某个服务出现异常时,断路器会将该服务的调用请求拒绝,从而保护服务调用链路中的其他服务不被压垮。

比较知名的熔断器算法有HystrixSentinel,它们都是通过统计服务调用的成功率和响应时间来判断服务是否可用,从而实现熔断的功能。

go-zero内置了熔断器组件breaker.Breaker(自研的),go-zero中采用滑动窗口来进行数据采集,目前是以10s为一个窗口,单个窗口有40个桶,然后将窗口内采集的数据使用google sre算法计算是否开启熔断,详情可参考https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101

brreaker.Breaker中,提供了DoDoWithAcceptableDoWithFallbackDoWithFallbackAcceptable四个方法,分别对应不同的场景。 开发者可以通过breaker直接调用这些方法,或者创建一个breaker实例来调用,两种方法均可,直接调用其实本质上也会以name作为唯一key去获取/创建一个breaker实例。

  1. Do方法:默认按照错误率来判断服务是否可用,不支持指标自定义,也不支持错误回调。
  2. DoWithAcceptable:支持自定义的采集指标,可以自主控制哪些情况是可以接受,哪些情况是需要加入熔断指标采集窗口的。
  3. DoWithFallback:默认采用错误率来判断服务是否可用,不支持指标自定义,但是支持熔断回调。
  4. DoWithFallbackAcceptable:支持采集指标自定义,也支持熔断回调。

以上方法都是通过name来获取/创建一个breaker实例,即熔断器名称相同的同属于一个熔断器控制,如果需要自定义breaker的配置,可以通过NewBreaker方法来创建一个breaker实例,通过实例可以精确控制具体情况是放过还是拒绝。

go-zero中,开发者不需要对请求单独进行熔断处理,该功能已经集成到了框架中,因此开发者无需关系。

  1. HTTP以请求方法+路由作为统计维度,用HTTP状态码500作为错误采集指标进行统计,详情可参考:breakerhandler.go
  2. gRPC客户端以RPC方法名作为统计维度,用gRPC的错误码为:codes.DeadlineExceededcodes.Internalcodes.Unavailablecodes.DataLosscodes.Unimplemented作为错误采集指标进行统计,详情可参考:breakerinterceptor.go
  3. gRPC服务端以RPC方法名称作为统计维度,用gRPC的错误作为错误采集指标进行统计,详情可参考:breakerinterceptor.go

5.6 go-zero限流

参考1:限流(并发控制)

限流器是一种服务治理能力,用于限制服务的并发调用量,以保护服务的稳定性。限流分为restgrpc

限流一般有单节点限流集群限流(将限流数值对集群节点数求平均值,其本质还是单节点限流)分布式限流

限流,倒不如说是并发控制更贴切。

*********************

5.7 在这个项目主要负责的内容?

这一块还需要再整理逻辑,细化具体做了哪些。

5.8 处理的数据量大吗?一般是多少?

– 208服务器每秒最多,目前出现在 2023-10-09 14:09:54,条数是1813

SELECT formatDateTime(capture_time,‘%Y-%m-%d %H:%M:%S’) dates,COUNT(id) nums FROM http_capture GROUP BY dates ORDER BY nums DESC;

5.9 数据量比较大的话该如何处理?

使用单个asynq来处理其实不对,如果使用asynq来进行数据的分发的话在数据量比较大的情况下,Redis会塞满数据,导致瘫痪。

应该利用kafka的消费者组的特点,这样可以充分利用kafka单个消费者组多个消费者并发处理的性能。

5.10 为什么使用kafka而不是RabbitMQ?

适用于实时大数据,并且可以快速扩展,并发处理(也就是增加多个分区,同时再增加多个消费者)。

6 分布式

6.1 分布式事务

参考1:go-zero学习 第六章 分布式事务dtm
参考2:DTM开源项目文档:官方文档

seata-golang的分布式事务。
参考3:seata-golang 接入指南
参考4:官网
参考5:【微服务架构】分布式事务

分布式事务是一种涉及多个独立组件或服务的事务操作,需要确保在不同节点上的操作要么全部成功,要么全部失败,以维护数据的一致性。

常见事务模式:

  • msg:二阶段消息,适合不需要回滚的全局事务。
  • saga:适合需要支持回滚的全局事务。
  • tcc:适合一致性要求较高的全局事务。
  • xa:适合性能要求不高,没有行锁争抢的全局事务。
6.1.1 二阶段消息事务

二阶段消息事务(Two-Phase Commit,2PC)是一种用于实现分布式事务的协议,它通常用于确保在不同节点上的数据库或服务上的事务操作的一致性。适合不需要回滚的全局事务。
二阶段消息事务通常包含以下两个阶段:

  1. 准备阶段(Prepare Phase): 在这个阶段,事务协调者(通常是分布式系统中的一个中心节点)向所有参与者(各个节点或服务)发送一个准备请求。每个参与者会执行以下操作:
  2. 检查自己是否能够成功完成该事务,包括检查事务操作是否合法和是否有足够的资源。
  3. 如果参与者准备好了,它会向协调者发送一个“同意”消息。
  4. 如果参与者不能准备好,它会向协调者发送一个“中止”消息。
  5. 提交阶段(Commit Phase): 如果所有参与者都在准备阶段发送了“同意”消息,那么协调者会向所有参与者发送一个提交请求。每个参与者在接收到提交请求后,会执行以下操作:
  6. 执行事务的实际操作,将数据持久化或执行其他必要的操作。
  7. 一旦操作成功,它会向协调者发送一个“已提交”消息,表示它已成功提交事务。
  8. 如果操作失败,参与者会向协调者发送一个“中止”消息,表示事务无法提交。
  9. 完成和恢复: 一旦协调者收到所有参与者的“已提交”消息,它会将事务标记为已提交,并通知客户端事务已成功完成。如果有任何一个参与者发送了“中止”消息或在规定时间内没有响应,协调者会将事务标记为已中止,并通知客户端事务失败。

2PC的关键特点是在准备阶段引入了一个同意/中止机制,以确保所有参与者都可以成功完成操作。然而,2PC也存在一些问题,如协调者单点故障、性能瓶颈和阻塞等问题。因此,一些分布式系统会选择使用三阶段提交(Three-Phase Commit,3PC)等更复杂但更可靠的分布式事务协议来解决这些问题。

6.1.2 saga事务

Saga是一种通过将大型事务拆分成多个小事务并使用补偿操作来维护数据一致性的方式。Saga模式的主要目标是最小化分布式事务的范围,以提高性能和可伸缩性,并降低事务中断的风险。适合需要支持回滚的全局事务。

Saga事务包含以下关键特点和概念:

  1. 拆分事务Saga将大型事务拆分为多个小事务,每个小事务涵盖了系统中的一个服务或组件。这些小事务可以在不同的节点上独立执行,从而减小了分布式事务的范围。
  2. 补偿操作:每个小事务都有一个关联的补偿操作,用于撤销或修复该事务的效果。补偿操作通常是与原事务操作相反的操作。如果某个小事务失败,Saga会逆序执行之前成功的小事务的补偿操作,以维护数据一致性。
  3. 事务协调Saga中的事务协调器(Transaction Coordinator)负责协调各个小事务的执行和补偿。它跟踪事务的状态,并在必要时触发补偿操作。
  4. 局部数据一致性Saga模式接受局部数据不一致性,即在某个小事务成功后,该事务的数据可能在稍后的补偿操作中被撤销或修改。这是Saga的一种权衡,旨在提高性能和可伸缩性。
  5. 长时间执行Saga事务可以在较长的时间范围内执行,因为它们可以在多个步骤之间等待用户或外部事件的响应。这使得Saga适用于需要长时间执行的业务流程。

Saga模式的一个重要优势是允许系统部分失败而不影响整体一致性。如果某个小事务失败,系统可以继续运行并通过执行补偿操作来纠正问题。这降低了系统中断的风险,尤其在大规模和复杂的分布式系统中。

需要注意的是,实施Saga模式可能会引入复杂性,例如管理补偿操作和确保系统可恢复性。因此,在选择使用Saga模式时,需要权衡其优势和复杂性,以确保它适合特定的应用场景和系统架构。

6.1.3 tcc事务

TCC(Try-Confirm-Cancel)是一种分布式事务模式,TCC事务将大事务拆分成三个阶段:尝试(Try)、确认(Confirm)和取消(Cancel)来实现分布式事务,以确保在不同节点上的操作要么全部成功,要么全部失败。特别适用于需要严格保证数据一致性的分布式系统。

以下是TCC事务模式的关键概念和步骤:

  1. Try阶段(尝试阶段): 在Try阶段,事务协调器(Transaction Coordinator)会向所有参与者(各个节点或服务)发送一个尝试请求,询问它们是否愿意执行事务。每个参与者会执行以下操作:
  • 尝试执行与该事务相关的操作,但不会将其结果持久化。这个阶段用于检查是否满足执行事务的前提条件,例如检查资源是否足够、锁定资源等。
  1. Confirm阶段(确认阶段): 如果所有参与者在Try阶段成功执行了相关操作并确认可以执行事务,那么事务协调器会向所有参与者发送确认请求。每个参与者会执行以下操作:
  • 确认之前Try阶段执行的操作,并将结果持久化。这个阶段将确保事务操作的执行。
  1. Cancel阶段(取消阶段): 如果在Try阶段有任何参与者失败或拒绝执行事务,或者在Confirm阶段的某些参与者失败,那么事务协调器会向所有参与者发送取消请求。每个参与者会执行以下操作:
  • 撤销或回滚之前Try阶段执行的操作,以确保事务操作的撤销。

TCC模式的关键特点包括:

  1. TCC模式将事务的操作拆分为TryConfirmCancel阶段,以确保在不同节点上的操作具有原子性。
  2. TCC要求参与者实现TryConfirmCancel操作,以处理不同阶段的事务状态。
  3. TCC允许在Cancel阶段回滚事务操作,从而确保数据一致性,即使在部分节点失败的情况下也能恢复到一致状态。

需要注意的是,TCC模式引入了额外的复杂性,包括事务协调、状态管理和异常处理等方面的问题。因此,TCC模式通常适用于需要强一致性和精确控制的分布式系统场景,但也需要谨慎考虑其实施和维护的复杂性。一些分布式事务管理框架(如Seata)提供了TCC模式的支持,以简化实施过程。

6.1.4 xa事务

适合性能要求不高,没有行锁争抢的全局事务。

XA(eXtended Architecture)事务是一种用于管理分布式事务的协议,它允许多个资源管理器(通常是数据库或消息队列)协同工作以确保分布式事务的原子性、一致性、隔离性和持久性(ACID属性)。XA协议提供了两阶段提交(Two-Phase Commit,2PC)协议的标准化实现,但它也可以扩展到支持三阶段提交(Three-Phase Commit,3PC)以提高可靠性。

以下是XA事务的主要概念和步骤:

  1. 事务管理器(Transaction Manager):事务的协调和管理是由事务管理器来完成的。它是分布式事务的核心组件,负责协调多个资源管理器和确保事务的一致性。
  2. 资源管理器(Resource Manager):资源管理器是与各种资源(如数据库、消息队列、文件系统等)交互的组件。每个资源管理器负责事务的处理和数据持久性。
  3. 全局事务(Global Transaction):全局事务是由多个资源管理器参与的分布式事务。它可以包括一个或多个分支事务。
  4. 分支事务(Branch Transaction):分支事务是全局事务中的一个局部操作,通常对应于一个资源管理器上的事务操作。

XA事务的典型流程如下:

  1. 准备阶段(Prepare Phase):在此阶段,全局事务协调器向每个资源管理器发送准备请求。资源管理器会将事务操作记录在一个准备日志中,但不会对其进行实际提交。如果所有资源管理器都成功地准备好了,协调器会进入下一阶段。
  2. 提交阶段(Commit Phase):在此阶段,全局事务协调器向每个资源管理器发送提交请求。资源管理器会根据准备阶段的日志来执行实际的提交操作。如果任何一个资源管理器在此阶段失败,协调器会发送回滚请求,以确保事务的原子性。
  3. 回滚阶段(Rollback Phase,可选):如果在提交阶段发生错误,全局事务协调器会向每个资源管理器发送回滚请求,以撤销之前的事务操作。

XA事务的主要优点是它提供了一种标准化的分布式事务管理方法,确保了ACID属性。然而,XA事务也有一些缺点,包括性能开销、对资源管理器的依赖性和可能的单点故障问题。因此,使用XA事务需要仔细考虑系统的需求和复杂性。在某些情况下,更轻量级的分布式事务模式(如TCCSaga)可能更适合。

6.1.5 分布式事务和消息中间件应用场景区别

分布式事务主要是订单、商城,需要立即结束订单、库存、支付等,否则业务无法继续,耦合度较高,其中一个环节失败,则整个业务失败。

消息中间件是解耦,各管各的。

6.2 分布式锁

参考1:微服务中的分布式锁方案
参考2:一文彻底弄清楚分布式锁
参考3:分布式锁有哪些解决方案

6.2.1 基于数据库实现分布式锁

基于数据库的分布式锁是通过在数据库中创建一个特殊的记录或行来表示锁的状态,从而确保在任何给定时刻只有一个客户端能够获得该锁,以执行特定的操作。这可以用于避免多个客户端同时修改相同的数据或执行相同的任务,从而确保数据的一致性和可靠性。

基于数据库的分布式锁的一般步骤和一些考虑因素:

  1. 选择数据库引擎: 选择一个适合应用程序的数据库引擎。常见的选择包括MySQLPostgreSQLRedis等。不同的数据库引擎可能提供不同的特性和性能。
  2. 创建锁记录: 在选定的数据库中创建一个专门用于存储锁状态的表格或键值对。这个表格或键值对应该包含以下信息:
  • 锁的名称或标识符
  • 锁的持有者(客户端标识)
  • 锁的到期时间(可选)
  1. 获取锁: 当客户端需要获取锁时,它会尝试在锁表中插入一条记录,或者更新一个已存在的记录,来表示它正在持有该锁。这可以使用数据库事务来保证原子性操作。
  2. 释放锁: 当客户端完成任务或需要释放锁时,它会从锁表中删除相关记录或将锁的持有者字段重置为空。同样,使用数据库事务来确保原子性操作。
  3. 设置锁的超时(可选): 有时候,为了避免死锁或客户端长时间持有锁而无响应,可以为锁设置一个超时时间。客户端需要在规定时间内完成任务并释放锁,否则锁将自动过期。
  4. 处理竞争和失败情况: 当多个客户端尝试获取同一个锁时,可能会出现竞争情况。需要编写逻辑来处理这些情况,通常使用数据库的事务隔离级别来确保数据的一致性。
  5. 监控和故障恢复: 实施监控来跟踪锁的使用情况,以及故障恢复策略,以处理可能的锁冲突或数据库故障。

注意:
基于数据库的分布式锁可以工作,但也需要谨慎考虑性能和可伸缩性问题。锁表可能成为瓶颈,尤其是在高并发环境中。在某些情况下,可能需要考虑使用更高级的分布式锁管理工具,例如ZooKeeperetcdRedis的分布式锁等,以减轻数据库的负载。

6.2.2 基于Redis实现分布式锁

基于Redis的分布式锁是利用Redis作为中心化的锁管理器。Redis是一个高性能的内存数据库,具有原子操作和持久性的特性,适用于实现分布式锁。以下是如何创建基于Redis的分布式锁的一般步骤:

  1. 选择一个唯一标识符:为了创建锁,需要选择一个唯一的标识符,通常是一个字符串,用于标识锁的名称。
  2. 尝试获取锁:客户端尝试在Redis中设置一个特定的键值对,其中键是锁的名称,值是客户端的标识符(通常是一个唯一的标识符,如UUID)。这个设置操作需要使用RedisSETNX(Set if Not eXists)命令,以确保只有一个客户端能够成功设置锁。如果客户端成功设置了锁,表示获取锁成功。

SET lock_name client_identifier NX PX lock_timeout

各命令含义:

  • lock_name: 锁的名称
  • client_identifier: 客户端的唯一标识符
  • NX: 表示仅在键不存在时设置锁
  • PX: 设置锁的超时时间(毫秒)
  1. 处理竞争和超时情况: 如果多个客户端同时尝试获取锁,只有一个客户端将成功,其他客户端将获取失败。可以使用轮询或等待一段时间后重新尝试获取锁,但要小心避免死锁。
  2. 释放锁: 当客户端完成任务或需要释放锁时,它可以使用Redis的DEL命令来删除锁的键,以释放锁。

DEL lock_name

  1. 设置锁的超时时间(可选): 可以为锁设置一个自动过期时间,以确保即使客户端崩溃或意外终止,锁也会在一定时间后自动释放。这可以使用EXPIRE命令或在设置锁时使用PX参数来实现。

EXPIRE lock_name lock_timeout

  1. 处理异常和故障情况: 考虑如何处理客户端崩溃、锁的超时以及其他异常情况。可以使用监控和定期检查锁是否过期来处理这些情况。

基于Redis的分布式锁是一种简单而有效的方式来实现分布式锁,但也需要小心处理竞争和故障情况。还需要仔细选择锁的名称以及设置超时时间,以适应应用程序需求。请注意,虽然Redis是一个快速的内存数据库,但要确保Redis服务器的高可用性和稳定性,以防止锁的单点故障。

6.2.3 基于zookeeper实现分布式锁

基于ZooKeeper的分布式锁是一种强大且高度可靠的分布式锁实现方法。ZooKeeper是一个分布式协调服务,提供了分布式锁所需的原语。以下是如何创建基于ZooKeeper的分布式锁的一般步骤:

  1. 创建ZooKeeper连接:客户端需要首先创建到ZooKeeper集群的连接,通常使用ZooKeeper客户端库来实现。这个连接将用于创建锁节点以及监听锁的释放。
  2. 创建锁节点:每个客户端尝试获取锁时,都在ZooKeeper中创建一个独立的有序临时节点。节点的路径通常包含锁的名称。
  3. 获取锁:客户端通过在ZooKeeper上创建一个有序的临时节点来表示它想要获取锁。然后,它检查自己创建的节点是否是当前锁路径下最小的节点。如果是,表示客户端获得了锁。
  4. 处理竞争和等待:如果多个客户端尝试获取同一个锁,只有一个客户端将成功,其他客户端将进入等待状态。等待的客户端可以监听前一个节点的删除事件,一旦前一个节点被删除,它们会再次检查是否是最小节点。这种方式确保了锁的公平性。
  5. 释放锁:当客户端完成任务或需要释放锁时,它只需删除自己创建的节点。这将触发ZooKeeper的事件通知,通知下一个等待的客户端可以尝试获取锁。
  6. 处理异常和故障情况ZooKeeper提供了强大的分布式协调功能,可以处理各种异常和故障情况,包括客户端崩溃、网络问题等。可以使用ZooKeeper的会话超时机制来检测客户端连接问题,并确保锁的可靠性。
  7. 设置锁的超时时间(可选):类似于基于Redis的分布式锁,可以为锁设置一个超时时间,以防止某个客户端长时间持有锁。

基于ZooKeeper的分布式锁是一种可靠和高度分布式的锁实现方法,适用于需要强一致性和高可用性的分布式系统。然而,使用ZooKeeper也需要更多的配置和维护工作,因此要确保ZooKeeper集群的稳定性和性能。此外,要考虑锁的公平性,以避免某个客户端长时间持有锁,阻塞其他客户端。

6.2.4 三种实现方式的区别
  1. 底层技术:
  • 基于数据库的分布式锁:使用关系型数据库作为底层存储来实现锁,通常使用事务来确保锁的原子性。
  • 基于Redis的分布式锁:使用Redis内存数据库作为底层存储,利用Redis的原子性操作来实现锁。
  • 基于ZooKeeper的分布式锁:使用ZooKeeper分布式协调服务作为底层存储,通过ZooKeeper的节点操作和监听来实现锁。
  1. 性能和延迟:
  • 基于数据库的分布式锁:数据库通常不如内存数据库RedisZooKeeper快速,可能会引入较大的延迟。
  • 基于Redis的分布式锁Redis是内存数据库,速度非常快,适合高性能的应用场景。
  • 基于ZooKeeper的分布式锁ZooKeeper通常比数据库慢,但比数据库快。
  1. 可用性和复杂性:
  • 基于数据库的分布式锁:可以通过数据库的复制和备份来提高可用性,但配置和管理数据库可能更复杂。
  • 基于Redis的分布式锁Redis通常具有高可用性和容错性,但需要定期备份和监控。
  • 基于ZooKeeper的分布式锁ZooKeeper专注于分布式协调,通常具有高可用性和一致性,并提供了复杂的ZooKeeper集群配置和管理。
  1. 锁的粒度和管理:
  • 基于数据库的分布式锁:可以实现细粒度的锁,例如行级锁或表级锁,但需要额外的逻辑来管理锁。
  • 基于Redis的分布式锁Redis锁通常是全局性的,只有一个锁的实例,因此适合用于全局资源的锁定。
  • 基于ZooKeeper的分布式锁ZooKeeper锁通常是全局性的,但可以更容易地管理多个锁。
  1. 故障恢复:
  • 基于数据库的分布式锁:需要额外的机制来处理数据库故障和恢复。
  • 基于Redis的分布式锁Redis具有持久性选项,可以配置为在故障后自动恢复锁状态。
  • 基于ZooKeeper的分布式锁ZooKeeper通常具有高可用性和故障恢复功能。

基于Redis和基于ZooKeeper的分布式锁通常更适合需要高性能和高可用性的场景,而基于数据库的分布式锁可能更适合需要简化的场景或已经在使用数据库的应用程序。

6.2.5 基于Etcd实现分布式锁

参考 4.8 怎么使用Etcd做分布式锁?

6.2.6 zookeeper和Redis分别实现分布式锁,它主要的区别在哪里?

ZooKeeper分布式锁:

  1. 一致性ZooKeeper提供了强一致性的特性,这是其设计的核心之一。在分布式锁的实现中,ZooKeeper会保证锁的获取和释放的顺序,从而确保分布式环境中的一致性。
  2. 临时有序节点ZooKeeper的分布式锁通常通过创建临时有序节点来实现。每个客户端在ZooKeeper上创建一个唯一的节点,并带有序号,最终形成一个有序的节点队列。锁的获取就是判断自己的节点是否是队列中最小的节点。
  3. 阻塞等待:如果某个客户端获取锁失败,它会监听前一个节点的变化,一旦前一个节点释放了锁,它就有机会再次尝试获取锁。

Redis分布式锁:

  1. 基于SETNX命令Redis分布式锁通常使用SETNX(SET if Not eXists)命令实现。一个客户端尝试通过SETNXRedis中设置一个键值对,如果设置成功,表示获取锁成功;否则,表示锁已经被其他客户端持有。
  2. 超时机制: 为了防止死锁,可以为锁设置一个过期时间,确保即使获取锁的客户端异常退出,锁也会在一定时间后自动释放。
  3. 非阻塞Redis分布式锁是非阻塞的,如果获取锁失败,客户端可以选择重试或者放弃。

区别和选择:

  1. 一致性ZooKeeper提供了强一致性,适合需要强一致性保证的场景。Redis分布式锁则是最终一致性,适合一些对一致性要求相对较低的场景。
  2. 性能Redis分布式锁通常比ZooKeeper实现更为简单,性能也可能更好。如果应用场景对性能要求较高,而且可以接受最终一致性,Redis分布式锁可能是一个更轻量级的选择。
  3. 功能特性ZooKeeper提供了更多的分布式协调服务,而不仅仅是分布式锁。如果项目中需要其他分布式协调服务,ZooKeeper可能更适合。

在选择分布式锁方案时,需要根据具体的场景和需求权衡一致性、性能和功能特性。

6.3 分布式缓存

参考:搞定分布式系列:分布式缓存

分布式缓存是一种用于提高应用程序性能和可伸缩性的技术,它将数据存储在多个节点上,以便快速访问和减轻后端存储的负载。分布式缓存通常位于应用程序和后端数据存储之间,可以大大减少对数据库或其他数据存储系统的访问频率,从而提高响应时间和降低系统负载。

分布式缓存的主要特点:

  1. 高性能:分布式缓存通常使用内存存储数据,因此能够提供快速的读取和写入操作。这使得它们非常适合存储频繁访问的数据,例如数据库查询结果、API响应等。
  2. 可伸缩性:分布式缓存可以水平扩展,通过添加更多的缓存节点来处理更大的负载。
  3. 数据一致性:一些分布式缓存提供强一致性,确保缓存中的数据与后端存储中的数据保持一致。其他分布式缓存可能提供最终一致性,允许一定程度的数据延迟。
  4. 缓存失效策略:分布式缓存通常支持设置数据的过期时间或失效策略,以确保缓存中的数据不会过时。

分布式缓存的常见用途:

  1. 减轻数据库负载:分布式缓存可以缓存频繁访问的数据库查询结果,从而减轻数据库服务器的负载,提高数据库的响应速度。
  2. 提高响应速度:将静态内容、模板、页面片段等缓存在分布式缓存中,可以显著提高网站或应用程序的响应速度。
  3. 缓存API响应:缓存API响应数据,以减少对外部服务的请求,降低响应时间,并减轻外部服务的负载。
  4. 分布式会话管理:在分布式系统中,可以使用缓存来存储会话数据,确保用户在多个服务器之间的状态共享。
  5. 热门数据存储:存储热门、频繁访问的数据,例如电子商务网站的产品列表或社交媒体的热门帖子,以减轻后端服务器的负担。

常见的分布式缓存系统包括:

  • Redis:一个高性能的内存缓存和数据存储系统,支持多种数据结构,包括字符串、列表、哈希等。它也可以用作消息队列和分布式锁。
  • Memcached:一个简单而高效的内存缓存系统,用于存储键值对数据。
  • Ehcache:一个Java开发

分布式缓存的具体实现:
实现分布式缓存涉及多个方面,包括选择合适的缓存系统、配置和管理缓存集群、缓存数据的存储和更新、缓存失效策略、数据一致性等。下面是一个具体的实现分布式缓存的步骤:

  1. 选择缓存系统:首先,选择适合应用程序需求的缓存系统。常见的分布式缓存系统包括RedisMemcachedEhcache等。每个系统都有自己的特点和适用场景。
  2. 设置缓存集群:部署和配置缓存服务器以构建缓存集群。确保服务器之间可以相互通信,并可以进行水平扩展以适应负载增加的情况。
  3. 缓存数据的读写:通过缓存客户端库连接到缓存集群。使用客户端库来读取和写入缓存数据。通常,缓存数据可以使用键值对的方式存储。
  4. 缓存失效策略:考虑缓存数据的失效策略。可以为缓存数据设置过期时间,以确保数据不会永久存在于缓存中。根据数据的访问频率和重要性来设置合适的失效策略。
  5. 缓存数据的加载:当缓存中没有需要的数据时,确保应用程序可以从后端数据存储(如数据库)中加载数据,并将其缓存在分布式缓存中。这通常需要编写逻辑来处理缓存未命中的情况。
  6. 缓存数据的更新和删除:当后端数据发生变化时,确保应用程序更新缓存中的相应数据,以保持缓存的一致性。此外,需要考虑如何处理缓存数据的删除,以避免脏数据存在。
  7. 数据一致性:如果应用程序需要强一致性,确保在数据更新时使用合适的同步机制,例如在写入数据库后更新缓存,或者使用分布式锁来确保并发写入的一致性。
  8. 监控和维护:设置监控和报警系统,以监视缓存的性能和健康状态。定期备份缓存数据,确保数据的可恢复性。另外,实施缓存清理策略,以确保缓存不会占用过多的内存或存储。
  9. 安全性:针对缓存系统实施安全措施,包括限制访问、数据加密以及身份验证。
  10. 故障恢复:实施故障恢复策略,确保在缓存服务器或集群出现问题时,应用程序仍然能够正常工作。
  11. 性能优化:根据应用程序的需求,可以采取性能优化措施,例如缓存预热、使用缓存穿透保护策略、使用LRU(最近最少使用)等缓存淘汰策略。

分布式缓存的具体实现取决于应用程序需求和所选择的缓存系统。以上步骤提供了一个通用的指导框架,但每个应用程序都可能需要特定的配置和定制。确保在实施分布式缓存时,根据应用程序需求仔细考虑各个方面,并进行适当的测试和性能优化。

6.4 分布式唯一ID

参考1:分布式唯一 ID 生成方案浅谈
参考2:讲分布式唯一id,这篇文章很实在
参考3:Leaf:美团分布式ID生成服务开源

主要有以下几种:

  • UUID
  • 数据库自增ID
  • Redis生成ID
  • Zookeeper生成ID
  • Snowflake算法

  1. UUID
    UUIDUniversally Unique Identifier,即通用唯一标识码)算法的目的是生成某种形式的全局唯一ID来标识系统中的任一元素,尤其是在分布式环境下,UUID可以不依赖中心认证即可自动生成全局唯一ID
    UUID的标准形式为32个十六进制数组成的字符串,且分割为五个部分,例如:467e8542-2275-4163-95d6-7adc205580a9。

基于使用场景的不同,会存在以下几个不同版本的UUID以供使用,如下所示:

  • 基于时间的UUID:主要依赖当前的时间戳和机器mac地址。优势是能基本保证全球唯一性,缺点是由于使用了mac地址,会暴露mac地址和生成时间;
  • 分布式安全的UUID:将基于时间的UUID算法中的时间戳前四位替换为POSIXUIDGID。优势是能保证全球唯一性,缺点是很少使用,常用库基本没有实现;
  • 基于随机数的UUID:基于随机数或伪随机数生成。优势是实现简单,缺点是重复几率可计算;
  • 基于名字空间的UUIDMD5版):基于指定的名字空间/名字生成MD5散列值得到。优势是不同名字空间/名字下的UUID是唯一的,缺点是MD5碰撞问题,只用于向后兼容;
  • 基于名字空间的UUID(SHA1版):将基于名字空间的UUIDMD5版)中国的散列算法修改为SHA1。优势是不同名字空间/名字下的UUID是唯一的,缺点是SHA1计算相对耗时。UUID的优势是性能非常高,由于是本地生成,没有网络消耗。

而其也存在一些缺陷,包括不易于存储,UUID太长,16字节128位,通常以36长度的字符串表示;信息不安全,基于时间的UUID可能会造成机器的mac地址泄露;ID作为DB主键时在特定的场景下会存在一些问题。
2. 数据库自增ID
数据库自增ID是最常见的一种生成ID方式。利用数据库本身来进行设置,在全数据库内保持唯一。优势是使用简单,满足基本业务需求,天然有序;缺点是强依赖ID,会由于数据库部署的一些特性而存在单点故障、数据一致性等问题。
3. Redis生成ID
主要使用Redis的原子操作INCRINCRBY来实现。优势是不依赖于数据库,使用灵活,性能也优于数据库;而缺点则是可能要引入新的组件Redis,如果Redis出现单点故障问题,则会影响序号服务的可用性。
4. Zookeeper生成ID
主要是利用Zookeeperznode数据版本来生成序列号,可以生成32位和64位的数据版本号,客户端可以使用这个版本号来作为唯一的序列号。由于需要依赖Zookeeper,并且是多步调用API,如果在竞争较大的情况下,可能需要考虑使用分布式锁,故此种生成唯一ID的方法的性能在高并发的分布式环境下不甚理想。
5. Snowflake算法
Snowflake(雪花算法)是一个开源的分布式ID生成算法,结果是一个long型的ID。Snowflake算法将64bit划分为多段,分开来标识机器、时间等信息,具体组成结构如下图所示:
在这里插入图片描述
Snowflake算法的核心思想是使用41bit作为毫秒数,10bit作为机器的ID(比如其中5个bit可作为数据中心,5个bit作为机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0。

Snowflake算法可以根据自身业务的需求进行一定的调整。比如估算未来的数据中心个数,每个数据中心内的机器数,以及统一毫秒内的并发数来调整在算法中所需要的bit数。

Snowflake算法的优势是稳定性高,不依赖于数据库等第三方系统;使用灵活方便,可以根据业务需求的特性来调整算法中的bit位;单机上ID单调自增,毫秒数在高位,自增序列在低位,整个ID是趋势递增的。而其也存在一定的缺陷,包括强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务处于不可用状态;ID可能不是全局递增,虽然ID在单机上是递增的,但是由于涉及到分布式环境下的每个机器节点上的时钟,可能会出现不是全局递增的场景。

由于雪花算法强依赖于机器时间,如果时间上的时钟发生回拨,则可能引起生成的ID冲突的问题。解决该问题的方案如下所示:

  • ID生成交给少量服务器,然后关闭这些服务器的时钟回拨能力;
  • 当遇到时钟回拨问题时直接报错,交给上层业务来处理;
  • 如果回拨时间较短,在耗时要求范围内,比如5ms,等待回拨时长后在生成ID返回给业务侧;
  • 如果回拨时间很长,无法等待,可以匀出少量位作为回拨位,一旦时间回拨,将回拨位加1,可得到不一样的ID,2位回拨可允许标记三次时钟较长时间的回拨,基本够使用。如果超过回拨次数,可以再选择报错或抛出异常。

6.5 分布式开发的时候有没有碰到什么问题?

  1. 网络通信和延迟:分布式系统中的组件通常分布在不同的地理位置或服务器上,因此需要处理网络通信和延迟。网络通信可能会引入失败、丢包和不稳定性,需要设计适当的协议和处理机制。
  2. 一致性和可用性:维护分布式系统的一致性和可用性是一个复杂的问题。在分布式环境中,实现强一致性可能需要付出性能代价,而宽松一致性可能导致数据不一致。
  3. 数据一致性:确保数据在不同组件之间的一致性是一个关键问题。分布式事务和复制协议可以用于解决这个问题。
  4. 容错和故障处理:分布式系统需要考虑容错机制,以处理组件故障和网络中断。这包括故障检测、故障恢复和负载均衡等。
  5. 分布式锁和并发控制:在多个组件之间协调共享资源访问时,需要处理分布式锁和并发控制,以避免竞争条件和数据不一致。
  6. 数据分布和分片:将数据分布在多个服务器上,以提高性能和可伸缩性。这涉及到数据分片和负载均衡的设计。
  7. 版本控制和升级:维护分布式系统的多个版本和组件,需要有效的版本控制和升级策略,以确保系统的兼容性和可维护性。
  8. 监控和调试:监视和调试分布式系统可以是具有挑战性的任务。需要使用适当的工具和日志记录来跟踪问题并分析性能瓶颈。
  9. 安全性:分布式系统需要维护数据的安全性和隐私,因此需要考虑身份验证、授权、加密和防护措施。
  10. 数据传输和序列化:数据在不同组件之间传输和序列化可能需要额外的注意,以确保数据的完整性和正确性。
  11. 资源管理和扩展性:在分布式系统中,有效地管理资源(如计算资源、存储和带宽)和实现可扩展性是至关重要的。
  12. 文档和通信:在分布式开发中,清晰的文档和有效的沟通是关键,以确保团队理解系统的设计和协作。

这些问题只是分布式开发中的一部分,而实际挑战可能因应用程序的特定需求和规模而有所不同。解决这些问题需要仔细的设计、合适的工具和技术,以及经验丰富的开发团队。分布式系统的复杂性和挑战性是一个长期的研究领域,也是计算机科学领域的重要主题之一。

6.6 分布式架构一般哪些场景会用到?

  1. 高可用性需求:当系统对高可用性有极高的要求时,分布式架构可以通过在多个节点上部署相同的服务或数据来保障系统的持续可用性。即使某个节点发生故障,其他节点依然可以提供服务。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
测、故障恢复和负载均衡等。
5. 分布式锁和并发控制:在多个组件之间协调共享资源访问时,需要处理分布式锁和并发控制,以避免竞争条件和数据不一致。
6. 数据分布和分片:将数据分布在多个服务器上,以提高性能和可伸缩性。这涉及到数据分片和负载均衡的设计。
7. 版本控制和升级:维护分布式系统的多个版本和组件,需要有效的版本控制和升级策略,以确保系统的兼容性和可维护性。
8. 监控和调试:监视和调试分布式系统可以是具有挑战性的任务。需要使用适当的工具和日志记录来跟踪问题并分析性能瓶颈。
9. 安全性:分布式系统需要维护数据的安全性和隐私,因此需要考虑身份验证、授权、加密和防护措施。
10. 数据传输和序列化:数据在不同组件之间传输和序列化可能需要额外的注意,以确保数据的完整性和正确性。
11. 资源管理和扩展性:在分布式系统中,有效地管理资源(如计算资源、存储和带宽)和实现可扩展性是至关重要的。
12. 文档和通信:在分布式开发中,清晰的文档和有效的沟通是关键,以确保团队理解系统的设计和协作。

这些问题只是分布式开发中的一部分,而实际挑战可能因应用程序的特定需求和规模而有所不同。解决这些问题需要仔细的设计、合适的工具和技术,以及经验丰富的开发团队。分布式系统的复杂性和挑战性是一个长期的研究领域,也是计算机科学领域的重要主题之一。

6.6 分布式架构一般哪些场景会用到?

  1. 高可用性需求:当系统对高可用性有极高的要求时,分布式架构可以通过在多个节点上部署相同的服务或数据来保障系统的持续可用性。即使某个节点发生故障,其他节点依然可以提供服务。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-S6XSv7b3-1713217035170)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值