lukaliou123 / lukaliou123.github.io

lukaliou123在2022年的面试用知识点总结
Other
5 stars 0 forks source link

zookeeper和RPC #27

Open lukaliou123 opened 2 years ago

lukaliou123 commented 2 years ago

1.ZooKeeper 是什么?

ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。 具体常用的功能:

  1. 集群管理:监控节点存活状态、运行请求等;
  2. 主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 Zookeeper 可以协助完成这个过程;
  3. 分布式锁:Zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。Zookeeper 可以对分布式锁进行控制。
  4. 命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。

ZooKeeper 的目标就是封装好复杂易出错的关键服务将简单易用的接口和性能高效、功能稳定的系统提供给用户

2.zookeepr保证了如下分布式一致性特性

(1)顺序一致性

(2)原子性

(3)单一视图

(4)可靠性

(5)实时性(最终一致性)

2.1.zk如何实现分布式锁?

在ZooKeeper中,有一种锁叫做“顺序节点锁”,它利用了ZooKeeper的临时顺序节点特性。

在ZooKeeper中,每个节点都是唯一的,我们可以将它们想象成一个目录路径。比如说我们想在目录/lock下创建锁,我们可以这样做:

1.每个客户端都尝试在/lock节点下创建一个临时顺序节点

2.客户端会获取/lock节点下所有的子节点,并将其排序。

3.客户端会查看自己创建的节点在所有节点中的排序情况。如果自己创建的节点是序号最小的,那么该客户端就认为自己获取到了锁

4.如果客户端发现自己创建的节点不是序号最小的,那么它就会监听它前面的一个节点(也就是序号比它小的那个节点),当该节点被删除时,客户端会再次检查自己的节点是否是当前最小的节点,如果是,则获取锁。

这就是在ZooKeeper中实现分布式锁的基本流程。

简单的例子:

假设有三个客户端A、B和C,它们都想要获取一个锁。

A、B和C都在/lock节点下创建了一个临时顺序节点,假设A创建的节点是/lock/0000000001,B创建的节点是/lock/0000000002,C创建的节点是/lock/0000000003。

A、B和C都获取了/lock节点下的所有子节点,并将它们排序,得到的结果是A的节点是最小的,所以A认为自己获得了锁。

此时,B和C都会监听它们前面的一个节点,B监听A的节点,C监听B的节点。

当A处理完毕后,它会删除自己的节点,这时,B就会收到通知,然后再次获取/lock节点下的所有子节点并排序,发现自己的节点现在是最小的,于是B认为自己获得了锁。

为什么ZK锁是强一致性的?

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它是专门为分布式应用设计的。ZooKeeper提供了一种高效且可靠的分布式锁服务。你可以在ZooKeeper中创建一个临时的顺序节点来实现一个锁服务,比如:所有客户端都去创建/lock下的子节点,如果创建成功,那么就相当于获取了锁,如果创建失败,那么就相当于锁被别的进程占用。这种方式,可以保证只有一个客户端能够成功创建子节点,也就是说只有一个客户端能够获取锁。

ZooKeeper的一大特性就是可以提供顺序保证,也就是说它可以保证客户端在创建临时顺序节点时的顺序性,而且这个顺序是全局一致的。这意味着ZooKeeper不仅可以用来做锁服务,还可以用来做其他需要全局一致顺序保证的服务

总结一下,ZooKeeper在设计时就考虑到了一致性和分布式协调,因此在实现分布式锁时,能提供比Redis和数据库更强的一致性保证

一个实现的例子

(G8L%0 M)ZDIESJBFYCL CS 在Curator中,当我们实例化InterProcessMutex对象的时候,我们需要提供一个ZooKeeper客户端对象和锁的路径。实际上,这个路径就是在ZooKeeper上的锁节点路径。但这个节点并不是立刻创建的,而是在我们调用acquire方法尝试获取锁的时候创建的

在ZooKeeper中,所有创建的节点都可以是临时的(在客户端断开时自动删除)或持久的(需要手动删除)。并且,可以是顺序的(ZooKeeper自动为其添加一个自增的序列号),也可以是非顺序的。

