查看: 115|回复: 0

2022年大厂offer必备java面试题整理-分布式(2)

[复制链接]

1

主题

6

帖子

5

积分

新手上路

Rank: 1

积分
5
发表于 2023-1-17 13:12:23 | 显示全部楼层 |阅读模式
Allen:2022年大厂offer必备java面试题整理-Java基础(1)
Allen:2022年大厂offer必备java面试题整理-Java基础(2)
Allen:2022年大厂offer必备java面试题整理-Java基础(3)
Allen:2022年大厂offer必备java面试题整理-JVM
Allen:2022年大厂offer必备java面试题整理-MySQL(1)
Allen:2022年大厂offer必备java面试题整理-MySQL(2)
Allen:2022年大厂offer必备java面试题整理-Spring(1)
Allen:2022年大厂offer必备java面试题整理-Spring(2)
Allen:2022年大厂offer必备java面试题整理-redis(1)
Allen:2022年大厂offer必备java面试题整理-redis(2)
Allen:2022年大厂offer必备java面试题整理-redis(3)
Allen:2022年大厂offer必备java面试题整理-redis(4)
Allen:2022年大厂offer必备java面试题整理-redis(5)
Allen:2022年大厂offer必备java面试题整理-redis(6)
Allen:2022年大厂offer必备java面试题整理-分布式(1)

11、Dubbo 为什么默认⽤ Javassist?


速度快,且字节码⽣成⽅便。
ASM ⽐ Javassist 更快,但是没有快⼀个数量级,⽽Javassist 只需⽤字符串拼接就可以⽣成字节码,⽽ ASM 需要⼿⼯⽣成,成本较⾼,⽐较麻烦。

12、为什么需要锁?


原因其实很简单:因为我们想让同⼀时刻只有⼀个线程在执⾏某段代码。
因为如果同时出现多个线程去执⾏,可能会带来我们不想要的结果,可能是数据错误,也可能是服务宕机等等。
以淘宝双11为例,在0点这⼀刻,如果有⼏⼗万甚⾄上百万的⼈同时去查看某个商品的详情,这时候会触发商品的查询,如果我们不做控制,全部⾛到数据库去,那是有可能直接将数据库打垮的。
这个时候⼀个⽐较常⽤的做法就是进⾏加锁,只让1个线程去查询,其他线程待等待这个线程的查询结果后,直接拿结果。在这个例⼦中,锁⽤于控制访问数据库的流量,最终起到了保护系统的作⽤。
再举个例⼦,某平台做活动“秒杀茅台”,假如活动只秒杀1瓶,但是同时有10万⼈在同⼀时刻去抢,如果底层不做控制,有10000个⼈抢到了,额外的9999瓶平台就要⾃⼰想办法解决了。此时,我们可以在底层通过加锁或者隐式加锁的⽅式来解决这个问题。
此外,锁也经常⽤来解决并发下的数据安全⽅⾯的问题,这⾥就不⼀⼀举例了。

13、为什么需要分布式锁?



分布式锁是锁的⼀种,通常⽤来跟 JVM 锁做区别。
JVM 锁就是我们常说的 synchronized、Lock。
JVM 锁只能作⽤于单个 JVM,可以简单理解为就是单台服务器(容器),⽽对于多台服务器之间,JVM 锁则没法解决,这时候就需要引⼊分布式锁。

14、实现分布式锁的⽅式


实现分布式锁的⽅式其实很多,只要能保证对于抢夺“锁”的系统来说,这个东⻄是唯⼀的,那么就能⽤于实现分布式锁。
举个简单的例⼦,有⼀个 MySQL 数据库 Order,Order 库⾥有个 Lock 表只有⼀条记录,该记录有个状态字段 lock_status,默认为0,表示空闲状态,可以修改为1,表示成功获取锁。
我们的订单系统部署在100台服务器上,这100台服务器可以在“同⼀时刻”对上述的这1条记录执⾏修改,修改内容都是从0修改为1,但是 MysQL 会保证最终只会有1个线程修改成功。因此,这条记录其实就可以⽤于做分布式锁。
常⻅实现分布式锁的⽅式有:数据库、Redis、Zookeeper。这其中⼜以 Redis 最为常⻅。

