面试准备

283 阅读23分钟

Spring相关面试题-重点

SpringBoot启动流程机制

HashMap

各注册中心Euraka和nacos的区别

中通快递面试踩坑--Tomcat怎么保证运行多个项目相同的类不冲突 或一个项目怎么保证两个冲突的类同时存在

其他汇总

高可用
方法论:集群冗余+故障自动转移
细节:
端 到 反向代理:keeplived+nginx
反向代理 到 站点应用 :站点应用集群冗余+反向代理(nginx)自动探测站点应用存活性并自动进行流量迁移
站点应用 到 微服务:微服务的客户端连接池来保证自动转移的,微服务使用集群冗余;
微服务 到 缓存;一般是不设计缓存高可用,只要保证缓存挂了所有流量达到数据库不会雪崩就行,如果必要做缓存高可用的话,memcache封装客户端多读多写,redis采用sentinel哨兵集群来实现;
微服务 到 读库:一个读写分离的分组有多个读节点,通过数据库的连接池实现故障转移;
微服务 到 写库:把写库做集群冗余,比如两个写库相互同步,通过vip(keeplived)探测的方式 实现故障监控和故障转移;


高性能/高并发:
反向代理层水平扩展:通过DNS轮询,DNS对同一个域名配置不同的nginx外网IP,每次域名解析的时候返回不同的nginx外网IP
站点应用层的水平扩展:站点集群做集群,然后在nginx反向代理层配置多个站点IP,当站点应用出现瓶颈,只需要增加站点应用Ip
微服务的水平扩展:微服务做集群后,站点应用通过rpc client调用下层的微服务,rpc client会建立与多个微服务的连接,当出	现服务瓶颈,增加新的微服务节点,rpc client会建立新的连接(都是通过连接池来实现的)。
数据层:水平切分到多个节点

SOLID + HD
S(single..)单一职责原则:一个类只干一件事,实现类要单一
O(open..)开闭原则:对扩展开放,对修改关闭 
L里氏替换原则:不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义
I(interface..)接口隔离原则:一个接口只干一件事,接口要精简单一
D(dependence..)依赖倒置原则:高层不应该依赖低层,要面向接口编程;(定义补充:抽象不应该依赖细节,细节应该依赖抽象)
———————
H合成复用原则:尽量使用组合或者聚合关系实现代码复用,少使用继承
D迪米特法则:不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度

23种设计模式
创建型模式有五种:工厂方法模式 、抽象工厂模式 、单例模式 、建造者模式 、原型模式
结构型模式有七中:装饰器模式、适配器模式、桥接模式、代理模式、外观模式(Facade)、组合模式、享元模式
行为型模式有十一种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

适配器模式:将一个类的接口变成客户端期望的接口(原接口不匹配,变得匹配)
桥接模式:通过组合的方式建立两个类之间的联系
策略模式:同一行为在不同场景下有不同的实现,(比如通过策略模式实现不同的支付方式的业务)
观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新

Spring涉及的模式:
工厂模式 BeanFactory
装饰器模式 BeanWrapper
代理模式 AopProxy
委派模式 DispatcherServlet
策略模式 HandlerMapping
适配器模式 HandlerAdapter
模板模式 JdbcTemplate
观察者模式 ContextLoaderListener

同步异步是针对请求,阻塞非阻塞是针对客户端
BIO 同步阻塞IO
NIO 同步非阻塞IO
AIO 异步非阻塞IO
同步、异步 和 阻塞、非阻塞
在一个网络请求中,客户端会发一个请求到服务端
同步阻塞IO:客户端发了请求后,就一直等着服务端响应。 
客户端:阻塞 (客户端一直在等待)
请求:同步 (服务端接收到请求后就需要立即给出响应,对请求来说是同步的)

同步非阻塞IO:客户端发了请求后,就去干别的事情了,时不时过来检查服务端是否给出了响应。
客户端:非阻塞
请求:同步 (服务端接收到请求后就需要立即给出响应,对请求来说是同步的)

