• 微服务高频热点面试题汇总


     1.SpringBoot面试题

    1.1.简单聊聊SpringBoot的作用是什么?

    知识背景:

    SpringBoot采用默认配置,帮助我们快速的构建和运行Spring项目

    - 简化spring初始搭建和开发过程
    - 提供了大量的starter,集成了spring及大量第三方应用的自动配置
    - 允许使用配置文件(properties或者yml文件)覆盖默认配置
    - 创建独立的spring应用程序,使用main方法运行
    - 内嵌Tomcat无需部署war包,直接打成jar包,用nohup java -jar – & 启动就好

    面试话术:

    SpringBoot是一个快速构建项目并简化项目配置的工具,内部集成了Tomcat及大多数第三方应用和Spring框架的默认配置。与我们学习的SpringMVC和SpringCloud并无冲突,SpringBoot提供的这些默认配置,大大简化了SpringMVC、SpringCloud等基于Spring的Web应用的开发。

     1.2.SpringBoot的自动配置原理?

    问题1:那你说说SpringBoot的自动配置是如何实现的?

    面试话术:

    一般我们的SpringBoot项目启动类都会添加`@SpringBootApplication`注解,而这个注解的其中一个二级注解是`@EnableAutoConfiguration`注解。而`@EnableAutoConfiguration`注解通过`@Import`注解,以`ImportSelector`接口的方法来导入classpath下的`META-INF/spring.factories`文件,这些文件中会指定需要加载的一些类名称。

    这些类一般都加了`@Configuration`注解,并且完成了对某框架(例如Redis、SpringMVC)的默认配置,当这些类符合条件时,就会被实例化,其中的配置生效,那么自动配置自然生效了。

    问题2:满足怎样的条件配置才会生效?

    面试话术:

    一般提供默认配置的类都会添加`@ConditionalOnXxx`这样的注解,例如:`@ConditionalOnClass`,`@ConditionalOnProperties`等。`@ConditionalOnClass`表示只有classpath中存在某些指定的类时,条件满足,此时该配置类才会生效。例如Redis的默认配置其实早就有了,但是只有你引入redis的starter依赖,才满足了条件,触发自动配置。

    问题3:那如果我需要覆盖这些默认配置呢?

    有两种方式可以覆盖默认配置:

    - SpringBoot提供默认配置时,会在提供的Bean上加注解@ConditionalOnMissingBean,意思是如果这个Bean不存在时条件满足,那么我们只要配置了相同的Bean,那么SpringBoot提供的默认配置就会失效
    - SpringBoot提供默认配置时,一些关键属性会通过读取application.yml或者application.properties文件来获取,因此我们可以通过覆盖任意一个文件中的属性来覆盖默认配置。

    1.3.有没有自定义过SpringBoot的stater?

    面试话术:

    有,项目中某些中间件的客户端(如Redis、ElasticSearch)会进行二次封装,并通过starter方式提供jar包,供大家使用。

    一般定义starter包括下面几个子工程:

    - xxx-spring-boot-starter:pom格式,管理当前starter中需要的各种依赖
    - xxx-spring-boot-autoconfigure:jar格式,自动配置的核心代码

    我以elasticsearch为例来说说autoconfigure中包含哪些

    - elasticsearch的工具类
    - 属性加载的类,一般通过@ConfigurationProperties注解读取yaml文件中的es地址
    - 添加了@Configuration的配置类,作用是初始化elasticsearch工具类,初始化elasticsearch客户端,初始化一些其它必备的实例。
    - resource下定义META-INF文件夹,并且文件夹下定义spring.factories文件,文件中是key-value形式
      - key是EnableAutoConfiguration这个注解的全路径名
      - value是我们自定义自动配置类(加了@Configuration的类),如果有多个以","隔开

    1.4.SpringBoot项目的启动流程

    面试话术:

    SpringBoot项目启动第一步就是创建SpringApplication的实例,并且调用SpringApplication.run()这个方法。

    创建SpringApplication实例主要完成三件事情:

    - 记录当前启动类字节码
    - 判断当前项目类型,普通Servlet、响应式WebFlux、NONE
    - 加载/META-INF/spring.factories文件,初始化ApplicationContextInitializer和ApplicationListener实例

    而后的run()方法则会创建spring容器,流程如下:

    - 准备监听器,监听Spring启动的各个过程
    - 创建并配置环境参数Environment
    - 创建ApplicationContext
    - `prepareContext()`:初始化ApplicationContext,准备运行环境
    - `refreshContext(context)`:准备Bean工厂,调用一个BeanDefinition和BeanFactory的后处理器,初始化各种Bean,初始化tomcat
    - `afterRefresh()`:拓展功能,目前为空
    - 发布容器初始化完毕的事件

    1.5.SpringBoot的配置加载优先级

    面试话术:

    SpringBoot参数配置方式很多,比较常用参数配置方式按照优先级从高到低分别是:

    - 在命令行中传入的参数
    - java 的系统属性,可以通过System.getProperties()获得的内容
    - 操作系统的环境变量
    - 针对不同{profile}环境的配置文件内容,例如 applicaiton-{profile}.yaml 

    - application.yml或application.proerties文件
    - 在@Configration注解修改的类中,通过@PropertySource注解定义的属性
     

     2.SpringCloud相关

     2.1.SpringCloud和Dubbo的区别

    聊聊看SpringCloud和Dubbo有什么区别?

    面试话术:

    两者都是现在主流的微服务框架,但却存在不少差异:

    - 初始定位不同:SpringCloud定位为微服务架构下的一站式解决方案;Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用和治理
    - 生态环境不同:SpringCloud依托于Spring平台,具备更加完善的生态体系;而Dubbo一开始只是做RPC远程调用,生态相对匮乏,现在逐渐丰富起来。
    - 调用方式:SpringCloud是采用Http协议做远程调用,接口一般是Rest风格,比较灵活;Dubbo是采用Dubbo协议,接口一般是Java的Service接口,格式固定。但调用时采用Netty的NIO方式,性能较好。
    - 组件差异比较多,例如SpringCloud注册中心一般用Eureka,而Dubbo用的是Zookeeper

    SpringCloud生态丰富,功能完善,更像是品牌机,Dubbo则相对灵活,可定制性强,更像是组装机。

    相关资料:

    SpringCloud:Spring公司开源的微服务框架,SpirngCloud 定位为微服务架构下的一站式解决方案,生态丰富,功能完善。

    Dubbo:阿里巴巴开源的RPC框架,Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。

    SpringCloudAlibaba

    两者的生态对比:

    | 功能         | Dubbo            | SpringCloud                        |
    | ------------ | ---------------- | ---------------------------------- |
    | 服务注册中心 | Zookeeper        | Eureka(主流)、Consul、zookeeper   |
    | 服务调用方式 | RPC基于Dubbo协议 | REST API 基于Http协议              |
    | 服务监控     | Dubbo-Monitor    | Spring Boot Admin                  |
    | 熔断器       | 不完善           | Spring Cloud Netflix Hystrix       |
    | 服务网关     | 无               | Spring Cloud Netflix Zuul、Gateway |
    | 分布式配置   | 无               | Spring Cloud Config                |
    | 服务跟踪     | 无               | Spring Cloud Sleuth+Zipkin(一般)   |
    | 数据流       | 无               | Spring Cloud Stream                |
    | 批量任务     | 无               | Spring Cloud Task                  |
    | 信息总线     | 无               | Spring Cloud Bus                   |

    Spring Cloud 的功能很明显比 Dubbo 更加强大,涵盖面更广,而且作为 Spring 的旗舰项目,它也能够与 Spring Framework、Spring Boot、Spring Data、Spring Batch 等其他 Spring 项目完美融合,这些对于微服务而言是至关重要的。

    使用 Dubbo 构建的微服务架构就像组装电脑,各环节选择自由度很高,但是最终结果很有可能因为一条内存质量不行就点不亮了,总是让人不怎么放心,但是如果使用者是一名高手,那这些都不是问题。

    而 Spring Cloud 就像品牌机,在 Spring Source 的整合下,做了大量的兼容性测试,保证了机器拥有更高的稳定性,但是如果要在使用非原装组件外的东西,就需要对其基础原理有足够的了解。

    2.2.dubbo和Feign远程调用的差异

    面试话术:

    Feign是SpringCloud中的远程调用方式,基于成熟Http协议,所有接口都采用Rest风格。因此接口规范更统一,而且只要符合规范,实现接口的微服务可以采用任意语言或技术开发。但受限于http协议本身的特点,请求和响应格式臃肿,其通信效率相对会差一些。

    Dubbo框架默认采用Dubbo自定义通信协议,与Http协议一样底层都是TCP通信。但是Dubbo协议自定义了Java数据序列化和反序列化方式、数据传输格式,因此Dubbo在数据传输性能上会比Http协议要好一些。

    不过这种性能差异除非是达极高的并发量级,否则无需过多考虑。

    相关资料:

    Dubbo采用自定义的Dubbo协议实现远程通信,是一种典型的RPC调用方案,而SpringCloud中使用的Feign是基于Rest风格的调用方式。

    1)Rest风格

    REST是一种架构风格,指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

    Rest的风格可以完全通过HTTP协议实现,使用 HTTP 协议处理数据通信。REST架构对资源的操作包括获取、创建、修改和删除资源的操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。

    因此请求和想要过程只要遵循http协议即可,更加灵活

    SpringCloud中的Feign就是Rest风格的调用方式。

    2)RPC

    Remote Procedure Call,远程过程调用,就是像调用本地方法一样调用远程方法。RPC架构图:

    ![img](assets/20190526223643953.png)

    RPC一般要确定下面几件事情:

    - 数据传输方式:多数RPC框架选择TCP作为传输协议,性能比较好。
    - 数据传输内容:请求方需要告知需要调用的函数的名称、参数、等信息。
    - 序列化方式:客户端和服务端交互时将参数或结果转化为字节流在网络中传输,那么数据转化为字节流的或者将字节流转换成能读取的固定格式时就需要进行序列化和反序列化

    因为有序列化和反序列化的需求,因此对数据传输格式有严格要求,不如Http灵活

    Dubbo协议就是RPC的典型代表。

    我们看看Dubbo协议和Feign的调用区别:

    |          | Dubbo | Feign(Http调用) |
    | -------- | ----- | ----------------- |
    | 传输协议 | TCP   | TCP               |
    | 开发语言 | java  | 不限              |
    | 性能     | 好    | 一般              |
    | 灵活性   | 一般  | 好                |

    2.3.Eureka和Zookeeper注册中心的区别

    面试话术:

    SpringCloud和Dubbo都支持多种注册中心,不过目前主流来看SpringCloud用Eureka较多,Dubbo则以Zookeeper为主。两者存在较大的差异:

    - 从集群设计来看:Eureka集群各节点平等,没有主从关系,因此可能出现数据不一致情况;ZK为了满足一致性,必须包含主从关系,一主多从。集群无主时,不对外提供服务
    - CAP原则来看:Eureka满足AP原则,为了保证整个服务可用性,牺牲了集群数据的一致性;而Zookeeper满足CP原则,为了保证各节点数据一致性,牺牲了整个服务的可用性。
    - 服务拉取方式来看:Eureka采用的是服务主动拉取策略,消费者按照固定频率(默认30秒)去Eureka拉取服务并缓存在本地;ZK中的消费者首次启动到ZK订阅自己需要的服务信息,并缓存在本地。然后监听服务列表变化,以后服务变更ZK会推送给消费者。

    相关资料:

    首先,Eureka和Zookeeper都是服务治理框架,但是设计上有一定的差别。

    先看下CAP原则:C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。

    - Eureka满足AP,Zookeeper满足CP

      当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是Zookeeper和Eureka在一致性与可用性间做出了不同的选择。

      - `Zookeeper`:Zookeeper的设计追求数据的一致性,不保证服务的可用性。当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

      - `Eureka`:Eureka追求的是服务的可用性,从而牺牲了数据的一致性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况。

        - Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
        - Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
        - 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

        因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。

    - Eureka集群各节点平等,Zookeeper中有主从之分

      - 如果Zookeeper集群中部分宕机,可能会导致整个集群因为选主而阻塞,服务不可用
      - eureka集群宕机部分,不会对其它机器产生影响

    - Eureka的服务发现需要主动去拉取,Zookeeper服务发现是监听机制

      - eureka中获取服务列表后会缓存起来,每隔30秒重新拉取服务列表
      - zookeeper则是监听节点信息变化,当服务节点信息变化时,客户端立即就得到通知

    2.4.SpringCloud中的常用组件有哪些?

    Spring Cloud的子项目很多,比较常见的都是Netflix开源的组件:

    - Spring Cloud Config
      集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。

    - Spring Cloud Netflix
      Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。
      - Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制;
      - Ribbon:负载均衡的服务调用组件,具有多种负载均衡调用策略;
      - Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;
      - Feign:基于Ribbon和Hystrix的声明式服务调用组件;
      - Zuul:API网关组件,对请求提供路由及过滤功能。
    - Spring Cloud Bus
      用于传播集群状态变化的消息总线,使用轻量级消息代理链接分布式系统中的节点,可以用来动态刷新集群中的服务配置。
    - Spring Cloud Consul
      基于Hashicorp Consul的服务治理组件。
    - Spring Cloud Security
      安全工具包,对Zuul代理中的负载均衡OAuth2客户端及登录认证进行支持。
    - Spring Cloud Sleuth
      Spring Cloud应用程序的分布式请求链路跟踪,支持使用Zipkin、HTrace和基于日志(例如ELK)的跟踪。
    - Spring Cloud Stream
      轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。
    - Spring Cloud Task
      用于快速构建短暂、有限数据处理任务的微服务框架,用于向应用中添加功能性和非功能性的特性。
    - Spring Cloud Zookeeper
      基于Apache Zookeeper的服务治理组件。
    - Spring Cloud Gateway
      API网关组件,对请求提供路由及过滤功能。
    - Spring Cloud OpenFeign
      基于Ribbon和Hystrix的声明式服务调用组件,可以动态创建基于Spring MVC注解的接口实现用于服务调用,在Spring Cloud 2.0中已经取代Feign成为了一等公民。

    2.5.微服务调用关系复杂,如何做监控和错误排查?

    面试话术:

    企业中对于微服务监控有一套东西,叫做APM。比如:SpringCloudSeluth+Zipkin,Pinpoint、Skywalking,可以实现性能监控、链路跟踪(精确到某个代码,某条sql)、CPU运行情况,链路运行耗时。

    当然, 还可以借助于分布式日志管理系统。把项目运行的日志收集,形成统计报表,放入elasticsearch,便于搜索查看。比如:ELK技术栈、GrayLog

    3.RabbitMQ常见面试题

    3.1.你们公司为什么选择了RabbitMQ产品,而不是RocketMQ和Kafka(问区别)?

    面试话术:

    kafka是以吞吐量高而闻名,不过其数据稳定性一般,而且无法保证消息有序性。

    阿里巴巴的RocketMQ基于Kafka的原理,利用Java代码打造,弥补了Kafka的缺点,继承了其高吞吐的优势,其客户端目前以Java为主。

    RabbitMQ基于面向并发的语言Erlang开发,吞吐量不如Kafka,但是消息可靠性较好。也能有效的保证消息的有序性。因为Erlang的原因,集群搭建比较方便。支持多种协议,并且有各种语言的客户端,比较灵活。

    综合考虑我们公司的并发需求以及稳定性需求,我们选择了RabbitMQ。

    3.4.如何保证RabbitMQ的消息可靠性,防止消息丢失?

    面试话术:

    我们针对MQ消息丢失的几种不同情况,采用不同的应对方案:

    - 生产者发送消息时可能因为网络问题导致消息丢失:
      - 利用RabbitMQ提供的publisher confirm机制,[参考文档](https://www.rabbitmq.com/tutorials/tutorial-seven-java.html)。
        - 生产者发送消息后,可以编写成功回调函数或失败超时回调函数
        - RabbitMQ接收消息成功并持久化会调用成功回调函数,通知消息的发送者
        - RabbitMQ接收消息时出现异常会调用失败回调函数,通知消息的发送者
        - 消息超时未发送成功也会调用失败回调函数
    - MQ宕机导致丢失消息:
      - 消息持久化,RabbitMQ会将消息持久化到磁盘,宕机重启可以恢复消息
      - 镜像集群,主从备份,避免引宕机导致消息丢失
    - 消费者丢失消息:避免引消费者异常或宕机导致消息丢失
      - 消费者的确认机制,在处理消息结束后,手动Acknowledge回执给MQ
      - MQ未接受到Acknowledge会认为消费失败,消息会保留在MQ

    rabbitmq中消息消费后自动删除,不会永久保留,无法实现消息回溯.

    3.5.如何防止MQ消息的重复消费?

    消息重复消费产生的原因:

    - 因为网络故障,导致生产者确认机制失败,生产者重发消息
    - 因为网络故障,导致生产者确认机制失败,MQ重新投递消息

    解决思路:业务处理时,保证处理消息接口的**幂等**性。

    - 能根据具体的业务或状态来确定的,在消费端通过业务判断是否执行过,例如新增订单,看看订单ID是否已经存在
    - 对于无法通过业务判断的,我们可以为每一条消息设置全局唯一id,保存到数据库消息表。消息处理前对ID进行判断即可

    3.6.如何解决MQ的消息堆积问题?

    面试话术:

    RabbitMQ支持多消费者绑定同一队列,消息Broker会把消息轮询的发送给每一个消费者。通过同一个队列多消费者监听,实现消息的争抢,加快消息消费速度。

    RabbitMQ也可以做集群,集群数据会分片效果,从而能堆积更多消息。

    备选方案:也可以给单个消费者接收消息后放入队列,交给线程池去处理。

    3.7.如何保证MQ消息的有序性?

    某个业务发出了3条消息,要求这3条消息按照发送时的顺序执行。

    - 业务对并发要求不高:
      - 保证消息发送时有序同步发送
      - 保证消息发送被同一个队列接收,MQ本身是先进先出,保证消息有序
      - 保证一个队列只有一个消费者,避免多个消费者争抢导致顺序混乱
    - 业务同时对并发要求较高:
      - 满足上述第一个场景的条件
      - 可以有多个队列
      - 有时序要求的一组消息,通过hash方式分派到一个固定队列

    3.8.如何利用RabbitMQ实现延迟队列

    面试话术:

    RabbitMQ中有一个**死信队列**设定:我们可以给一个队列**设置过期时间**,或者发送消息时给消息设置过期时间。过期的消息称为**死信**,队列会把死信转发给提前设置的**死信交换机**,而与死信交换机绑定的队列就可以拿到这些消息。

    因为发送消息超过一定时间(过期)后,才会被队列拿到,这样就实现了延迟队列效果。

    实现起来非常简单,不过也有一些缺陷:

    - 如果延迟消息过多,可能导致MQ的消息堆积过多
    - MQ消息无法删除,因此不能撤销延迟消息。

    如果对上述问题有要求,可以利用Redis来实现延迟队列。

  • 相关阅读:
    一致性哈希算法原理图文详解!学不会接着来砍我!
    Node.js开发-path模块
    OCR -- 文本检测
    包装接口,处理数据
    mits6.081-lab2
    计算机组成原理(三)
    下班时间做抖音小店,连直播都不会的我,7天销售额流水50多万
    【线性代数】P8 逆矩阵&矩阵方程以及逆矩阵的性质
    golang的垃圾回收算法之五GMP模型
    刷爆力扣之检查数组对是否可以被 k 整除
  • 原文地址:https://blog.csdn.net/JACK_SUJAVA/article/details/126508759