15、分布式锁如何实现?



RedLock
⾸先,该⽅案也是基于之前的那个⽅案(set加锁、lua脚本解锁)进⾏改良的,所以 antirez 只描述了差异的地⽅,⼤致⽅案如下。
假设我们有 N 个 Redis 主节点,例如 N = 5,这些节点是完全独⽴的,我们不使⽤复制或任何其他隐式协调系统,
为了取到锁,客户端应该执⾏以下操作:
1、获取当前时间,以毫秒为单位。
2、依次尝试从5个实例,使⽤相同的 key 和随机值(例如UUID)获取锁。当向Redis 请求获取锁时,客户端应该设置⼀个超时时间,这个超时时间应该⼩于锁的失效时间。例如你的锁⾃动失效时间为10秒,则超时时间应该在5-50 毫秒之间。这样可以防⽌客户端在试图与⼀个宕机的 Redis 节点对话时⻓时间处于阻塞状态。如果⼀个实例不可⽤,客户端应该尽快尝试去另外⼀个Redis实例请求获取锁。
3、客户端通过当前时间减去步骤1记录的时间来计算获取锁使⽤的时间。当且仅当从⼤多数(N/2+1,这⾥是3个节点)的Redis节点都取到锁,并且获取锁使⽤的时间⼩于锁失效时间时,锁才算获取成功。
4、如果取到了锁,其有效时间等于有效时间减去获取锁所使⽤的时间(步骤3计算的结果)。
5、如果由于某些原因未能获得锁(⽆法在⾄少N/2+1个Redis实例获取锁、或获取锁的时间超过了有效时间),客户端应该在所有的Redis实例上进⾏解锁(即便某些Redis实例根本就没有加锁成功,防⽌某些节点获取到锁但是客户端没有得到响应⽽导致接下来的⼀段时间不能被重新获取锁)。
可以看出,该⽅案为了解决数据不⼀致的问题,直接舍弃了异步复制,只使⽤ master 节点,同时由于舍弃了 slave,为了保证可⽤性,引⼊了 N 个节点,官⽅建议是 5。

该⽅案看着挺美好的,但是实际上我所了解到的在实际⽣产上应⽤的不多,主要有两个原因:1)该⽅案的成本似乎有点⾼,需要使⽤5个实例;2)该⽅案⼀样存在问题。

该⽅案主要存以下问题:
1)严重依赖系统时钟。如果线程1从3个实例获取到了锁,但是这3个实例中的某个实例的系统时间⾛的稍微快⼀点,则它持有的锁会提前过期被释放,当他释放后,此时⼜有3个实例是空闲的,则线程2也可以获取到锁,则可能出现两个线程同时持有锁了。
2)如果线程1从3个实例获取到了锁,但是万⼀其中有1台重启了,则此时⼜有3个实例是空闲的,则线程2也可以获取到锁,此时⼜出现两个线程同时持有锁了。
针对以上问题其实后续也有⼈给出⼀些相应的解法,但是整体上来看还是不够完美,所以⽬前实际应⽤得不是那么多。

Zookeeper 实现分布式锁
Zookeeper 的分布式锁实现⽅案如下:
1)创建⼀个锁⽬录 /locks,该节点为持久节点
2)想要获取锁的线程都在锁⽬录下创建⼀个临时顺序节点
3)获取锁⽬录下所有⼦节点,对⼦节点按节点⾃增序号从⼩到⼤排序
4)判断本节点是不是第⼀个⼦节点,如果是,则成功获取锁,开始执⾏业务逻辑操作;如果不是,则监听⾃⼰的上⼀个节点的删除事件
5)持有锁的线程释放锁,只需删除当前节点即可。
6)当⾃⼰监听的节点被删除时,监听事件触发,则回到第3步重新进⾏判断,直到获取到锁。
由于 Zookeeper 保证了数据的强⼀致性,因此不会存在之前 Redis ⽅案中的问题,整体上来看还是⽐较不错的。
Zookeeper 的主要问题在于性能不如 Redis 那么好,当申请锁和释放锁的频率较⾼时,会对集群造成压⼒,此时集群的稳定性可⽤性能可能⼜会遭受挑战。

16、分布式锁如何选型?


