声明:
- 背景:本人为24届双非硕校招生,已经完整经历了一次秋招,拿到了三个offer。
- 本专题旨在分享自己的一些Java开发岗面试经验(主要是校招),包括我自己总结的八股文、算法、项目介绍、HR面和面试技巧等等,如有建议,可以友好指出,感谢,我也会不断完善。
- 想了解我个人情况的,可以关注我的B站账号:东瓜Lee
微服务篇
-
说一下你对微服务的理解?
微服务是一种架构风格,具体来说就是把一个大的应用程序划分为一组小型的服务,每个服务都运行在自己的进程中,并通过轻量级的通信机制来进行服务间的通信。每个服务都是独立部署、独立扩展、独立更新的,从而提高了整个应用程序的可维护性、可测试性。
由于微服务是属于分布式架构下的一种设计风格,所以会有一系列复杂问题,而基于Spring的SpringCloud就能够来实现微服务。
-
单体项目和微服务项目的区别?
- 单体项目:是指整个应用程序是一个单一的、紧密耦合的单元,所有的功能模块都在这一个单元中,共享着相同的数据库和资源,这种架构风格简单直观,适合一些小型的复杂度不高的项目。
- 微服务项目:是指将一个应用程序拆分成多个独立的服务,每个服务都能独立扩展、部署和更新,服务之间通过通信机制来通信。不同的业务功能放在不同的服务上,这种架构风格提供了更高的灵活性和可扩展性,适合于大型的复杂度高的系统。
- 具体选用那种架构风格取决于业务的需求、项目规模等等,两者没有绝对好与坏。
-
分布式和微服务的区别和联系?
分布式和微服务是相关但是不相同的两个概念,我们经常放在一起说主要是因为微服务架构通常是建立在分布式系统的基础之上的。
- 分布式:指的是将多台计算机(服务器)通过网络连接在一起,共同完成某一组认为的系统,每个计算机都是一个节点,它能够提高系统的可靠性和性能,降低单点故障的风险。(比如Redis搭建集群、Tomcat服务器搭建集群都是属于分布式架构)
- 分布式是一个很早的概念,当时就是为了解决如何将任务分配到多个计算机来提高系统的可靠性和性能。相比之下,微服务是近十几年才流行的,它是建立在分布式系统的基础上的架构风格。
- 微服务:是一种软件架构风格,将一个大的复杂的应用程序拆分成一些小型、独立的服务,每个服务都能独立部署、独立扩展、独立更新,然后通过轻量级的通信机制来进行服务间的通信。
- 放在一起讲的原因就是,微服务一般是建立在分布式系统的基础之上的,比如将一个大的应用程序分成多个独立的微服务,然后每个微服务都部署在多个节点上,通过一定的通信机制来进行通信,两者都是为了解决大规模系统的问题。
- 微服务的架构可以不采用分布式,比如每个服务都部署在一个节点上,就不是分布式的。
- 分布式系统也并不一定要采用微服务的架构,使用单体项目也可以有分布式,比如搭建分布式的数据库、分布式的Redis都是分布式系统。
-
SpringCloud的五大组件
- 注册中心:Eureka
- 远程调用:Feign
- 负载均衡:Ribbon
- 服务降级\熔断:Hystrix
- 网关:Zuul\Gateway
-
SpringCloudAlibaba的组件
- 注册中心\配置中心:Nacos
- 远程调用组件:Feign
- 负载均衡:Ribbon
- 服务保护(降级\熔断):Sentinel
- 服务网关:Gateway
-
服务注册和发现是什么意思?SpringCloud如何实现的服务注册和发现?
- 注册中心的核心作用就是 服务的注册和发现
- 常见的注册中心:Eureka、Nocas、zookeeper
- 比如使用Eureka作为注册中心:
- 服务注册:所有服务启动的时候都需要把自己的信息注册到Eureka,由Eureka来保存这些服务的信息,比如服务名称、IP地址和端口号等等
- 服务发现:消费者(也是个服务)需要调用其他的服务,就需要从注册中心Eureka拉取服务的列表信息,如果该服务搭建了集群,则使用负载均衡算法选择其中的一个发起调用
- 服务监控:每个服务都会每隔30s向注册中心Eureka发送心跳,以此来报告健康状态,如果Eureka对某个服务等待了90s还没接收到心跳,则认为这个服务宕机,然后删除该服务的信息
-
Eureka和Nocas的区别是什么?
都可以作为注册中心,实现服务的注册和发现,也可以对服务进行健康监控
区别:
-
如果Nocas的ephemeral参数设置为ture的时候(默认),Nocas作为注册中心的逻辑和Eureka一样
-
如果把ephemeral参数设置为false的时候,代表设置为非临时实例,这个时候就有区别了
- 服务不但可以向Nocas发送心跳以实现健康监控,Nocas也可以主动向服务询问健康状态(主要的方式)
- 消费者不但可以从Nocas主动拉去服务,Nocas也可以主动向消费者推送服务的变更消息(如果有的话)
-
Nocas不但可以作为注册中心,还可以作为配置中心,而Eureka只能作为注册中心
-
-
负载均衡怎么实现的?
使用Ribbon作为负载均衡的组件,流程:
- 服务1使用远程调用组件Feign向服务2发起调用请求,Feign就会调用Ribbon组件实现负载均衡
- Ribbon会从注册中心从得到服务2的信息(有搭建集群),然后再采用负载均衡算法 选中集群中的某个服务发起调用
服务的集群:正常来讲,一个服务就是搭建在一个服务器上,为了保证服务的高可用性,就可以把一个服务搭建在多个服务器上。
-
Ribbon负载均衡的策略有哪些 ?
- 简单轮询集群中的所有服务器
- 按照权重大小选择某个服务器(响应时间越长的服务器 权重值越小)
- 随机选择一个可用的服务器
- 区域敏感策略:一个区域就可以看作一个机房,一个机房部署了多台服务器,比如先按照就近原则选择某个区域,再对这个区域内的多个服务器做轮询,如果就一个区域,那跟直接轮询是一样的(Ribbon默认的方式)
-
自定义负载均衡策略如何实现?
两种方式:
1. 创建类实现IRule接口,可以指定负载均衡策略,这个是全局的,对所有服务的远程调用都起作用 2. 在客户端的配置文件中,可以配置对某一个服务调用的负载均衡策略,只对配置的这个服务生效
-
什么是服务雪崩?怎么解决这个问题?
一个服务宕机导致 整条链路的服务都宕机的现象
比如某个服务(D)宕机,服务A去请求它,就会请求失败,就会一直发请求,而服务的请求连接次数是有限的,达到了限度后,服务A也会宕机,那就会引发这个请求链路上的所有服务都宕机。
解决 服务雪崩的方案:降级、熔断
可以使用SpringCloud提供的 服务降级熔断组件Hystix
- 服务降级:服务自我保护的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃,一般在实际开发中与feign接口整合,编写降级逻辑
- 服务熔断:默认关闭,需要手动打开,如果检测到 10 秒内请求的失败率超过 50%,就触发熔断机制。之后每隔 5 秒重新尝试请求微服务,如果微服务不能响应,继续走熔断机制。如果微服务可达,则关闭熔断机制,恢复正常请求
预防 服务雪崩的方案:限流
-
微服务是怎么进行监控的?
监控的主要目的是为了实现
1. 问题定位(定位哪个服务出现了问题) 2. 性能分析(分析服务的性能情况) 3. 服务关系(帮助理清楚好服务之间的关系) 4. 服务告警(一旦服务出问题了,有告警提示)
可以使用skywalking工具实现微服务的监控(一个分布式系统的应用程序性能监控工具APM,提供了完善的链路追踪能力),可以监控接口、服务、物理实例的一些状态,还可以设置服务的告警规则
-
为什么项目要采用限流?限流的使用场景?
- 并发量大的需求,比如秒杀业务,短时间内的请求量巨大,就必须做限流
- 平时为了防止用户恶意刷接口,也可以做限流
-
限流怎么实现?
限流就是限制流量,也就是限制用户的请求,防止请求量过大对服务器造成巨大的压力。
-
Tomcat在配置文件中可以设置最大连接数,可以限制对该服务器的访问(对于单体项目可以)
-
在Nginx中
-
使用漏桶算法:把所有的请求都存入到漏桶中,然后以固定速率从漏桶中流出一部分请求,这个漏桶也是有大小限制的(也就是只能存入一定数量的请求,多的请求就只能等待进入桶or直接放弃该请求),所以可以以固定速率来处理请求(以达到限流效果)
-
可以设置最大并发连接数
-
-
在网关中
使用令牌桶算法:在桶中存储的是令牌,按照设定好的速率生成令牌,每个请求都要先申请令牌,申请到令牌以后才能正常的执行请求,所以可以有一定的限流效果
(令牌就是放在redis中的,所以在网关中还需要配置redis的连接)
-
-
CAP定理是什么?
CAP定理:分布式项目下的一个理论
分布式系统有三个指标:一致性、可用性、分区容错性,一个分布式系统无法同时满足这三个指标,最多只能满足其中两个,这就叫做CAP定理。
- 一致性C:用户访问分布式系统中的任意节点,得到的数据必须一致
- 可用性A:用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
- 分区容错性P:
- 分区:因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区
- 分区容错:当集群出现分区时,整个系统也要持续对外提供服务
结论:无法同时保证一致性C和可用性A
1. 对于分布式系统而言,分区容错性是一个最基本的要求,所以分区P是必然存在的
2. 如果要保证访问的高可用性(A),可以持续对外提供服务,就不能保证数据的强一致性了 --> AP
3. 如果要保证访问数据的强一致性C,就不能保证高可用性了 --> CP
- BASE理论是什么?
BASE理论是对CAP中AP方案的延伸,核心思想就是即使AP不能保证强一致性,但可以采取合适的方式达到最终一致性,它包含三个思想:
- Basically Available(基本可用):是指分布式系统在出现不可预知的故障的时候,允许损失部分可用性,但不等于系统不可用。
- Soft State(软状态):是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
- Eventually Consistent(最终一致性):强调系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。其本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
消息中间件篇
-
什么是消息队列?
消息队列是一种应用程序间进行通信(传递消息)的技术,它允许应用程序在传递和处理消息时 相互隔离,将消息存储在队列中等待被处理。
主要分为三个部分:
- 生产者 (Producer):负责创建和发送消息到消息队列。
- 消息队列 (Queue):用于存储生产者发送的消息,直到被消费者处理。
- 消费者 (Consumer):从消息队列中取出并处理消息。
-
消息队列的作用和应用场景是什么?
- 异步处理:允许生产者和消费者独立工作,生产者发送任务到队列,然后继续进行其他操作,不需要等待任务完成。消费者从队列中按需取出并处理任务即可,这样就可以提高整体的处理效率和响应速度。主要应用在对于实时性不那么高的场景,比如用户注册后,异步发送一份欢迎邮件,还比如黑马点评项目中的异步下单优惠券操作。
- 系统解耦:在多个系统之间通过消息队列间接的进行通信,降低直接依赖,每个系统只需关注如何与消息队列进行交互,而不是直接与其他系统交互。这样,如果一个系统需要更改或维护,它不会直接影响到其他的系统。
- 流量削峰:使用消息队列来控制数据处理的速率,主要应用在一些高流量业务中(秒杀场景),比如在高流量期间,大量请求首先进入消息队列,而不是直接打到服务上,然后消息队列可以按照其处理能力逐渐释放消息给后端服务处理。而且在高流量的情况下,如果直接处理所有请求可能会导致数据丢失或处理失败,有了消息队列就可以确保所有请求都得到妥善存储和处理。
-
有什么主流的消息队列?
- RabbitMQ:是一个遵循AMQP(高级消息队列协议)的消息代理,它支持多种消息传递模式,适合于需要复杂的业务逻辑和消息路由、多协议支持和企业级特性的应用场景
- Kafka:是一个分布式流处理平台,适合需要处理大规模的数据流、实时数据处理和高吞吐量的场景
直接用Redis实现的消息队列,相比这些消息队列有什么不同?
- 功能简单:Redis实现的消息队列功能比较基础,缺乏一些高级特性,如消息排序、复杂的消息路由、消息事务等。
- 持久性:专业的消息队列系统通常提供更强大的数据持久性支持。虽然Redis也可以持久化数据,但它主要是作为内存存储系统设计的,持久化能力不如专门的消息队列技术。
- 可靠性:在消息传递方面,像RabbitMQ这样的系统通常提供更多的消息传递保证(如消息确认机制),而Redis则较少。
- 吞吐量和扩展性:专业的消息队列系统在处理高吞吐量和大规模分布式环境方面通常更加出色。
- 使用场景:Redis实现的消息队列更适合轻量级、低复杂度的应用场景。对于需要高度可靠性、复杂处理流程的场景,专业的消息队列系统会是更好的选择。
-
消息队列如何实现高可用?
消息队列实现高可用通常涉及到:
- 集群部署:通过部署多个节点,可以确保即使某个节点出现故障,其他节点仍可继续提供服务,这样可以提高系统的可用性和容错能力。
- 消息持久化:确保消息在发送和接收的时候持久地保存在硬盘上,可以保证消息在系统出现故障时也不会丢失。
-
什么是消息持久化?
消息持久化是指将消息保存在磁盘上,而不是仅仅存放在内存中,以防止在系统崩溃或重启时消息丢失,这样可以提高消息传输的可靠性和稳定性,并确保在异常情况下不会丢失重要数据。
-
消息队列中的消息如何保证不丢失?(消息的可靠性)
保证消息不丢失通常需要结合多种机制,包括:
- 消息持久化:将消息存储在磁盘上,而不是仅存储在内存中。这样即使系统重启或发生故障,消息也不会丢失。
- 确认机制:当消息被成功处理后,接收方发送一个确认消息给消息队列,告知消息已经成功处理。如果消息队列在一定时间内没有收到确认消息,可以采取重发等措施,确保消息不会丢失。
- 事务机制:在发送和消费消息的过程中,使用事务机制确保消息的原子性。即要么消息被完整地发送和消费,要么不进行任何操作,这样可以保证消息的可靠性,避免因部分操作失败而导致消息丢失。
-
什么是消息积压?如何处理?
消息积压是指在消息队列中,由于某些原因导致消息没有被及时处理,从而在消息队列中越积越多。
消息积压可能是由于系统负载过高、生产者生成消息速度过快、消费者处理消息速度过慢等原因造成的。
处理消息积压的方法:
- 控制生产者生产消息的速度,避免过多的消息堆积在消息队列中。
- 增加消费者的数量,可以提高并行处理消息的能力,加速消息的处理速度。
- 建立完善的监控和告警体系,实时监测消息队列的状态和性能指标,当出现消息积压时,及时发出告警,以便能够迅速采取措施。
-
什么是死信队列?
死信队列是用来存放无法正常处理的消息的队列,比如因为消息格式错误、消息过期等等原因。
死信队列使我们能够隔离这些有问题的消息,以便进一步分析和处理。
-
消息的顺序性是什么?如何保证?
在某些应用场景中,消息的处理需要按照特定的顺序执行,如果消息的处理顺序被打乱,可能会导致数据异常。
保证顺序性的方法:
- 单一消费者:只有一个消费者来处理消息队列中的消息,就可以保证消息按照被发送的顺序处理,这是最简单的方式来确保消息处理的顺序性,但是无法满足高并发和高可用的需求。
- 有序的消息:在生产消息时,可以为每一条消息分配一个唯一的序号,这样在处理消息时,就可以根据序号来保证消息的顺序性。
-
消息重复是什么?如何处理?
消息重复是指消息在消息队列中被多次处理的情况。在消息传递过程中,由于网络故障、消息队列的故障或消费者处理失败等原因,可能会导致消息被重复发送或重复消费。
处理消息重复的方法:
- 幂等性处理:在消费端,可以设计幂等操作(幂等性是指一个操作不论执行多少次,其结果都是一样的)对于消息队列来说,就是指无论消息被处理多少次,都只执行一次业务操作。这样即使消息被重复处理,也不会对系统造成影响。
- 记录操作的日志:在处理消息时,记录操作的日志,包括操作的时间戳、操作的内容和操作的用户等信息。当发现重复消息时,可以通过比对操作的日志来判断是否已经处理过该消息。
【后续继续补充,敬请期待】