异步非阻塞IO:客户端发了请求后,就去干别的事情了,等到服务端给出响应回调后,客户端再进行业务逻辑。
客户端:非阻塞
请求:异步(服务端可以在完成之后再通过请求回调客户端)



Mysql存储引擎有哪些?
InnoDB ,MyIASM ,Memory

MyIASM 和 InnoDB的区别:
MyIASM没有提供对数据库事务的支持,也不支持行级锁和外键;
ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源

一个抽象类会不会加载到Spring的容器内?不会

Mysql的回表了解吗?怎么避免回表?



Springmvc工作流程
1、 用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet(也叫中央控制器)。
2、DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知,该请求该由哪个Controller来处理(并未调用Controller,只是得知)
3、DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去执行哪个Controller
4、HandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图),并层层返回给DispatcherServlet
5、DispatcherServlet将ModelAndView交给ViewReslover视图解析器解析,然后返回真正的视图。
6、DispatcherServlet将模型数据填充到视图中
7、DispatcherServlet将结果响应给用户
组件说明
-   DispatcherServlet:前端控制器,也称为中央控制器,它是整个请求响应的控制中心,组件的调用由它统一调度。
-   HandlerMapping:处理器映射器,它根据用户访问的 URL 映射到对应的后端处理器 Handler。
    也就是说它知道处理用户请求的后端处理器,但是它并不执行后端处理器,而是将处理器告诉给中央处理器。
-   HandlerAdapter:处理器适配器,它调用后端处理器中的方法,返回逻辑视图 ModelAndView 对象。
-   ViewResolver:视图解析器,将 ModelAndView 逻辑视图解析为具体的视图(如 JSP)。
-   Handler:后端处理器,对用户具体请求进行处理,也就是我们编写的 Controller 类。


MySql中redo log和undo log区别:
1,类型不同
redo log:物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改。
undo log:逻辑日志,记录每一行记录变化的过程
2,功能不同:
redo log:用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置),当MySql挂掉时,只需要重启Mysql服务,就可以将
未持久保存的数据持久到磁盘,完成数据的恢复。
undo log:
用来回滚行记录到某个版本,在InnoDB中用来实现MVCC多版本控制,执行rollback操作时,undo log可以作为事务回滚的快照读参考。
3,保证事务的特性不同
redo log:保证事务的持久性
undo log:保证事务的原子性


Spring核心组件:
Spring Core:Spring核心,它是框架最基础的部分,提供IOC和依赖注入DI特性。
Spring Context:Spring上下文容器,它是 BeanFactory 功能加强的一个子接口。
Spring Web:它提供Web应用开发的支持。
Spring MVC:它针对Web应用中MVC思想的实现。
Spring DAO:提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性。
Spring ORM:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等。
Spring AOP:即面向切面编程,它提供了与AOP联盟兼容的编程实现。

IOC:
由`Spring`来负责控制对象的生命周期和对象间的关系。所有的类都会在Spring容器中登记,告诉`Spring这`这个类是什么,
需要什么,然后Spring会在系统运行到适当的时候,把你要的东西`主动`给你,同时也把你交给`其他`需要你的Bean。
所有的类的创建、销毁都由`Spring`来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,
所以这叫控制反转(Inversion of Controller),也可以叫依赖注入 DI(Dependency Injection)。

Spring的生命周期:
1,资源定位  -如@ComponentScan扫描包
2,Bean定义  -Bean定义保存到BeanDefinition实例中
3,IOC容器装载Bean定义
4,实例化
5,依赖注入  -@Autowire依赖
6,销毁  -容器关闭

SpringBoot的启动过程:
`SpringBoot`应用程序的启动流程主要包括初始化`SpringApplication`和运行`SpringApplication`两个过程。
其中初始化`SpringApplication`包括配置基本的环境变量、资源、构造器和监听器,为运行`SpringApplciation`实例对象作准备;
而运行`SpringApplication`实例为应用程序正式启动加载过程,包括`SpringApplicationRunListeners `引用启动监控模块、`ConfigrableEnvironment`配置环境模块和监听及`ConfigrableApplicationContext`配置应用上下文。
当完成刷新应用的上下文和调用`SpringApplicationRunListener#contextPrepared`方法后表示`SpringBoot`应用程序已经启动完成。