InterProcessMutex在尝试获取锁时,会在我们提供的锁路径下创建一个临时顺序节点。这个节点的存在就代表着锁的持有。当调用release方法时,这个临时顺序节点就会被删除,代表锁被释放。如果在持有锁的过程中客户端突然断开,由于节点是临时的,它也会被ZooKeeper自动删除,防止了锁无法被释放的问题。

ZK的选举机制

为了保证数据的一致性,Zookeeper的所有写操作都需要经过Leader节点进行,并且必须在大多数节点(过半)同步了这次操作后才算成功。这就涉及到了Zookeeper的选举机制

Zookeeper的选举机制主要包括如下几个步骤:

1.启动选举:当Zookeeper集群中的一台服务器启动或者在运行过程中没有和Leader服务器保持有效通信,那么它就会进入选举状态。

2.投票:进入选举状态后,该服务器就会向其他Zookeeper服务器发送投票。在Zookeeper中,每台服务器都有投票的权利,每次投票会投给自己认为适合做Leader的服务器。通常情况下,服务器会首先投给自己。

3.选举:当服务器收到其他服务器的投票信息后,会根据投票信息和自己的服务器状态以及已经接收的票数等信息重新选举Leader。

4.改变状态:当一台服务器收到过半数服务器的相同投票信息时,那么投票的结果有效,该服务器将改变自己的状态。如果自己是被选为Leader的服务器,那么状态变为Leading,同时其他服务器的状态变为Following

5.通知:状态改变后,它会向其他所有服务器发送当前Leader服务器的信息,其他服务器在收到这个消息后会更改自己的状态。

通过这个机制,Zookeeper保证了在任何时候,集群中只有一个Leader,保持了整个集群的一致性。如果Leader服务器出现故障,那么可以通过这个机制迅速选出新的Leader,进一步保证了服务的高可用性。

为了保证奇数性,至少要有三台zk节点

ZK的leader和follower的各自的责任

1.Leader(领导者):Leader负责处理所有的写请求(包括创建、删除、更新数据等操作)。它不仅要处理这些写请求,还需要将这些更改复制(同步)到其他所有的Follower节点。此外,Leader还负责维护整个集群的运行状态,例如进行节点选举,以及在集群状态改变时进行相应的处理。

Follower(跟随者):Follower主要负责处理读请求(查询数据)。由于数据的更新由Leader统一处理,并同步到所有的Follower,所以每个Follower都可以直接处理读请求,这在一定程度上提高了集群的读取性能。此外,Follower还需要响应Leader的状态更改请求,并参与到新的Leader选举中

lukaliou123 commented 2 years ago

3.什么是RPC

1.RPC (Remote Procedure Call)即远程过程调用,是分布式系统常见的一种通信方法。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。 2.除 RPC 之外,常见的多系统数据交互方案还有分布式消息队列、HTTP 请求调用、数据库和分布式缓存等。 3.其中 RPC 和 HTTP 调用是没有经过中间件的,它们是端到端系统的直接数据交互。

简单的说

RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。 RPC会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)。 客户端发起请求,服务器返回响应(类似于Http的工作方式)RPC在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)

lukaliou123 commented 2 years ago

4.什么是JMM

JMM就是Java内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果

Java内存模型规定所有的变量都存储在主内存中,包括实例变量,静态变量,但是不包括局部变量和方法参数。每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存的副本拷贝,线程对变量的操作都在工作内存中进行。线程不能直接读写主内存中的变量。 image

补充解释:我对JMM的理解

Java内存模型(JMM)是Java并发编程的核心组成,它主要关注两个方面的问题:线程间的内存可见性以及指令重排序。内存可见性指的是当一个线程修改了共享变量的值,其他线程能够立即看到修改的值。而指令重排序是由于处理器和编译器为了优化性能,可能会对指令进行重排。Java内存模型通过一些规定,例如'先行发生原则',来保证在并发环境下的有序性和一致性。同时,它定义了Java程序中各种变量的访问规则,以此来解决多线程间通信的并发问题。

理解和应用Java内存模型能够帮助我们更好地进行并发编程,确保程序的正确性。

