面试Java后端框架部分yyds干货盘点
前言
本系列为面试专题,主要记录一些易混淆、易忘记的知识点;
目前共有四个部分:Java基础、计算机基础、数据库与框架;
4。框架
4。1Spring
1。Spring中的bean的作用域:singleton(单例)、prototype(多例)、request(一次请求一个)、session(一次请求一个)、globalsession(全局);
2。Component和Bean:
3。SpringMVC:DispatcherServlet、HandlerMapping、HandlerAdapter(Handler,ModelAndView)、ViewResolver、View;
4。设计模式:工厂(通过BeanFactoryApplicationContex创建对象)、代理(SpringAOP)、单例(Bean默认单例)、包装器(动态切换数据源)、观察者(事件驱动模型)、适配器(springMVC);
5。AOP代理方式:JDK动态代理(创建接口实现类)、CGLIB动态代理(创建子类的代理对象);
6。AOP术语:连接点(增强哪些方法)、切入点(实际被真正增强的方法)、通知(增强的类型)、切面(动词,把通知应用到切入点的过程);
7。事务传播行为:Transactional。支持当前事务、不支持当前事务、其他;
4。2SpringBoot
1。三个注解:Configuration、EnableAutoConfiguration、ComponentScan;
2。启动过程:1。始化准备ApplicationContext、告知服务启动、2。准备Environment、告知Environment环境准备好、(打印banner)、3。Environment设置进ApplicationContext、4。初始化ApplicationContext、告知上下文环境准备好、5。加载Config配置到ApplicationContext、告知上下文环境加载完毕、6。refresh()方法刷新应用上下文、告知应用程序启动完毕;
4。3Zookeeper
1。数据:结构化存储、由Znode(keyvalue形式)组成、维护stat状态信息;
2。节点类型:持久化节点、临时节点、有序节点、容器节点、TTL节点;
4。4Oauth2
1。工作原理:第三方客户端向资源所有者(用户)申请认证请求、资源所有者同意请求返回许可、客户端根据许可向认证服务器申请认证令牌Token、客户端根据认证令牌向资源服务器申请相关资源;
4。5Nacos
1。SpringCloud客户端集成Nacos:
springcloudcommons的METAINFspring。factories包下包含SpringCloud自动配置类的全类名;
其中有个AutoServiceRegistrationAutoConfiguration服务注册相关的配置类;
这个配置类注入了一个AutoServiceRegistration实例;
我们的NacosAutoServiceRegistration间接实现了这个接口(中间隔了个AbstractAutoServiceRegistration);
同时,AutoServiceRegistration类实现了EventListener接口,说明Nacos通过事件监听机制注册进SpringCloud;
当Webserver初始化完成之后,调用this。bind(event)方法启用事件监听;
最终会调用NacosServiceRegistry。register()方法进行服务注册;
register()方法中调用namingService。registerInstance()完成服务的注册。具体来说会做这几件事:
通过BeatReactor。addBeatInfo()创建心跳信息实现健康检测;
通过NamingProxy。registerService(),最终使用openAPI或SDK方式发生HTTP请求给Nacos服务器;
2。服务的注册服务实例:
在nacosnaming模块下的InstanceController中使用接口nacosv1nsinstance接受请求;
请求参数获得serviceName(服务名)和namespaceId(命名空间Id);
调用registerInstance注册实例;
根据服务名和命名空间id从缓存获取,service对象,没有则构建一个保存到ConcurrentHashMap集合中(也就是Nacos控制台的服务信息),并加到缓存;
使用定时任务对当前服务下的所有实例建立心跳检测机制;
基于数据一致性协议服务数据进行同步;
3。服务端查询服务实例:
客户端通过sdk或api发生请求;
解析请求参数;
根据namespaceId、serviceName获得Service实例;
从Service实例中基于srvIPs得到所有服务提供者实例;
遍历组装JSON字符串并返回;
4。Nacos服务地址动态感知原理:
客户端通过subscribe()方法来实现监听;
Nacos客户端中有一个HostReactor类,实现服务的动态更新,基本原理是:
客户端发起时间订阅后,在HostReactor中有一个UpdateTask线程,每10s发送一次Pull请求,获得服务端最新的地址列表;
对于服务端,它和服务提供者的实例之间维持了心跳检测,一旦服务提供者出现异常,则会发送一个Push消息给Nacos客户端,也就是服务端消费者;
服务消费者收到请求之后,使用HostReactor中提供的processServiceJSON解析消息,并更新本地服务地址列表;
5。SpringCloud加载配置原理:
Nacos的配置初始化依赖于SpringCloud的配置自动加载;
SpringCloud的配置自动加载在SpringCloud主程序类加载时加载进来;
而主程序类加载有这么几个关键的步骤:始化准备ApplicationContext、准备Environment、Environment设置进ApplicationContext、加载Config配置到ApplicationContext、refresh()方法刷新应用上下文;
其中在准备完Environment环境后会使用事件监听机制通知BootstrapApplicationListener加载classpath路径下查找METAINFspring。factories预定义的配置类。这些配置类是SpringCloud提供的;
在最后一步刷新应用上下文时会执行一些SpringCloud非官方的操作,比如从Nacos服务器里加载配置文件等;(最终调用的是NacosPropertySourceLocator。locate()方法)
该方法的主要作用是:初始化ConfigService对象,按照顺序分别加载共享配置、扩展配置、应用名称对应的配置;
6。客户端的长轮询定时机制
在创建ConfigService对象时使用反射机制创建NacosConfigService对象;
在NacosConfigService的构造方法里有规定长轮询定时机制的一些基本属性:HttpAgent、ClientWorker;
ClientWorker创建了两个定时调度的线程池,其中一个每隔10s检查一次配置是否发生变化。另一个用于实现客户端的定时长轮询功能。
(配置用一个cacheMap来存储,key是根据datalDgrouptenant拼接的值,值是配置文件)
(超过3000个配置集会启动多个LongPollingRunnable去执行)
检查配置这里先会检查本地配置,再检查服务端的配置是否发生改变,发生改变就使用HttpAgent调用接口请求变更配置的id等信息;
(等待30s)
然后在30s后收到一个HttpResult,里面有存在数据变更的DataID、Group、Tenant;
然后通过getServerConfig()调用HttpAgent的接口请求去Nacos服务器上读取具体的配置内容;
7。服务端的长轮询定时机制:
服务器使用ConfigController类里的listener接口接受请求,然后执行主要两个逻辑:
获取客户端请求的变化的配置,使用MD5值校验。和执行长轮询定时机制;
长轮询定时机制首先会将客户端的长轮询请求封装成ClientPolling;
然后使用一个ClientLongPolling线程池执行长轮询定时机制;
具体来说就是把ClientLongPolling实例放进一个allSubs队列中。在29。5s后拿出来执行任务。校验配置是否发生改变,发生改变则通过response返回给客户端;
返回的是存在数据变更的DataID、Group、Tenant;
(提前0。5s是避免客户端超时);
缺点是:服务器连接会消耗资源,服务器开销;
好处是:使得客户端和服务端之间在30s之内数据没有发生变化的情况下一直处于连接状态。解决了轮询机制请求频繁对资源的消耗;
8。服务器修改配置:
服务器谁配置的修改是通过监听机制实现的;
我们在Nacos服务器或通过API方式变更配置后,会发布一个LocalDataChangeEvent事件,该事件会被LongPollingService监听;
然后会使用线程池执行DataChangeTask任务,修改服务器上的配置;
4。6Sentinel
1。如何拦截请求:
SentinelWebAutoConfiguration配置类里有个FilterRegistrationBean;
这个Bean注册了个CommonFilter;
默认情况下通过规则拦截所有的请求,并且将这些请求设置为Sentinel的资源;
2。Sentinel的工作原理
Sentinel依赖ProcessorSlot调用链进行工作,使用的是责任链模式,链表元素是一个Slot槽;
Sentinel里给我们实现了很多Slot槽,其中有FlowSlot(流控槽)、StatisticSlot(统计槽)、DegradeSlot(熔断槽);
先调用lookProcessChain()方法从缓存中获取slot调用链,没有就创建一个;
然后以遍历链表的方式完成流控、熔断和统计等功能;
进入每个槽的方法是:xxxSlot。entry()方法,里面都是调用两个方法(除了统计槽外),checkXxx()检查规则和fireEntry()调用下一个Slot槽;
3。流控槽lowSlot:
先到Sentinel控制台获取流控规则;
会根据获取到的信息做一些判断和匹配,如:请求路径、是否集群、阈值类型(QPS,并发线程数)、流控类型(直接,关联,链路)、流控效果(快速失败,排队等待等);
在代码层面根据:流控类型(Strategy)和针对来源(limitApp)用ifelse的方式给我们实现了三种场景:
1。优先保证针对来源(QPS每秒的响应请求数);
2。优先保证规则,所有来源共用一个规则;
3。其他情况(针对场景1中没有配置规则的应用来源);
选定场景后会根据流控效果进行相应处理,有四种处理策略:
1。直接拒绝:抛异常;
2。匀速排队:请求以匀速的速度通过;
3。冷启动:QPS阈值除以冷加载因子得预热时长,预热时长后达到QPS阈值(秒杀场景);
4。匀速冷启动;
4。熔断槽DegradeSlot:
熔断功能在Sentinel1。8。0版本前后有较大变化;
先根据资源名称resourceName(请求路径)获取断路器;
循环判断每个断路器;
如果断路器状态为关闭(State。CLOSED),则通过;
如果断路器状态为半开,则拒绝;
如果断路器状态为打开,则尝试转换状态为半开;
状态转为半开会重置(retry)一个超时时间,如果在这个时间内请求成功,则断路器关闭。反之打开;
断路器打开的条件根据熔断策略去配置,有:慢比例调用、异常比例、异常数;
断路器打开的条件如下:
慢比例调用:统计时间内请求数大于最小请求数慢调用(响应时间大于RT)比例大于阈值;
异常比例:统计时间内请求数大于最小请求数请求异常的比例大于阈值;
异常数:统计时间内的异常数目超过阈值;
5。统计槽StatisticSlot:
统计槽的实现与其他槽不一样,它先调用fireEntry()方法执行后续槽,再进行统计;
它主要统计的是两个核心指标:增加线程数和请求通过数;
Sentinel使用的是滑动窗口算法来统计单位时间内的请求数;
简单来说就是用一个环形数组,数组元素表示单位时间内的请求统计量,随单位时间循环遍历数组;
具体实现来看是使用一个ArrayMetric指标数组,里面有个LeapArray环形数组,ArrayMetric指标数组里的所有方法都是操作LeapArray环形数组的;
LeapArray环形数组里的元素是WindowWrap窗口包装类,也就是我们说的窗口,包装类包装的是MetricBucket指标桶;
每个MetricBucket指标桶统计的是单位时间内的请求成功、失败、异常、响应时长等指标数据,指标数据存储在LongAdder〔〕数组里;
LongAdder对象是JDK1。8新增的类,用于在高并发场景下代替AtomicLong,以用空间换时间的方式降低了CAS失败的概率,从而提高性能;
(AtomicLong的高并发性能问题:使用CAS算法,过多线程同时竞争同一个变量,大量线程会竞争失败,处于自旋状态消耗性能。LongAdder内部维护一个Cells数组,把一个变量分解为多个变量,避免大量线程同时失效);
(总结来说LongAdder比AtomicLong的有几处优化:使用Cells数组分解变量、获取资源失败时尝试获取其他原子变量的锁而不是自旋CAS重试、Cells数组占用内存较大使用惰性加载)
小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏评论转发关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【666】即可免费获取