MQ:
1,如何保证消息的有序性
    消息发送时保持顺序
    发送到通一个队列中(有先进先出的特性)
    只有一个消费者
2,如何避免消息堆积?
    提高消费者的处理速度,优化代码,提高性能
    增加队列的大小,可以采用惰性队列
    提高消费者的数量,增加消费者服务器数量
    
备注:惰性队列会尽可能的将消息存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中,它的一个重要的设计目标是能够支持更长的队列,即支持更多的消息存储。



布隆过滤器(只要问什么存在不存在,就是往布隆过滤器来回答)
如何防止缓存穿透?
采用布隆过滤器,哎这个居然没有回答出来!! 主要是没联系上!!!
1,将所有可能存在的数据哈 希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存 储系统的查询压力。
2,如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。

如何防止缓存雪崩?
在某一个时间点,一大部分热点数据过期,导致高并发
1. 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
2. 为 key 设置不同的缓存失效时间。
3,给缓存设置随机时间


热点数据key问题解决:
1,服务端本地加一层基于 LRU算法的本地缓存。
2,可以使用读写分离的架构。
(读写分离架构增加了业务代码层面的复杂度,也增加了架构层的复杂度。我们需要为多个从节点提供转发层(proxy, LVS)来实现负载均衡。
    问题:在读请求大的场景下,读写分离架构会产生延迟,对数据一致性要求高的场景,并不合适)
3,在Redis Cluster结构中对热key进行复制。
(此时可以将对应热Key进行复制并迁移至其他node,例如为热Key foo复制出3个内容完全一样的Key并名为foo2,foo3,foo4,然后将这三个Key迁移到其他node来解决单一node的热Key压力。
    问题:代码需要联动修改,同时存在多key的数据一致性问题。)


SpringCloud技术栈

redis 哈希的底层结构,并和string有什么区别

String:int,embstr
List: quicklist
	quicklist(快速列表)是 ziplist 和 linkedlist 的结合体
	每个ziplist可以放多个元素,然后ziplist和ziplist使用linkedlist来连接
Hash:ziplist,hashtable
所有的键值对的健和值的字符串长度都小于等于 64byte;
哈希对象保存的键值对数量小于 512 个;
hashtable数组+链表的结构
Set:intset,hashtable
Zset:ziplist,skiplist



Spring事务原理;
微服务和数据库连接的时候,怎么看数据库的连接池中可用连接?


hashmap底层原理:
Put
Get


hashmap的扩容机制:
第一次扩容,第二次扩容



为什么重写equals时候要重写hashcode?
在java中hashcode是object类,


幂等的解决方案:
前端解决方案:
1,按钮点击后置灰不可点击
2,Token(点击按钮前先获取到了Token,这个Token在redis中保存,客户端调用时携带这个token,校验成功就删除redis里的token,校验失败,表示redis里没有这个token,表示重复提交)
后端解决方案:
1,悲观锁
2,乐观锁
3,分布式锁
4,某个字段做唯一索引

nacos和eureka的区别:
共同点:
1,都支持服务注册和服务拉取
2,都支持服务提供者心跳的方式做健康检测
不同点:
1,nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
(一般情况下都使用临时实例,主动检测消费的服务器资源较大,服务器压力大)
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
nacos支持服务列表变更的消息推送模式,服务列表更新及时
nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式。
1)、范围不同。
Nacos 的阈值是针对某个具体 Service 的,而不是针对所有服务的;但 Eureka 的自我保护阈值是针对所有服务的。
Nacos 支持 CP 和 AP 两种;Eureka 只支持 AP 。Nacos 使用 Netty,是长连接;Eureka 是短连接,定时发送
2、保护方式不同。
Eureka 保护方式:当在短时间内,统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,
Eureka Server 不会剔除任何的微服务,等到正常后,再退出自我保护机制。自我保护开关(eureka.server.enable-self-preservation: false)。
Nacos 保护方式:当域名健康实例占总服务实例的比例小于阈值时,无论实例是否健康,都会将这个实例返回给客户端。
    这样做虽然损失了一部分流量,但是保证了集群的剩余健康实例能正常工作。