MM是是对代码运行的一种规范。因为相同的代码,在不同的机器运行有不同的效果,可能会带来缓存不一致的效果,这是不同的处理器可能会对指令进行重排。为了保证可见性和有序性,JMM定义了线程间的先行发生关系。线程在处理完自己的副本后,要提交到主存中进行执行。那么我可以说,java中的原子类,锁,和volatile(尤其是volatile),都是为了遵循JMM而设计出来使用的吗?

5.那JMM定义了什么

原子性,可见性,有序性

6.八种内存交互操作吧

image lock(锁定),作用于主内存中的变量,把变量标识为线程独占的状态。 read(读取),作用于主内存的变量,把变量的值从主内存传输到线程的工作内存中,以便下一步的load操作使用。 load(加载),作用于工作内存的变量,把read操作主存的变量放入到工作内存的变量副本中。 use(使用),作用于工作内存的变量,把工作内存中的变量传输到执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。 assign(赋值),作用于工作内存的变量,它把一个从执行引擎中接受到的值赋值给工作内存的变量副本中,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作。 store(存储),作用于工作内存的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用。 write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。 unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

lukaliou123 commented 1 year ago

7.分布式场景保持数据一致

在分布式系统中,保证数据一致性是一大挑战。这是因为分布式系统中的节点可能位于不同的地理位置,而且可能会有不同的数据更新频率。在这种情况下,以下是一些常见的策略来保证数据一致性:

1.两阶段提交(2PC):这是一种经典的分布式系统数据一致性保证策略。在第一阶段,事务协调者询问所有的参与者是否准备好提交事务。如果所有的参与者都回应说他们已经准备好了,那么在第二阶段,协调者会告诉所有的参与者提交事务。否则,协调者会告诉所有的参与者中止事务

2.三阶段提交(3PC):这是2PC的一个改进版本。在这种策略中,添加了一个预提交阶段,这使得系统能够更好地处理协调者和参与者之间的失败。

3.分布式事务框架:例如阿里巴巴的Seata,Google的Spanner等。这些框架提供了完整的解决方案来处理分布式事务问题。

4.最终一致性:在某些情况下,强一致性可能会影响到系统的性能。在这种情况下,我们可能会选择使用一种被称为最终一致性的策略。这种策略允许系统在短时间内存在不一致的状态,但保证在没有新的更新操作时,数据会最终达到一致的状态。

5.CAP理论: 分布式事务还涉及到CAP理论,即一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)中的两项。因此,设计分布式事务时需要权衡这些因素。

lukaliou123 commented 1 year ago

8.HTTP和RPC的区别

1.HTTP的本质:HTTP是一种基于TCP/IP的应用层协议,主要用于在网络中传输超文本信息,如文本、图片、音频和视频。HTTP 的核心是定义了一种请求和响应的规范,规定了浏览器和服务器之间的通信交互格式。这样无论你是什么服务器、什么浏览器都能顺利的交流,减少交互的成本。强调通用性

2.RPC的定义与作用:RPC是一种远程过程调用协议,它使得程序可以在远程服务器上调用函数,就像在本地调用一样。RPC 的通信可以使用 HTTP 协议,也可以自定义其他协议,取决于实际需要。RPC 的出现主要是为了解决微服务架构中,不同服务之间的通信问题,使得代码在调用远程服务时,看起来仍然像是在进行本地调用。

HTTP和RPC的区别

1.协议和机制的区别HTTP 是一种网络通信协议,规定了客户端和服务器之间如何交互;而 RPC 是一种机制,用于实现跨网络的函数调用。

2.通用性和专用性的区别HTTP 是通用的,适用于各种网络通信场景,如 Web 浏览器与服务器的通信;而 RPC 通常用于特定的场景,如在微服务架构中的服务间通信。

3.使用的便利性的区别:HTTP 协议比较简单,使用广泛,但在微服务调用中,可能需要处理更多的网络通信细节;而 RPC 框架则尽可能地屏蔽了网络通信的复杂性,使开发者可以像调用本地函数一样调用远程函数。

4.效率的区别:HTTP 协议可能比较冗长,传输效率可能不如某些专为内部服务通信设计的 RPC 协议高。但是,HTTP 的优势在于其通用性,因此通常用于公开的 API 和外部系统的通信。