当前主流的⽅案有两种:
1)Redis 的 set 加锁+lua 脚本解锁⽅案,⾄于是不是⽤守护线程续命可以结合⾃⼰的场景去决定,个⼈建议还是可以使⽤的。
2)Zookeeper ⽅案
通常情况下,对于数据的安全性要求没那么⾼的,可以采⽤ Redis 的⽅案,对数据安全性要求⽐较⾼的可以采⽤ Zookeeper 的⽅案。

17、Zookeeper做为注册中⼼,主要存储哪些数据?存储在哪⾥?


ip、端⼝,还有⼼跳机制。数据存储在Zookeeper的节点上⾯。

18、Zookeeper 和 Eureka 的区别


Eureka和Zookeeper都是CAP定理中的实现,Eureka(保证AP),Zookeeper(保证CP)。
Zookeeper保证CP,当向注册中⼼查询服务列表时,我们可以容忍注册中⼼返回的是⼏分钟以前的注册信息,但不能接受服务直接down掉不可⽤。也就是说服务注册功能对可⽤性的要求要⾼于⼀致性、但是zk会出现这样⼀种情况当master节点因为⽹络故障与其他节点失去联系时,剩余节点会重新进⾏leader选举。问题在于,选举leader的时间太⻓,30~120s.选举期间整个zk集群都是不可⽤的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因⽹络问题使得zk集群失去master节点是较⼤概率会发⽣的事,虽然服务能够最终恢复,但是漫⻓的选举时间导致的注册⻓期不可⽤是不能容忍的。
Eureka吸取了Zookeeper的经验,因此在设计时就优先保证可⽤性。Eureka各个节点都是平等的,⼏个节点挂掉不会影响正常节点的⼯作剩余的节点依然可以提供注册和查询服务。
⽽Eureka的客户端在向某个Eureka注册或时如果发现连接⽣败,则会⾃动切换⾄其它节点,只要有⼀台Eureka还在就能保证注册服务可⽤(保证可⽤性)只不过查到的信息可能不是最新的(不保证强⼀致性)。
除此之外,Eureka还有⼀种⾃我保护机制如果在15分钟内超过85%的节点都没有正常的⼼跳,那么Eureka就认为客户端与注册中⼼出现了⽹络故障,此时会出现以下⼏种情况∶

1)Eureka不再从注册列表中移除因为⻓时间没收到⼼跳⽽应该过期的服务
2)Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可⽤)
3)当⽹络稳定时,当前实例新的注册信息会被同步到其它节点中。因此,Eureka可以很好的应对因⽹络故障导致部分节点失去联系的情况,⽽不会像zookeeper那样使整个注册服务瘫痪。

Zookeeper的设计理念就是分布式协调服务,保证数据(配置数据,状态数据)在多个服务系统之间保证⼀致性,这也不难看出Zookeeper是属于CP特性(Zookeeper的核⼼算法是ZAB,保证分布式系统下,数据如何在多个服务之间保证数据同步)。Eureka是吸取Zookeeper问题的经验,先保证可⽤性。

19、Kafka相对其他消息队列,有什么特点?



持久化:Kafka的持久化能⼒⽐较好,通过磁盘持久化。⽽RabbitMQ是通过内存持久化的。
吞吐量:Rocket的并发量⾮常⾼。
消息处理:RabbitMQ的消息不⽀持批量处理,⽽RocketMQ和Kafka⽀持批量处理。
⾼可⽤:RabbitMQ采⽤主从模式。Kafka也是主从模式,通过Zookeeper管理,选举Leader,还有Replication副本。
事务:RocketMQ⽀持事务,⽽Kafka和RabbitMQ不⽀持。

20、Kafka ⾼效⽂件存储设计特点有哪些?

1)Kafka 把 topic 中⼀个 parition ⼤⽂件分成多个⼩⽂件段,通过多个⼩⽂件段,就容易定
期清除或删除已经消费完⽂件,减少磁盘占⽤。
2)通过索引信息可以快速定位 message 和确定 response 的最⼤⼤⼩。
3)通过 index 元数据全部映射到 memory,可以避免 segment file 的 IO 磁盘操作。
4)通过索引⽂件稀疏存储,可以⼤幅降低 index ⽂件元数据占⽤空间⼤⼩。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表