3、连接方式不同。
Nacos 支持动态刷新,在控制器(Controller)上加 @RefreshScope 注解即可,采用 Netty 连接,是长连接;
Eureka 本身不支持动态刷新,需要配合 MQ 完成动态刷新,且是短连接,是定时发送。


怎么查询内存溢出?
1,堆内存,,看哪个类,
2,栈内存
Arthas 线上调试
查看一个class类的具体信息
跟踪某个方法的返回值、入参
查看最繁忙的线程,以及是否有阻塞情况发生
验证自己的代码猜想,临时更改代码运行
测试某个方法的性能问题


ArrayList和LinkedList区别:
1. ArrayList的实现是基于数组,LinkedList的实现是基于双向链表。 
2. 对于随机访问ArrayList要优于LinkedList,ArrayList可以根据下标以O(1)时间复杂度对元素进行随机访问,
    而LinkedList的每一个元素都依靠地址指针和它后一个元素连接在一起,查找某个元素的时间复杂度是O(N)。 
3. 对于插入和删除操作,LinkedList要优于ArrayList,因为当元素被添加到LinkedList任意位置的时候,
    不需要像ArrayList那样重新计算大小或者是更新索引。 
4. LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,
   一个指向前一个元素,一个指向后一个元素。



jvisual 和 jconsole 和mat是本地调试的(不要回答这个)


feign的底层原理:
底层用jdk动态代理,封装了http请求

线程几大状态:
Sleep之后是什么状态,和wait之后有什么区别
1,创建状态
在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的
内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread 类的构造方法来实现,例
如,“Thread thread=new Thread();”。
2,就绪状态
新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,
线程将进入线程队列排队,等待CPU 服务,这表明它已经具备了运行条件。
3,运行状态
当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象
的run()方法。run()方法定义了该线程的操作和功能。
4,阻塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作时,将让出
CPU 并暂时中止自己的执行,进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被
消除后,线程才可以转入就绪状态。
5,死亡状态
线程调用stop()方法时或run()方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运
行的能力。

线程池面试题?


OSP服务治理:
负载均衡:权重Random,根据cookie hash等负载均衡策略
路由:服务路由决定一个服务请求由哪个服务实例处理。
自我保护:快速失败、隔离问题依赖,自动恢复、隔离问题服务器,自动恢复、降低重试频率、提供fallback机制
流量控制:基于服务端并根据环境及服务调用统计信息(如响应时间,错误率,CPU,memory等)自动触发。
资源隔离
方法级熔断器(Circuit breaker):熔断器的状态(打开、关闭、半开)取决于配置。例如使用50%错误率作为规则,当错误率低于50%时,熔断器处于“关闭”状态,放行所有的请求;
        当错误率高于50%的时候,进入“打开”状态,请求无法通过;这时会启动一个定时器,出发是进入“半开”状态,允许少量流量通过以测试是否恢复。
权重预热 刚发布的服务实例响应实际通常较高,缓慢增加这些实例的权重可以减免超时的发生。
Fallback 服务降级的一种机制,在服务端由于某种原因(失败,繁忙,权限或政策等)无法提供服务时,仍然向调用方返回合法响应。

SpringCloud:
Ribbon
OpenFeign
Spring Cloud Gateway
    针对请求进行鉴权,限流(如令牌桶),熔断,灰度,路由,监控,缓存
Eureka
Spring Cloud Config
Hystrix
Hystrix-dashboard
Sleuth  生成链路跟踪的数据,将数据上报到zipkin
zipkin 提交数据的持久化和数据的展示
prometheus 监控报警系统和时序列数据库,通过HTTP协议周期性抓取被监控组件的状态
grafana 图形化工具, 从很多种数据源(例如Prometheus)中读取数据信息,来展示数据