不过2.0因为各种原因现在更快了

见https://github.com/lukaliou123/lukaliou123.github.io/issues/2#issuecomment-1047792710

lukaliou123 commented 1 year ago

9.一致性哈希

一致性哈希通过解决这个问题提供了更好的灵活性和稳定性。它的工作原理如下:

环形空间:一致性哈希将所有的哈希值限定在一个环形的空间中(例如,如果使用32位哈希,则环的大小是2^32,对2^32取模找位置)。

服务器映射:每个服务器通过哈希函数映射到环上的一个或多个点。

键映射:同样地,每个键也通过哈希函数映射到环上的一个点。

查找服务器:为了找到一个键的服务器,一致性哈希沿着环的方向查找第一个位于键哈希值之后的服务器哈希值。

灵活扩展:当添加或删除服务器时,只影响环上该服务器附近的键,其余的键不受影响。这极大地减少了服务器变动时所需的数据迁移。

虚拟节点:为了解决不均匀分布的问题,一致性哈希通常使用虚拟节点的概念。每个物理服务器可以映射到环上的多个虚拟节点,从而使得数据分布更均匀。

image 简单的说,一致性哈希是将整个哈希值空间组织成一个虚拟的圆环,如假设哈希函数H的值空间为0-2^32-1(哈希值是32位无符号整形),整个哈希空间环如下:

整个空间按顺时针方向组织,0和2^32-1在零点中方向重合。 接下来,把服务器按照IP或主机名作为关键字进行哈希,这样就能确定其在哈希环的位置。

然后,我们就可以使用哈希函数H计算值为key的数据在哈希环的具体位置h,根据h确定在环中的具体位置,从此位置沿顺时针滚动,遇到的第一台服务器就是其应该定位到的服务器。 例如我们有A、B、C、D四个数据对象,经过哈希计算后,在环空间上的位置如下:

根据一致性哈希算法,数据A会被定为到Server 1上,数据B被定为到Server 2上,而C、D被定为到Server 3上。 3 容错性和扩展性 那么使用一致性哈希算法的容错性和扩展性如何呢? 3.1 容错性 假如RedisService2宕机了,那么会怎样呢?

那么,数据B对应的节点保存到RedisService3中。因此,其中一台宕机后,干扰的只有前面的数据(原数据被保存到顺时针的下一个服务器),而不会干扰到其他的数据。 3.2 扩展性 下面考虑另一种情况,假如增加一台服务器Redis4,具体位置如下图所示:

原本数据C是保存到Redis3中,但由于增加了Redis4,数据C被保存到Redis4中。干扰的也只有Redis3而已,其他数据不会受到影响。 因此,一致性哈希算法对于节点的增减都只需重定位换空间的一小部分即可,具有较好的容错性和可扩展性 4 虚拟节点 前面部分都是讲述到Redis节点较多和节点分布较为均衡的情况,如果节点较少就会出现节点分布不均衡造成数据倾斜问题。 例如,我们的的系统有两台Redis,分布的环位置如下图所示:

这会产生一种情况,Redis1的hash范围比Redis2的hash范围大,导致数据大部分都存储在Redis1中,数据存储不平衡。 为了解决这种数据存储不平衡的问题,一致性哈希算法引入了虚拟节点机制,即对每个节点计算多个哈希值,每个计算结果位置都放置在对应节点中,这些节点称为虚拟节点。 具体做法可以在服务器IP或主机名的后面增加编号来实现,例如上面的情况,可以为每个服务节点增加三个虚拟节点,于是可以分为 RedisService1#1、 RedisService1#2、 RedisService1#3、 RedisService2#1、 RedisService2#2、 RedisService2#3,具体位置如下图所示:

对于数据定位的hash算法仍然不变,只是增加了虚拟节点到实际节点的映射。例如,数据C保存到虚拟节点Redis1#2,实际上数据保存到Redis1中。这样,就能解决服务节点少时数据不平均的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。

作者:自律小喇叭 链接:https://juejin.cn/post/6844903750860013576 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。