Tips
面试指南系列,很多情况下不会去深挖细节,是小六六以被面试者的角色去回顾知识的一种方式,所以我默认大部分的东西,作为面试官的你,肯定是懂的。
上面的是脑图地址
叨絮
分布式系统开发,微服务架构的一种主流实现方式,当然在面试中是必不可少的拉。
然后下面是前面的文章汇总
- 2021-Java后端工程师面试指南-(引言)
- 2021-Java后端工程师面试指南-(Java基础篇)
- 2021-Java后端工程师面试指南-(并发-多线程)
- 2021-Java后端工程师面试指南-(JVM)
- 2021-Java后端工程师面试指南-(MySQL)
- 2021-Java后端工程师面试指南-(Redis)
- 2021-Java后端工程师面试指南-(Elasticsearch)
- 2021-Java后端工程师面试指南-(消息队列)
- 2021-Java后端工程师面试指南-(SSM)
什么是SpringBoot呢
简而言之,从本质上来说,Spring Boot 就是 Spring,它做了那些没有它你自己也会去做的 Spring Bean 配置。基于约定大于配置的一个理论
说说SpringBoot的特点吧
- 开发基于 Spring 的应用程序很容易。
- Spring Boot 项目所需的开发或工程时间明显减少,通常会提高整体生产力。
- Spring Boot 不需要编写大量样板代码、XML 配置和注释。
- Spring 引导应用程序可以很容易地与 Spring 生态系统集成,如 Spring JDBC、Spring ORM、Spring Data、Spring Security 等。
- Spring Boot 遵循“固执己见的默认配置”,以减少开发工作(默认配置可以修改)。
- Spring Boot 应用程序提供嵌入式 HTTP 服务器,如 Tomcat 和 Jetty,可以轻松地开发和测试 web 应用程序。(这点很赞!普通运行 Java 程序的方式就能运行基于 Spring Boot web 项目,省事很多)
说说@SpringBootApplication 这个注解吧
可以看出大概可以把 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot官网,这三个注解的作用分别是:
- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
- @ComponentScan: 扫描被@Component (@Service,@Controller)注解的bean,注解默认会扫描该类所在的包下所有的类。
- @Configuration:允许在上下文中注册额外的bean或导入其他配置类。
-所以说 @SpringBootApplication 就是几个重要的注解的组合,为什么要有它?当然是为了省事,避免了我们每次开发 Spring Boot 项目都要写一些必备的注解。这一点在我们平时开发中也 经常用到,比如我们通常会提一个测试基类,这个基类包含了我们写测试所需要的一些基本的注解和一些依赖。
知道SpringBoot的钩子函数吗,如何对你项目的启动和死亡做监控。
- 启动的时候,比如CommandLineRunner 重写它的run方法,就能在启动的时候做一个钩子函数,比如链接钉钉等
- 意外宕机也是可以的, @PreDestroy 这个注解也能实现,在宕机之前回调这个方法,实现钉钉机器人等。
了解spring boot 中的spring factories 机制吗?
Spring Factories.这种机制实际上是仿照java中的SPI扩展机制实现的。
spring -core 包里定义了SpringFactoriesLoader 类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。 在这个类中定义了两个对外的方法: -loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表
- loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。
说说springBoot的自动配置原理吧
首先我们知道SpringBoot项目的启动注解@SpringBootApplication 中有一个@EnableAutoConfiguration,这个就是开启springBoot的自动注册机制 可以看到,在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包
最后再加上我们的EnableAutoConfiguration 读取我们在配置文件中的文件就可以实现自动配置了,就比如我们的springboot Admin,我们的client只要配置下配置文件就能成功了,原因就是这个
上面这些都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation类都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。如果我们自定义了一个starter的话,也要在该starter的jar包中提供 spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类。所有框架的自动配置流程基本都是一样的,判断是否引入框架,获取配置参数,根据配置参数初始化框架相应组件
说说SpringBoot的启动流程吧
其实这块很大一部分和spring的启动流程有重叠的,但是,我们还是从头到尾来过一遍,当复习了。
SpringBoot的启动主要是通过实例化SpringApplication来启动的,启动过程主要做了以下几件事情:配置属性、获取监听器,发布应用开始启动事件初、始化输入参数、配置环境,输出banner、创建上下文、预处理上下文、刷新上下文(加载tomcat容器)、再刷新上下文、发布应用已经启动事件、发布应用启动完成事件。
实例化SpringApplication时做了什么
- 推断WebApplicationType,主要思想就是在当前的classpath下搜索特定的类
- 搜索META-INF\spring.factories文件配置的ApplicationContextInitializer的实现类
- 搜索META-INF\spring.factories文件配置的ApplicationListenerr的实现类
- 推断MainApplication的Class
SpringApplication的run方法做了什么?
- 创建一个StopWatch并执行start方法,这个类主要记录任务的执行时间
- 配置Headless属性,Headless模式是在缺少显示屏、键盘或者鼠标时候的系统配置
- 在文件META-INF\spring.factories中获取SpringApplicationRunListener接口的实现类EventPublishingRunListener,主要发布SpringApplicationEvent
- 把输入参数转成DefaultApplicationArguments类
- 创建Environment并设置比如环境信息,系统熟悉,输入参数和profile信息
- 打印Banner信息
- 创建Application的上下文,根据WebApplicationTyp来创建Context类,如果非web项目则创建AnnotationConfigApplicationContext,在构造方法中初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
- 在文件META-INF\spring.factories中获取SpringBootExceptionReporter接口的实现类FailureAnalyzers
- 准备application的上下文
- 初始化ApplicationContextInitializer
- 执行Initializer的contextPrepared方法,发布ApplicationContextInitializedEvent事件
- 如果延迟加载,在上下文添加处理器LazyInitializationBeanFactoryPostProcessor
- 执行加载方法,BeanDefinitionLoader.load方法,主要初始化了AnnotatedGenericBeanDefinition
- 执行Initializer的contextLoaded方法,发布ApplicationContextInitializedEvent事件
- 刷新上下文(后文会单独分析refresh方法),在这里真正加载bean到容器中。如果是web容器,会在onRefresh方法中创建一个Server并启动。
refresh方法 和spring的有点不同
srping中的onrefesh方法是空的,这个里面是需要去加载web容器的如tomcat jetty等,具体的方法还是一样的,这边就不说了,可以去看ssm那篇
说说SpringCloud容器和SpringBoot容器的关系呗
首先说一点就是 如果是SpringBoot呢?他是可以单独使用的,而SpringCloud是不能单独使用的,它必须依赖SpringBoot。
在我们SpringCloud的项目中呢,整个项目的容器分为三层
- BootStrap Spring 容器:由SpringCloud 监听器创建,用来初始化 SpringCloud 上下文
- SpringBoot Spring 容器:由SpringBoot创建,也是项目中常用的Spring容器。
- 微服务 Spring相关容器:Feign和Ribbon配置类对应的上下文,由配置容器抽象工厂 NamedContextFactory 创建,用于容器隔离。
主要流程
首先 SpringBoot 项目启动,触发监听器,如果引入了SpringCloud 中的BootstrapApplicationListener,则开始初始化 SpringCloud 相关的上下文:Bootstrap ApplicationContext,将其设置为祖先容器,然后继续创建其子容器:SpringBoot Application。
说说分布式系统开发的痛点,业界是怎么设计的这些解决方案
首先分布式系统开发,目前主流的架构就是微服务架构,如果说你做微服务架构的话,无论你怎么去选型,怎么去设计,首先你总归要碰到以下的几个问题
- 这么多的服务,客户端如何去访问,你就好比说我们几百个服务,难道前端要在代码中调用几百个地址,而况服务地址多了,我们也不好去管理这些ip和端口,
- 服务与服务之间,如何去通信,那是不是得处理我们服务内部之间的调用方式,
- 服务挂了怎么办,你不能因为一个服务挂了,导致整个项目出现问题,导致服务雪崩吧
- 服务与服务是如何做到服务的发现与注册的,你不能说那个服务挂了,我是如何去通知其他服务的。也就是服务的治理
说说你们公司的SpringCloud的组件吧
- Spring Cloud 核心组件:Eureka 服务发现和注册中心
- Spring Cloud 核心组件:Feign 服务与服务直接的调用
- Spring Cloud 核心组件:Ribbon 负载均衡
- Spring Cloud 核心组件:Hystrix 熔断 降级
- Spring Cloud 核心组件:Zuul SpringCloudGateway 服务网关
聊聊Eureka吧
首先什么是Eureka
首先,eureka在springcloud中充当服务注册功能,相当于dubbo+zk里面得zk,但是比zk要简单得多,zk可以做得东西太多了,包括分布式锁,分布式队列都是基于zk里面得四种节点加watch机制通过长连接来实现得,但是eureka不一样,eureka是基于HTTprest来实现的,就是把服务的信息放到一个ConcurrentHashMap中,然后服务启动的时候去读取这个map,来把所有服务关联起来,然后服务器之间调用的时候通过信息,进行http调用。eureka包括两部分,一部分就是服务提供者(对于eureka来说就是客户端),一部分是服务端,客户端需要每个读取每个服务的信息,然后注册到服务端,很明显了,这个服务端就是接受客户端提供的自身的一些信息。 目前eureka是ap的 但是呢 zk是cp的,至于分布式理论下次有空再聊哈。
聊聊eureka中一些重要的概念呗
在Eureka的服务治理中,会涉及到下面一些概念:
- 服务注册:Eureka Client会通过发送REST请求的方式向Eureka Server注册自己的服务,提供自身的元数据,比如ip地址、端口、运行状况指标的url、主页地址等信息。Eureka Server接收到注册请求后,就会把这些元数据信息存储在一个双层的Map中。
- 服务续约:在服务注册后,Eureka Client会维护一个心跳来持续通知Eureka Server,说明服务一直处于可用状态,防止被剔除。Eureka Client在默认的情况下会每隔30秒发送一次心跳来进行服务续约。
- 服务同步:Eureka Server之间会互相进行注册,构建Eureka Server集群,不同Eureka Server之间会进行服务同步,用来保证服务信息的一致性。
- 获取服务:服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给Eureka Server,获取上面注册的服务清单,并且缓存在Eureka Client本地,默认缓存30秒。同时,为了性能考虑,Eureka Server也会维护一份只读的服务清单缓存,该缓存每隔30秒更新一次。
- 服务调用:服务消费者在获取到服务清单后,就可以根据清单中的服务列表信息,查找到其他服务的地址,从而进行远程调用。Eureka有Region和Zone的概念,一个Region可以包含多个Zone,在进行服务调用时,优先访问处于同一个Zone中的服务提供者。
- 服务下线:当Eureka Client需要关闭或重启时,就不希望在这个时间段内再有请求进来,所以,就需要提前先发送REST请求给Eureka Server,告诉Eureka Server自己要下线了,Eureka Server在收到请求后,就会把该服务状态置为下线(DOWN),并把该下线事件传播出去。
- 服务剔除:有时候,服务实例可能会因为网络故障等原因导致不能提供服务,而此时该实例也没有发送请求给Eureka Server来进行服务下线,所以,还需要有服务剔除的机制。Eureka Server在启动的时候会创建一个定时任务,每隔一段时间(默认60秒),从当前服务清单中把超时没有续约(默认90秒)的服务剔除。
- 自我保护:既然Eureka Server会定时剔除超时没有续约的服务,那就有可能出现一种场景,网络一段时间内发生了异常,所有的服务都没能够进行续约,Eureka Server就把所有的服务都剔除了,这样显然不太合理。所以,就有了自我保护机制,当短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。
从这些概念中,就可以知道大体的流程,Eureka Client向Eureka Server注册,并且维护心跳来进行续约,如果长时间不续约,就会被剔除。Eureka Server之间进行数据同步来形成集群,Eureka Client从Eureka Server获取服务列表,用来进行服务调用,Eureka Client服务重启前调用Eureka Server的接口进行下线操作。
说说Eureka的一些原理和服务流程
服务提供者
1、启动后,向注册中心发起register请求,注册服务
2、在运行过程中,定时向注册中心发送renew心跳,证明“我还活着”。
3、停止服务提供者,向注册中心发起cancel请求,清空当前服务注册信息。
服务消费者
1、启动后,从注册中心拉取服务注册信息
2、在运行过程中,定时更新服务注册信息。
3、服务消费者发起远程调用:
注册中心
1、启动后,从其他节点拉取服务注册信息(节点之间的数据同步)。
2、运行过程中,定时运行evict任务,剔除没有按时renew的服务(包括非正常停止和网络故障的服务)。
3、运行过程中,接收到的register、renew、cancel请求,都会同步至其他注册中心节点。