微服务集群会带来哪些问题?
对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。
进而依赖这个状态的服务被称为有状态的服务,反之成为无状态服务。
这个无状态服务原则并不是说在微服务架构里不允许存在状态,表达的真实意思就是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。
场景说明:例如我们从前在本地内存中建立的数据缓存、Session缓存,到现在微服务架构中就应该把数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。
迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。

还有数据库和缓存数据一致性的问题,解决方案:
现象:数据库主从,引入缓存:1,删除缓存中数据 2,修改数据库 3,在1后有一个get缓存操作,get为Null,然后去读数据库,这个时候主库的修改还未同步到从服务器,这个时候读到的是旧数据,然后会把旧数据放入缓存。  后面即使主从同步完成,但在缓存里还是缓存的旧数据。
解决方案:1,二次淘汰法:
线下异步淘汰:通过工具(canal)订阅从库的binlog,在cacal订阅到从库的binlog后,再进行一次cache的淘汰。
服务里淘汰:服务里删缓存后,修改数据库,然后Thread.sleep一下(sleep的时间是读缓存+写缓存的时间),然后再次进行删除缓存
2,设置过期时间,5分钟,只更新数据库,不更新缓存,缓存自己失效,这个是一致性的实时性要求不高。

Spring Cloud带来哪些优势?
Spring Cloud是目前最主流的微服务架构落地首选方案之一,是基于Spring Boot实现的开源框架,是一个全家桶,是微服务的整体技术栈。
Spring Boot是Spring 的一套快速配置脚手架,使用默认大于配置的理念,用于快速开发单个微服务。
它为服务注册发现、动态路由、负载均衡、配置管理、消息总线、熔断器、分布式链路追踪、大数据操作等提供了简单的实现,让我们可以更简洁地使用它。
正如我们前面说过的,微服务是可以独立部署、水平扩展、独立访问的服务单元,而Spring Cloud就是这些微服务的“大管家”,采用了微服务这种架构之后,项目的数量会非常多,调用链路复杂,从而管理成了很大的问题,而Spring Cloud框架恰恰提供了各种组件用于管理和治理微服务,理所应当地成了大家的首选框架。


synchronized 不能设置超时时间
ReentrantLock 在获取锁的时候需要可以设置超时时间,到了超时时间后还未获取锁就退出竞争,避免一直等待。注意这是还未得到锁,可以设置获取锁的超时时间,避免一直在等待,并不是持有锁之后的超时时间。

ReentrantLock:
lock()和tryLock(...)原理是一样的,两个执行完都要执行unlock()解锁,只是tryLock有返回值true/false
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false


synchronized不会锁泄漏的原因

ReentrantLock锁泄漏及解决方法(unlock方法放在finally中)

redis分布式锁
假设一个线程获取到锁,然后执行代码,锁如果过期了,业务还没有执行完,怎么办?
redisson在加锁成功后,会注册一个定时任务监听这个锁,每隔10秒就去查看这个锁,
如果还持有锁,就对过期时间进行续期。默认过期时间30秒。这个机制也被叫做:“看门狗(WatchDog)”

Redis分布式锁的实现原理

为什么 MySQL 默认值为 null,它不推荐?

缓存雪崩、击穿、穿透

image.png

布隆过滤器原理:预先定义一个长数组,把值根据多个不同的哈希算法除以数组长度求余,映射到对应数组索引然后标 1。(查询的时候,存在则数据库不一定有,不存在则数据库一定没有)

线程池原理

image.png 线程池饱和策略有以下几种:

线程池的四种饱和策略

Java线程池七大参数详解和配置(面试重点)

注意,项目启动后,还没有来请求的时候,线程池里是不会创建核心线程的,只有等有请求任务来的时候,才会创建线程。
1,如果正在运行的线程数量小于corePoolSize,不管其他核心线程是否空闲,总会创建一个新线程运行这个任务;
2,如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
3,如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
4,如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常RejectExecutionException。