Java面试题--Framework

1. springmvc整合web,整合spring
  1. SpringMVC中定义了一个前端控制器DispatcherServlet,这个控制器继承实现自HttpServlet;
  2. 当我们在web.xml中配置了拦截路径后,前端控制器会拦截对应的请求,并调用处理器映射器HandlerMapping来查询对应的处理器Handler;
  3. 前端控制器再将查询的Handler处理器发送至处理器适配器HandlerAdapter中执行对应的处理器,最后将执行结果返回给前端控制器;
  4. 前端控制器通过视图解析器ViewResolver来解析返回结果,最终生成View对象响应给前端。
2. spring常⽤的注解,每个注解的作⽤

@Configuration、@ComponentScan 指定为配置类;包扫描,设置spring启动扫描那些类

@Bean、@Import Bean对象注解,将指定方法的返回值设置为一个Bean对象;导入第三方类到Sprin中

@PropertySource 加载指定配置文件

@ConfigurationProperty 指定配置前缀

@Value 为基本类型注入值,来自于配置文件中

@Component @Controller、@Service、@Respository 将对应的类标记为Spring的Bean对象

@PostConstruct、@PreDestroy 设置Bean对象的生命周期

@Autowired、@Qualifier、@Primary 用于从SpringIoC容器中注入Bean对象使用

@Scope 设置Bean对象的作用域,单例singleton和非单例protoType

3. springmvc执⾏流程
  1. SpringMVC中有一个前端控制器DispatcherServlet,这个控制器继承实现自HttpServlet;
  2. 当我们在web.xml中配置了拦截路径后,前端控制器会拦截对应的请求,并调用处理器映射器HandlerMapping来查询对应的处理器Handler;
  3. 前端控制器再将查询的Handler处理器发送至处理器适配器HandlerAdapter(拦截器执行位置)中执行对应的处理器方法,最后将执行结果返回给前端控制器;
  4. 前端控制器通过视图解析器ViewResolver来解析返回结果,最终生成View对象响应给前端。
  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获,执行doDispatch()方法;
  2. doDispatch()方法中对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用处理器映射器(HandlerMapping)获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
  3. DispatcherServlet 根据获得的Handler,选择一个合适的处理器适配器(HandlerAdapter)。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
  6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
  7. ViewResolver 结合Model和View,来渲染视图
  8. 将渲染结果返回给客户端。
4.springmvc参数绑定原理

Spring通过参数名称进行参数绑定,并且会自动进行类型转换

详:当前端控制器DispatcherServlet将请求经过一系列处理之后进入处理器适配器HandlerAdapter中,而它的实现类(注解方式使用的适配器)RequestMappingHandlerAdapterafterPropertiesSet()方法中通过调用getDefaultArgumentResolvers方法实现参数绑定。在getDefaultArgumentResolvers方法中,调用HandlerMethodArgumentResolver接口的不同的实现类来完成对不同类型的参数的绑定:如,RequestParamMethodArgumentResolver是对@RequestParam注解进行参数绑定的参数绑定器;PathVariableMethodArgumentResolver是对RestFul风格的路径参数的绑定。

springmvc返回json格式数据
@ResponseBody@RestController
springmvc⽂件上传

使用MultipartResolver接口的实现类CommonsMultipartResolver

而CommonsMultipartResolver调用apache的文件上传下载的功能

springmvc和spring⼦⽗容器关系

Spring父容器:ContextLoaderListener监听创建Spring容器(WebApplicationContext这个上下文对象)主要用于整个Web应用程序需要共享的一些组件,比如DAO、数据库的ConnectionFactory等;

SpringMVC容器:DispatcherServlet创建的SpringMVC的容器主要用于和该Servlet相关的一些组件,比如Controller、ViewResovler等。

子容器(SpringMVC容器)可以访问父容器(Spring容器)的Bean,父容器(Spring容器)不能访问子容器(SpringMVC容器)的Bean。

SpringMVC容器的创建依赖于Spring容器。

子类容器和父类容器之间具有一定的独立性,当在Spring容器中配置AOP后,其无法拦截Controller,因此需要在SpringMVC的配置中开启AOP代理。

ServletContext:web应用的全局上下文对象;为Spring容器提供宿主环境。

Web容器启动时,会触发web.xml中配置的监听器ContextLoaderListener,该监听器会初始化Spring的启动上下文WebApplicationContext,即SpringIoC容器;当该容器创建成功后,会通过servletContext.setAttribute()存储到ServletContext容器中。

当ContextLoaderListener监听器初始化完成后,会开始初始化web.xml中的Servlet,而当我们配置的是SpringMVC的DispatcherServlet时,就会开始创建SpringMVC的容器,创建时,DispatcherServlet会先从ServletContext中获取SpringIoc容器,作为自己的父类容器,有了父类上下文之后,再初始化自己的上下文。初始化完成后,也会添加到ServletContext容器中。

springmvc的controller线程安全吗?怎么解决

不安全。

SpringMVC中的Controller默认是单例的,如果在Controller中定义了局部变量,则该类的对象就是一个有状态对象,因为其需要保存局部变量,此时在多线程环境下,如果涉及到成员变量的修改就会出现线程安全问题。

解决方式:1. 将Controller修改为非单例(影响性能,不建议);2. 或者在Controller中不定义成员变量,此时就算处于多线程环境中,因为没有共享变量的存在,因此仍处于线程安全状态(建议使用,结合3使用);3. 在Controller中使用ThreadLocal变量。

吊打面试官:防止出现局部变量逃逸。

springmvc⽀持restful风格吗?有哪些注解

支持。

@PathVariable从路径中获取参数

@RequestBody从请求体中获取数据,获取的是json格式数据

@RequestParam 获取url拼接的值

springmvc获得request,response,session的⼏种⽅式

request:直接在处理器方法的参数列表中添加HttpServletRequest参数即可

resposne:直接在处理器方法的参数列表中添加HttpServletResponse参数即可

session:直接在处理器方法的参数列表中添加HttpSession参数即可 或直接在参数列表中使用注解@SessionAttribute即可

当添加了这些参数后,SpringMVC在进行参数绑定时,会使用对应的参数解析器去从请求中获取对应的数据。

注解⽅式springmvc使⽤的是那个处理器映射器requestmapping,处理器适配器adaper

SpringMVC在加载处理器映射器时HandlerMapping:

RequestMappingHandlerMapping:注解类映射器

SimpleUrlHandlerMapping:直接通过简单的url匹配的方式将其映射到一个处理器

BeanNameUrlHandlerMapping:直接以bean的名称作为访问路径,但有个硬性条件就是bean的名称必须以/开始。

处理器适配器HandlerAdapter:

RequestMappingHandlerAdapter:主要是适配注解类处理器,注解类处理器就是我们经常使用的@Controller的这类处理器。

HttpRequestHandlerAdapter:主要是适配静态资源处理器,静态资源处理器就是实现了HttpRequestHandler接口的处理器,这类处理器的作用是处理通过SpringMVC来访问的静态资源的请求。

SimpleControllerHandlerAdapter:适配实现了Controller接口或Controller接口子类的处理器。

@Controller:RequestMappingHandlerMapping

@RequestMapping:RequestMappingHandlerAdapter

springmvc怎么优化
  1. 对指定的Url进行缓存,SpringMVC本身并没有对请求进行缓存操作,因此可以对一些请求进行缓存操作;如,对于一些get请求,其多次请求的结果都是相同的,因此可以对这类请求进行拦截,使用拦截器拦截后将这种请求的结果存在缓存中(redis),下次会直接将该请求拦截到缓存中获取数据,而不用进入Controller。
  2. Controller采用单例(默认就是),非单例模式下,每次有请求进入前端控制器,都会创建一个Controller的Bean对象,这会降低SpringMVC的执行效率。
springmvc的核⼼组件有哪些

前端控制器DispatcherServlet:用于拦截指定的请求,拦截后进行下一步操作

处理器映射器HandlerMapping:通过前端控制器拦截的请求来匹配对应的处理器方法

处理器适配器HandlerAdapter:找到对应的处理器后,交由处理器适配器进行执行对应的处理器方法

视图解析器ViewResolver:将返回结果进行解析,生成View对象,响应给前端

springmvc怎样设定重定向和转发

此时不能使用@ResponseBody注解

return "forward:page.jsp"; == return "page.jsp";	//默认是转发
return "redirect:page.jsp";							//重定向
springmvc怎么处理异常

实现HandlerExceptionResolver接口

或使用注解@ControllerAdvice/@RestControllerAdvice控制器类增强注解,在方法上使用@ExceptionHandler,方法中可以传入不同的异常种类,来处理不同的异常。

实现Exception接口?

实现了接口,可以重写所有的运行时异常;若不是实现该接口,则可能出现其它异常。

详:

springmvc拦截器怎么使⽤

需要在配置文件中配置拦截器或使用注解开启开启拦截器配置

实现HandlerInterceptor接口,重写三个方法:preHandle、postHandle、afterCompletion

也可以通过配置多个拦截器,形成责任链,每个拦截器负责自己的职责。

当prehandle返回值为false时,postHandle和afterCompletion都不会执行。

拦截器和过滤器的区别

拦截器Interceptor:拦截器是属于SpringMVC的,拦截器的执行在过滤器之后。拦截器是在指定的方法调用前后执行预先设定好的代码,阻止原代码的执行。只能对访问SpringMVC的请求进行拦截;拦截器是基于Java的反射机制实现;拦截器更灵活,功能强;

过滤器Filter:过滤器是属于Servlet的,过滤器在拦截器之前执行。能够对所有的请求进行拦截;过滤器是基于函数回调实现的,能实现的功能有限;主要用于针对URL设置编码、过滤参数、安全校验等。

什么是restful风格
restful风格怎么做crud

按照请求类型来划分

PUT:修改更新数据,JSON字符串传值

POST:新增插入数据,JSON字符串传值

GET:查询获取数据,路径参数

DELETE:删除数据,路径参数

为什么要选择springmvc框架

SpringMVC的优点:

(1)它是基于组件技术的。全部的应用对象,无论控制器和视图,还是业务对象之类的都是 java组件.并且和Spring提供的其他基础结构紧密集成.

(2)不依赖于Servlet API(目标虽是如此,但是在实现的时候确实是依赖于Servlet的)

(3)可以任意使用各种视图技术,而不仅仅局限于JSP

(4) 支持各种请求资源的映射策略

(5)它应是易于扩展的

为什么选择spring框架,使⽤有什么好处
spring启动流程

当Web应用启动后:即当ServletContext容器创建后,ContextLoaderListener监听到ServletContext容器创建后,会触发contextInitialized()方法,在这个方法中会调用父类ContextLoaderinitWebApplicationContext()方法对SpringIoC容器进行初始化;在该方法中会调用本类的createWebApplicationContext(servletContext)创建WebApplicationContext容器(SpringIoC容器),并调用setParent(ServletContext)将ServletContext设置为SpringIoC的父类容器;之后调用configureAndRefreshWebApplicationContext()方法来加载Spring的配置文件,创建Bean实例;最后一步调用setAttribute()将自己注册到ServletContext容器中。接下来是配置Servlet…(SpringMVC容器就是属于Servlet)

spring 特性ioc,di是什么,实现原理是什么

IoC:SpringIoC就是控制反转;在没有使用Spring框架的时候,当我们需要一个对象的时候,我们都是直接去new的,这样就会出现很高程度的代码耦合问题,一旦当需要的对象发生了改变,我们的代码也就需要发生改变,并且我们需要去确定什么时候需要创建该对象,而Spring的IoC就很好的解决了这个问题,让所有的类实现接口,不在类中new对象,而是由Spring去创建这些对象,放在Spring的容器中,当需要使用该对象时,只需要在自己的类中声明该对象的接口,通过注入来获取实现类对象,从而实现了代码的解耦。而这些实现类的创建和销毁都由Spring完成,所以IoC的核心思想就是处理这些相互依赖的对象的创建、协调工作,并对代码进行解耦。

DI:依赖注入,就是IoC在运行过程中,动态的向某个对象提供它所需要的其它对象。DI是通过反射机制实现的,在实例化一个类时,通过反射调用它的setXxx()方法来注入该对象实例化所需要的所有对象,并将该对象保存在SpringIoC容器中,以提供给其它对象使用。

实现原理:工厂模式+反射+配置文件

@autowird和@qualifier,@resourse区别

@Autowried:是Spring的自动装配注解;默认按照类型注入;当存在多个相同类型Bean对象时,或没有该类型对象时,均BeanCreationException异常;因此可以配合Qualifier的名称注入使用;可以对类成员变量、方法及构造函数进行标注,完成自动装配。

@Qualifier:按照名字注入;一般配合Autowried使用。

@Resource:是J2EE基于JSR-250规范的注解;默认按照名称装配;若没有指定name属性,则按字段名装配;当找不到时,按照类型装配;一旦指定name属性,则只按照name属性注入。

spring bean⽣命周期,实现哪些接口可以自定义⽣命周期⾏为

Bean的生命周期:

1、实例化bean对象(通过构造方法或者工厂方法)

2、设置对象属性(setter等)(依赖注入)

3、如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)

4、如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身

5、将Bean实例传递给Bean的后置处理器的BeanPostProcessorpostProcessBeforeInitialization(Object bean, String beanname)方法

6、调用Bean的初始化方法init-mothod()

7、将Bean实例传递给Bean的后置处理器的BeanPostProcessorpostProcessAfterInitialization(Object bean, String beanname)方法

8、使用Bean

9、容器关闭之前,调用Bean的销毁方法destroy-method()

spring中的循环注⼊是什么意思,怎么解决

构造器循环依赖:此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

Setter方法循环注入

  1. 单例模式:因为当构造方法执行完毕后,暴露一个“ObjectFactory”用于返回一个提前暴露一个创建中的Bean,并将标识符放到“当前创建Bean池”,然后开始setter注入;另一个Bean的创建也是如此,若setter中出现循环依赖,则会将暴露出的Bean注入,从而解决循环依赖的问题;
  2. ProtoType:因为该类型的Bean对象不会被缓存,因此无法提前暴露一个创建中的Bean。抛出BeanCurrentlyInCreationException异常。
spring事务能跨数据源吗?不能应该怎么解决

不可以。Spring Transaction默认是单数据源

可以采用编程式事务方式,采用AOP实现开启对应数据源的事务。

或者采用分布式事务两阶段提交、TCC、本地消息表。

两阶段提交(AT)

两阶段提交,顾名思义就是分两步提交。存在一个负责协调各个本地资源管理器的事务管理器,本地资源管理器一般是由数据库实现的,事务管理器在第一阶段的时候询问各个资源管理器是否就绪?如果收到每个资源的回复都是yes,则在第二阶段提交事务,如果存在一个资源的回复是no,则全部回滚事务。

大致流程:

  • 第一阶段:事务管理器向所有本地资源管理器发起请求,询问是否Ready,所有参与者都将本地事务能否成功的信息反馈给协调者;

  • 第二阶段:事务管理器根据本地资源管理器的反馈,通知所有的本地资源管理器,步调一致地在所有分支上提交或回滚。

存在的问题:

  • 同步阻塞:当参与事务者存在占用公共资源的情况,其中一个占用了资源,其他事务参与者就只能阻塞等待资源释放,处于阻塞状态。
  • 单点故障:一旦事务管理器出现故障,整个系统不可用
  • 数据不一致:在阶段二中,如果事务管理器在发出部分commit消息后,网络发生异常,那么只有部分参与者接收到commit消息,就是说只有部分参与者可以提交事务,其它的无法提交事务,使得系统出现数据不一致情况
  • 不确定性:当事务管理器发送commit之后,并且此时只有一个参与者收到了commit,那么当该参与者与事务管理器同时宕机,重新选举的事务管理器无法确定该条消息是否提交成功。

解决:三阶段提交。加入了超时机制和中间多了一次提交,但也存在一些数据不一致情况

TCC

解决了协调者单点故障问题,由业务方发起并完成这个业务活动。业务活动管理器也变成多点,引入了集群;

同步阻塞:引入超时,超时后进行补偿,并且不会锁定整个资源,将资源转换为业务逻辑形式,粒度变小;

数据一致性,有了补偿机制之后,由业务活动管理器控制一致性

大致流程:

Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)

Confirm阶段:确认执行真正执行业务,不做任何业务检查,只是用Try解读那预留的业务资源,Confirm操作满足幂等性。要求具备幂等性设设计,Confirm失败后需要及进行重试

Cancel阶段:取消执行,释放Try阶段预留的业务资源Cancel操作满足幂等性,Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。

本地消息表
spring如何保证⾼并发下Controller安全
  1. 将Controller声明为单例Bean、Controller中不定义成员变量、无状态对象在多线程下是安全的,并且要保证被注入的其它Bean对象也都是无状态对象,例如:mapper、service等。
  2. 采用ThreadLocal方式存储数据
  3. 万一必须要定义一个非静态成员变量时候,则通过注解 @Scope(“prototype”),将其设置为多例模式
spring事务配置⽅式有⼏种
  1. 编程式事务配置

使用AOP的环绕通知的方式,织入到原始业务对象的执行过程中。

  1. 基于xml的声明式事务配置

使用tx命名空间配置事务专属通知类,使用aop:config配置aop:advisor。

  1. 基于注解的声明式事务配置

@EnableTransactionManagement开启事务,需要事务的方法上@Transactional开启事务。

Spring的事务管理是底层通过Aop的方式来实现的。声明式事务是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也就符合非侵入式的轻量级的容器的概念。

Spring的事务管理

Spring的事务管理器

Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现。每个事务管理器都会充当某一特定平台的事务实现的门面,这使得用户在Spring中使用事务时,几乎不用关注实际的事务实现是什么。

Spring事务的只读

“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可。

Spring事务的事务超时

为了使应用程序更好的运行,事务不能运行太长的时间。因此,声明式事务的第四个特性就是超时。

Spring 事务的回滚规则

默认情况下,事务只有在遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚,但是也可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

Spring事务管理的方式

Spring事务管理主要包括3个接口,Spring的事务主要是由他们三个共同完成的。

1)PlatformTransactionManager:事务管理器–主要用于平台相关事务的管理主要有三个方法:

​ commit 事务提交;

​ rollback 事务回滚;

​ getTransaction 获取事务状态。

2)TransactionDefinition:事务定义信息–用来定义事务相关的属性,给事务管理器PlatformTransactionManager使用,这个接口有下面四个主要方法:

getIsolationLevel:获取隔离级别;

getPropagationBehavior:获取传播行为;

getTimeout:获取超时时间;

isReadOnly:是否只读(保存、更新、删除时属性变为false–可读写,查询时为true–只读)

事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。

3)TransactionStatus:事务具体运行状态–事务管理过程中,每个时间点事务的状态信息。

例如它的几个方法:

hasSavepoint():返回这个事务内部是否包含一个保存点,

isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚

isNewTransaction():判断当前事务是否是一个新事务

spring注解扫描原理

Spring中存在一个专门用于包扫描的类ClassPathBeanDefinitionScanner,它有一个doScan()方法,去扫描指定的包;在该方法中调用了findCandidateComponents方法,该方法调用scanCandidateComponents方法,在这个方法中将包替换为了具体的class类,并解析为Resource对象,通过反射获取到类的注解信息,遍历判断isCandidateComponent是否是Spring的注解,完成对应的加载。

spring核⼼模块有哪些

应用层模块:web集成和实现(Servlet)、数据访问和数据集成(JDBC)

中间层技术:AOP、Aspects、Messaging

核心容器:Beans、Core、Context、SpringEL表达式

spring 框架中都⽤到了哪些设计模式

单例模式:Bean默认为单例模式

工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

模板模式:用来解决重复性代码。Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到模板模式。

spring BeanFactory和FactoryBean区别

BeanFactory:BeanFactory是 IoC 容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

FactoryBean:FactoryBean 是一个 Bean,实现了 FactoryBean 接口的类有能力改变 bean,FactoryBean 希望你实现了它之后返回一些内容,Spring 会按照这些内容去注册 bean。用户可以通过实现该接口定制实例化Bean的逻辑。

Spring 中为我们提供了两种类型的 bean,一种就是普通的 bean,我们通过 getBean(id) 方法获得是该 bean 的实际类型,另外还有一种 bean 是 FactoryBean,也就是工厂 bean,我们通过 getBean(id) 获得是该工厂所产生的 Bean 的实例,而不是该 FactoryBean 的实例。

BeanFactory在创建Bean对象的时候,通过getBean方法获取时,若该Bean对象实现了FactoryBean接口,则该Bean对象的创建由FactoryBean创建返回;若没有实现FactoryBean接口,则直接由BeanFactory创建。

通常情况下:bean 无须自己实现工厂模式,Spring 容器担任工厂 角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

什么是aop,aop实现原理

面向切面编程。 是对面向对象编程的一种补充,是一种横向编程的方式;在运行期间通过动态代理的方式向目标类注入增强代码。对程序进行增强:不修改源码的情况下,权限校验,日志记录,性能监控,事务控制。

底层采用两种动态代理实现:JDK动态代理和Cglib动态代理。

Aop好处:

  1. 降低模块之间的耦合度
  2. 使系统容易扩展
  3. 避免修改业务代码,避免引入重复代码,更好的代码复用
切⼊点、通知、植⼊点、切⾯是什么

连接点:是各种不同的方法;切入点:是被挖掉共性功能的方法;

通知:被挖掉的共性功能通过方法体现,这个方法就称为通知,@Before、@After、@AfterReturning、@AfterThrowing、@Around ;

植入点:通知植入的位置;切面:切面就是由这些通知所组成的类;

代理⽅式有⼏种,怎么配置,实现原理是什么

静态代理:

JDK动态代理:采用反射机制,创建出和被代理类实现相同接口的代理对象

Cglib动态代理:(导jar包)使用Code生成类库的方式,创建被代理类的子类,使用Enhancer创建一个类的字节码,并设置其父类;setCallback(new MethodInterceptor())

spring事物传播机制有哪些

REQUIRED:当前方法必须在事务中执行;若不存在则创建一个事务;若存在则直接运行。

SUPPORTS:当前方法不需要事务;若存在也会执行。

NEVER:当前方法不能在事务中执行;若存在则抛出异常。

为什么选择springboot
  1. 使用方便:SpringBoot简化了SSM项目中繁杂的配置文件,只需要再pom文件中引入SpringBoot的起步依赖即可,在起步依赖中,SpringBoot提前对一些配置做了默认配置,开发人员只需要根据自身情况选择性修改即可。
  2. 集成了很多依赖包:SpringBoot提供了很多技术的起步依赖,在使用时不需要开发人员去寻找和配置相关依赖,仅需要导入SpringBoot提供的对应的起步依赖即可,也无需担心版本冲突问题。使用非常方便。
  3. 大大提升了开发人员的开发效率,不需要花费大量时间在项目的搭建上,这正是SpringBoot的初衷。
springboot提供了哪些核⼼功能
  1. 起步依赖:起步依赖就是一个Maven项目对象模型,定义了对于其他库的依赖传递,为项目提供某种功能,就是完成某种功能所需要的依赖集合,已经通过预先测试的,没有版本问题的一个最佳的依赖组合。
  2. 自动配置:Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的。

SpringBoot只是提供了一种快速使用Spring的方式,它可以整合Mybatis、Junit、Redis等技术,极大地提升了开发人员的开发效率。

springboot中的 Starter 是什么

起步依赖:是一种可以指定基于功能依赖,是利用Maven的依赖传递性,把常用类库组合在一起,为提供特定功能定制的依赖,并且不会存在版本冲突等问题。

springboot常⽤的 Starter 有哪些

spring-boot-starter-parent:SpringBoot的父依赖,SpringBoot项目必须实现的

spring-boot-starter-logging:提供Logging日志功能

spring-boot-starter-thymeleaf:使用Thymeleaf视图构造MVC Web应用程序的启动器

spring-boot-starter-web:shi用SpringMVC构建Web,包括RestFul应用程序,默认内嵌Tomcat服务器

spring-boot-starter-test:整合Junit测试

spring-boot-starter-data-redis:操作Redis客户端

spring-boot-starter-actuator:应用监控功能

spring-boot-starter-dubbo:使用Dubbo框架相关功能

springboot的配置⽂件有哪⼏种格式

properties > yml > yaml;三种,同目录优先级。

springboot怎么管理配置⽂件版本

多配置文件时:

​ application-dev.properties/yml 开发环境
​ application-test.properties/yml 测试环境
​ application-pro.properties/yml 生产环境

yml多文档方式:
在yml中使用 — 分隔不同配置

激活方式:

​ 配置文件中:spring.profiles.active=dev

​ 命令行:java –jar xxx.jar --spring.profiles.active=dev

springboot有哪⼏种读取配置的⽅式?
  1. 使用@Value注解
  2. 使用Environment对象
  3. 使用@ConfigurationProperties(prefix=""),需要set方法注入
springboot的核⼼注解是哪个

@SpringBootApplication

​ @SpringBootConfiguration

​ @Configuration

@EnableAutoConfiguration

@Import({AutoConfigurationImportSelector.class})

​ 该注解导入了一个类:AutoConfigurationImportSelector,该类是实现了ImportSelector接口,实现了selectImports方法,该方法中会读取META-INF/spring.factories文件中的org.springframework.boot.autoconfigure.EnableAutoConfiguration键对应的配置,将对应的配置项添加到SpringBoot的包扫描中,进行自动配置。而那些对应的配置中采用@Conditional注解来判断如何进行配置。

​ @ComponentScan:自动扫描加载符合条件的组件,将这些组件添加到Spring容器中。

springboot⾃动配置原理是什么?

在SpringBoot项目的启动类中@SpringBootApplication注解,该注解中包含一个@EnableAutoConfiguration,在该注解内部,又使用@Import导入了一个AutoConfigurationImportSelector类,这个类是实现自ImportSelector接口,实现了该接口的selectImports方法,该方法内部实现通过扫描META-INF/spring.factories文件,导入其中key为EnableAutoConfiguration的配置类,这些配置类中通过@Conditional注解来进行判断需要如何配置,需要加载那些Bean对象,将其加入到SpringIoC容器中。

什么是dubbo

Dubbo是一个开源的分布式SOA框架,相对于普通分布式架构,它在服务方和消费方之间提供了一个ESB企业总线;服务方可以将自己的服务地址注册到ESB总线中,消费方直接从ESB中获取服务方地址;在传统分布式架构中,一旦服务方的地址发生变更,消费方就无法获取到最新的服务方地址,需要修改消费方代码才能重新获取最新服务地址;而Dubbo提供的ESB总线功能,会在服务方地址发生变化后,会把新的地址推送给消费方,前提是消费方订阅了该服务方。

Dubbo中包含四个角色:注册中心(Zookeeper)、服务提供方、服务消费方、监控中心;

服务提供方会将服务地址注册到注册中心,消费方从注册中心订阅服务,获取服务方的地址;当需要使用服务方的服务时,通过RPC远程调用的方式调用服务,当服务方的地址发生变化,注册中心会自动将最新地址推送给消费方,提升了系统的可用性。监控中心用来监控服务调用次数和时间。

Dubbo还提供:服务重试、多版本、负载均衡、集群容错、服务降级等功能。

dubbo⽀持哪些协议

dubbo协议:单一长连接,就是建立连接过后可以持续发送请求,无须再建立连接。进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的 场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。

rmi协议:走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。而短连接,每次要发送请求之前,需要先重新建立一次连接。

hessian 协议:走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输, 一般较少用。

http协议:走json序列化

webservice协议:走SOAP文本序列化

dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。但是 hessian 是 其默认的序列化协议。

dubbo怎么做负载均衡

loadbalance=“random”

Dubbo内置四种负载均衡策略:

  1. RandomLoadBalance:随机负载均衡(默认),可以设置权重
  2. RoundRobinLoadBalance:轮询负载均衡,均匀分配流量;但是当某个服务器网络不好,性能差时,会导致负载过高,可以设置权重,流量少一些。
  3. LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机;,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给不活跃的性 能差的机器更少的请求。
  4. ConsistentHashLoadBalance:一致性Hash负载均衡,相同参数的请求总是落在一个服务器上
dubbo的集群容错策略

failover cluster 模式:失败自动切换,自动重试其他机器,默认就是这个;常用于读操作。

failfast cluster 模式:一次调用失败就立即失败,常见于非幂等性的写操作,比如新增一条记录(调用失败就立即失败)

failsafe cluster 模式:出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。

failback cluster 模式:失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。

forking cluster 模式:并行调用多个 provider,只要一个成功就立即返回。常用于实时性要求比较高的读操作,但是会浪费更多 的服务资源,可通过 forks=“2” 来设置最大并行数。

broadcacst cluster模式:逐个调用所有的 provider。任何一个 provider 出错则报错(从 2.1.0 版本开始支持)。通常用于通知 所有提供者更新缓存或日志等本地资源信息。

幂等性:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的

幂等性设计:调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生

dubbo服务降级怎么实现

指 消费方降级。

  1. Mock Null降级处理
@Reference(mock="force:return null")  //配置后不再调用UserService服务,直接返回null
private UserService userService;
@Reference(mock="fail:return null") // 调用失败后,再返回null
private UserService userService;
  1. Mock Class降级处理

配置dubbo服务消费引用标签的mock参数值为true,同时定义服务名+Mock 的自定义类;这样在服务接口超时失败时,就会返回自定义的相应信息;

public class UserServiceMock implements UserService {
    // 对方法进行降级。
}
dubbo熔断机制有哪些

通过Dubbo的Filter对Dubbo进行扩展,从而使得每次服务发起调用都可以得到监控,从而可以监控每次服务的调用。

对自动判断服务提供端是否宕机:通过一个记录器对每个方法出现RPC异常进行记录,并且可以配置在某个时间段内连续出现多少个异常可判定为服务提供端出现了宕机,从而进行服务降级。

自动恢复远程服务调用:通过配置检查服务的频率来达到定时检查远程服务是否可用,从而去除服务降级。

为什么⽤zookeeper做dubbo注册中⼼
dubbo远程调⽤的原理
dubbo⽹络通信怎么做的

网络通信框架,实现了 sync-over-async 和 request-response 消息机制

dubbo怎么连接zookeeper的
dubbo实现了哪些SPI
dubbo服务发布原理
  1. 先暴露本地服务
  2. 再暴露远程服务
  3. 启动netty
  4. 打开连接zk
  5. 注册到zk
  6. 监听zk
dubbo服务引⽤原理
dubbo怎么解决消息粘包

粘包:因为dubbo默认是使用长连接,在一个连接中发送多个数据包,而服务器端一次接收的字节数是不确定的,因此可能出现获取到的包包含多个小的数据包,也可能出现不完整的包。

拆包:若服务器端接受的字节数太小,多次才可以将一个数据包完全接收,此时发生了拆包。

解决:TCP无法理解上层业务数据,因此通过上层应用协议栈设计来解决

  1. 消息定长,每条消息固定长度,长度不足时使用空格补位;
  2. 在包尾添加回车换行符分割;
  3. 将消息分为消息头和消息体,消息头中包含消息总长度等信息。
  4. 采用复杂的应用层协议(Dubbo协议)
dubbo消息异步变同步实现原理

dubbo采用的是基于nettyNIO的异步请求。

当发出请求后,会使线程进入wait状态,等待响应回来会唤醒它。

dubbo是怎么和spring整合
请求失败dubbo怎么处理,默认尝试连接⼏次

默认两次

zookeeper在项⽬中哪⾥使⽤到,为什么要⽤

项目中使用了Dubbo,因此采用ZK作为服务注册中心;

并且使用ZK作为分布式锁,用来处理分布式架构的线程安全。

zookeeper能做什么事,在项⽬中

分布式协调:当A系统发送了个消息到MQ中,B系统消费后,A系统如何知晓B系统的执行结果;采用A系统创建一个节点监听指定的节点,一旦执行结束指定节点发生变化,则A系统收到执行结果。

分布式锁:每一个客户端建立和Zookeeper的连接,并在指定持久化节点下创建一个临时顺序节点,当某个客户端持有的节点是顺序最小的,则认为它获取到了锁,可以执行;否则,则会查询到比自己小的那个节点,监听它的删除事件,当事件发生时,则会再次判断自己是不是最小的节点,若不是,则再次监听比自己小的节点,若是,则获取到锁,执行任务,任务结束断开连接,此时节点自动删除,下一个节点获取到锁。 在Curator中有五种锁方案:
InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
InterProcessMutex:分布式可重入排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器
InterProcessSemaphoreV2:共享信号量

元数据/配置信息管理:可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用,zookeeper 来做一些元数据、配置信息的管理,dubbo使用zk做注册中心

HA 高可用性:很多系统都基于zk来开发高可用机制,对于一些集群,当某一个宕机后,通过zk可以马上感知到切换到备用节点。

zookeeper节点类型

临时节点、持久化节点、临时顺序节点、持久顺序节点

zookeeper集群搭建
  1. 在每个zookeeper的 data 目录下创建一个 myid 文件,内容分别是1、2、3 。这个文件就是记录每个服务器的ID

  2. 在每一个zookeeper 的 zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。

server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口
  1. Mode为follower表示是跟随者(从)
    再查询第二个服务Mod 为leader表示是领导者(主)
zookeeper会不会存在脑裂问题,怎么解决

会。

采用选举过半机制。只有当超过一半的投票才能成为主节点,当没有主节点时整个系统不可用。

Zookeeper选举

Followers之间会通过指定消息端口发送消息,该消息包含两个值:ServerId、Zxid;

其中ServerId是服务器的id,id越大则权重越大,Zxid是存放的数据的最大id,id越大数据越新,权重也越大;

当某个Follower获取了超过半数的投票后,此Follower就称为Leader。

当已经有了Leader后,再次有新服务器加入集群,不会影响现任领导者。

Observer不参与选举,不参与计数。

怎么操作zookeeper

Java中使用的是Curator来操作ZK的;

zookeeper常⽤的命令

创建普通(持久)节点:create path value

创建临时节点:create -e path value

创建顺序(持久)节点:create -s path value

获取节点值:get path

查看节点详细信息:ls -s path

删除单个节点:delete path

删除多级节点:deleteall path

zookeeper选主原理和机制

选主只存在于Follower之间,当不存在Leader时就会进行选举。默认采用的是 选举过半机制—解决脑裂问题。

Follower之间通过选举端口互相发送消息:ServerId、Zxid;

​ ServerId:ZK的id,值越大,权重越大; Zxid:存放的数据的id,值越大,数据越新,权重越大。

每个Follower先把票投给自己,然后发消息给别的Follower,发现比自己强的Follower,就会把票投给它;

当某一个Follower的票数超过服务器总数(不包含Observer)的一半时,就成为Leader;之后再有服务器也不会影响Leader。

zookeeper集群中的⾓⾊类型

Leader领导者:

  1. 处理事务请求:增删改

  2. 集群内布各服务器的调度者

  3. 与Follower之间保持心跳

  4. 崩溃时负责恢复数据及同步数据到Follower

所有的Follower和Observer收到的写请求都会转发给Leader;Leader收到请求会分发给所有的Follower,统计Follower的写成功数量,当超过半数的Follower写成功了,Leader会认为写请求提交成功,通知所有Follower执行Commit操作;即使集群崩溃,或重启,这个写操作也不会丢失。

Follower跟随者:

  1. 处理非事务请求、转发事务请求给Leader
  2. 参与Leader选举

Observer观察者:

  1. 处理非事务请求,转发事务请求给Leader

Observer出现的原因:

​ 当ZK规模变大时,集群中的Follower逐渐增多;此时ZK处理非事务请求具有很高的效率;但是处理事务请求就会很慢;因为每一次事务请求都需要所有的Follower进行投票,只有超过半数的Follower投票一致才算操作成功,才会写入成功;

​ 为了解决这个问题,提出了observer观察者;观察者只处理非事务请求,不参与选举投票;这样既保证了ZK的集群的扩展性,又避免了服务器过多影响事务请求的处理能力。

Zookeeper集群如何保证数据一致性?zk是 leader -follower机制,所有的写操作是leader广播通知到所有follower,有一半确认即可。那么广播肯定是不可靠的,万一有的follower没有操作本地数据,所有打到这台follower的请求读到的不是脏数据了吗?

1、zk保证的是顺序一致性,短时间是会有脏读的产生。leader会为每一个follower创建一个广播队列,保证消息的顺序性。folower端:在下一个消息到来时,必已经顺序操作之前的消息了。

2、如果一个客户端将Znode z的值更新为a,在之后的操作中,它又将z的值更新为b,则没有客户端能够在看到z的值是b之后再看到值a;因为客户端也会保存一个它见过的最大的 zxid,如果读取的时候,如果客户端发现 本地 zxid 比 server 端的最大 zxid 大,则拒绝,client 会自动重连到其他server。所以client可能会读到脏数据,但不会读到实时数据后,还会再读到脏数据。

3、如果是广播同步数据的过程中,集群崩溃了。集群会进入投票状态,会通过投票机制选出一个commit最高(数据最新)的zk节点,然后其他follower 都会同步数据到最高commit(数据最新)。

什么是CAP

CAP是衡量分布式架构的三个指标

一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)强一致性和最终一致性。

高可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。

CAP 原则的精髓就是要么 AP,要么 CP,要么 AC,但是不存在 CAP。如果在某个分布式系统中数据无副本, 那么系统必然满足强一致性条件, 因为只有独一数据,不会出现数据不一致的情况,此时 C 和 P 两要素具备,但是如果系统发生了网络分区状况或者宕机,必然导致某些数据不可以访问,此时可用性条件就不能被满足,即在此情况下获得了 CP 系统,但是 CAP 不可同时满足。

Base理论

BASE 理论指的是基本可用 Basically Available,软状态 Soft State,最终一致性 Eventual Consistency,核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性。

BASE,Basically Available Soft State Eventual Consistency 的简写:
BA:Basically Available 基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
S:Soft State 软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
E:Consistency 最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
BASE 理论本质上是对 CAP 理论的延伸,是对 CAP 中 AP 方案的一个补充。

Redis的常用命令有哪些?

切换数据库(0-15):SELECT 1

获取所有键:KEYS *

获取键总数:DBSIZE

查询键是否存在:EXISTS KEY [KEY]

删除键:DEL KEY [KEY]

查询键类型:TYPE KEY

查询键生命周期:TTL KEY

设置过期时间:EXPIRE KEY SECONDSPEXPIRE KEY MILLIONSECONDS

设置永不过期:PERSIST KEY

存放键值:SET KEY VALUE

获取键值:GET KEY

值自增/自减:INCR KEYINCRBY KEY VALUEDECR KEYDECRBY KEY VALUE

Redis是什么?都有哪些使用场景?

Redis是一个单线程的NoSql数据库。

1、缓存
2、分布式计数器
3、限流(例如;验证码倒计时)
4、分布式锁;锁在底层就是一个标识符。
5、队列:redis里的list是一个队列。List在java里是数组
6、用set可以做关注好友、点赞之类的。

Redis是单线程还是多线程的?为什么?

单线程的。

因为Redis处理事务的速度非常快,其性能瓶颈不在于CPU,而在于IO的性能,因为IO的限制,Redis可能会更快;如果采用多线程反而会因为线程的创建、销毁和切换线程降低处理的速度,因此采用单线程。如果单个Redis的性能达到瓶颈,建议采用单线程多进程,多核的CPU建议开启多个Redis,搭建Redis集群。

Redis持久化有几种方式?如何配置?

RDB:将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据。使用save会阻塞Redis,进行持久化,因此不建议;应使用bgsave,新建线程进行数据快照;
用法:设置自动持久化的条件,满足限定时间范围内key的变化数量达到指定数量即进行持久化
Java面试题--Framework_第1张图片
优点:

  1. RDB内部是一个紧凑的二进制文件,存储效率高
  2. RDB内部存储的是redis在某个时间节点的数据快照,非常适合数据备份,全量复制等场景;
  3. RDB恢复数据的速度比AOF快

缺点:

  1. RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
  2. bgsave指令每次运行要执行fork操作创建子线程,要牺牲掉一些性能
  3. Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象

应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复。
RDB:当数据量巨大时,效率非常低、大数据量下的IO性能较低、基于fork创建子进程,内存产生额外消耗、某一时刻宕机带来的数据丢失风险
解决思路:不写全数据,仅记录部分数据、降低区分数据是否改变的难度,改记录数据为记录操作过程、对所有操作均进行记录,排除丢失数据的风险

AOF:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令 达到恢复数据的目的。与RDB相比可以简单理解为由记录数据改为记录数据产生的变化;
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
AOF执行的策略:

  1. always(每次):每次写入操作均同步到AOF文件中数据零误差,性能较低,不建议使用。
  2. everysec(每秒):每秒将缓冲区中的指令同步到AOF文件中,在系统突然宕机的情况下丢失1秒内的数据 数据准确性较高,性能较高,建议使用,也是默认配置
  3. no(系统控制):由操作系统控制每次同步到AOF文件的周期,整体过程不可控

AOF重写:随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重 写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。
简单说就是将对同一个数据的若干个条命令执行结 果转化成最终结果数据对应的指令进行记录。

AOF重写作用:

  • 降低磁盘占用量,提高磁盘利用率
  • 提高持久化效率,降低持久化写时间,提高IO性能
  • 降低数据恢复用时,提高数据恢复效率

AOF重写规则:

  • 进程内具有时效性的数据,并且数据已超时将不再写入文件
  • 非写入类的无效指令将被忽略,只保留最终数据的写入命令
    如del key1、 hdel key2、srem key3、set key4 111、set key4 222等
  • 如select指令虽然不更改数据,但是更改了数据的存储位置,此类命令同样需要记录
  • 对同一数据的多条写命令合并为一条命令

AOF重写方式:手动重写、自动重写

如何选择:

  • 对数据非常敏感,建议使用默认的AOF持久化方案
    AOF持久化策略使用everysecond,每秒钟fsync一次。该策略redis仍可以保持很好的处理性能,当出 现问题时,最多丢失0-1秒内的数据。
    注意:由于AOF文件存储体积较大,且恢复速度较慢
  • 数据呈现阶段有效性,建议使用RDB持久化方案
    数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且恢复速度较快,阶段 点数据恢复通常采用RDB方案
  • 灾难恢复选用RDB
  • 双保险策略,同时开启 RDB和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据的量
Redis的过期策略?(过期数据的删除策略)

定期删除+惰性删除

惰性删除:数据到达过期时间时,不做任何处理;当访问数据时,没过期则返回数据;已过期则删除该数据

​ 优点:节省CPU性能 缺点:内存压力大,出现过期数据长期占据内存的情况

定期删除:当Redis启动时,读取配置 server.hz 的值,默认值10;

​ 即每秒钟执行10次activeExpireCycle()方法,该方法会对Redis中的expires数组进行扫描,每次耗时250ms/server.hz相当于每秒钟占用CPU1/4的时间,expires数组存放的是每个数据库的具有时效的key,因此只需要扫描这个数组即可;在对expires数组检测时,从中随机挑选出w个key:

​ 若key已经过期,则删除;

​ 删除后,发现被删除的key的数量>W*25%,则再次对该数组进行扫描;

​ 删除后,发现被删除的key的数量<=W*25%,则继续扫描下一个索引位置的值。默认从0-15

​ 优点:CPU性能占用设有峰值,检测频度可自定义,内存压力小,长期占用内存的冷数据会被持续清理;

最终方案:惰性删除+定期删除

Redis的内存淘汰机制?

Redis在执行命令之前会调用freeMemoryIfNeeded()检测内存是否充足,因此当新数据进入redis,且内存不足以加入数据时,Redis需要临时删除一些数据,为新数据清理空间。该策略被称为逐出算法。

若不能清理出足够的可用空间,则反复执行,最终当对所有的数据尝试完毕将出现错误信息:

image

对数据进行删除的选择策略:maxmemory-policy volatile-lru可分为三类:

第一类:检测易失数据(expires) 针对具有时效性的数据

​ volatile-lru:最近最久未使用的数据
​ volatile-lfu:最近最少使用的数据
​ volatile-ttl:挑选将要过期的数据
​ volatile-random:任意选择数据

第二类:检测全库数据(所有数据集server.db[i].dict)

​ allkeys-lru:最近最久未使用的数据
​ allkeys-lfu:最近最少使用的数据
​ allkeys-random:任意选择数据

第三欸:放弃数据放逐

​ no-enviction:禁止驱逐数据(Redis4.0中的默认策略),会引发OOM

Redis缓存预热

  1. 日常进行数据访问记录,统计访问频度高的热点数据
  2. 对统计结果进行分类,根据级别,redis优先加载级别高的热点数据
  3. 利用分布式多服务器进行数据加载,提速数据加载的过程
  4. 热点数据主从同时预热
  5. 使用脚本程序固定触发数据预热过程
Redis击穿

现象:Redis一切正常、数据库崩溃

问题:Redis中的某个Key非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效
的瞬间,大量的请求就击穿了缓存,直接请求数据库

解决:

  1. 监控key的访问量,对于持续高热的key设置为不过期;
  2. 启动定时任务,在高峰期之前,刷新数据有效期;
  3. 使用互斥锁,等待第一个请求建立缓存后释放锁,其它请求才可以直接访问缓存。
Redis中什么是缓存穿透?如何解决?

现象:Redis内存稳定CPU高压、数据库崩溃;

问题:Redis中出现大量未命中,或出现非正常的URL访问黑客攻击。访问不存在的Key,数据库也没有

解决:

​ 1️⃣缓存NULL值:对查询结果为NULL的数据进行缓存,设置有效期;

​ 2️⃣设置白名单,使用布隆过滤器;

​ 3️⃣KEY加密:临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验。

Redis中由穿透如何引发雪崩?如何解决?

在一个较短时间内key集中过期,在此期间内请求访问过期数据,Redis未命中,进入数据库查询;

数据库请求处理不及时,Redis出现请求积压;数据库流量增多,崩溃;Redis持续高压结果崩溃;

应用服务器响应不及时,客户端不断发出新的请求,应用服务器崩溃。导致全盘崩溃。

由缓存未命中,引发缓存穿透,数据库处理不及时崩溃,引发一系列问题。

解决:

  1. 限流、降级:限制一部分请求访问,降低应用服务器的压力,等待业务运转正常后逐步开放访问;
  2. 监控Redis性能指标、随时快速调整;
  3. 优化数据库;
  4. 根据业务对数据的有效期进行分类错峰,稀释集中到期的key的数量;
  5. 超热数据采用永久key;
  6. 定期维护,对持久性热点进行延时,无用数据进行清理
Redis支持的数据类型有哪些?其中一个String类型最多存储多大数据?

String(字符串)、List(双向链表)、Set(集合)、Hash表、Zset(排序Set)、hyperloglog(基数估算)

String:最大512MB;

如何保证Redis缓存数据和数据库数据保持一致?双写一致性问题?
  1. 读请求和写请求串行化,这样缓存和数据库中的数据一定是一致的;

  2. Cache Aside Pattern:

​ 未命中:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中;

​ 命中:应用程序从cache中取数据,取到后返回;

​ 更新:先把数据存到数据库中,成功后,再让删除缓存;

  1. 吊打面试官:根据数据唯一标识,使用jvm内部队列。解决Cache Aside Pattern的脏数据问题。

为什么不是更新缓存?

  1. 更新缓存成本高:序列化、反序列化等步骤;
  2. 更新缓存在高并发场景下存在脏数据问题;

为什么不是先操作缓存?

  1. 脏数据问题

先操作数据库问题:

  1. 仍然存在脏数据问题,但概率更小;
Redis如何实现分布式锁?

Redis通过setnx创建指定Key,设置过期时间;当创建成功时,即为获取到锁;当创建失败,则获取锁失败;

存在的问题:

  1. 阻塞问题:若采用永久key,出现一个业务阻塞,锁无法释放,导致所有任务阻塞,变成死锁
  2. 超时失效:若采用时效key,若业务耗时较长,则出现锁重入问题
  3. 主从架构锁失效:某业务获取到了锁,当主节点未同步数据到slave时宕机了,此时锁就会失效。

采用Redisson框架,该框架提供了一个看门狗机制,用来解决这些问题。

redis集群的搭建⽅式?(如哨兵集群模式如何搭建)

哨兵集群模式:Redis Sentinel集群 和 Redis集群

Redis集群采用多主多从结构,数据同步采用主从复制、读写分离的策略;主从复制即将master中的数据即时、有效的复制到slave中;一个master可以拥有多个slave,一个slave只能拥有一个master;master和slave各司其职;master主要写数据,并将出现变化的数据自动同步到slave;slave只负责读数据。

主从复制的作用:

  • 读写分离:master写、slave读,提高服务器的读写负载能力
  • 负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
  • 故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
  • 数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式
  • 高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案

Redis Sentinel集群不存储数据,采用奇数个,因为存在选举过半机制,因此采用奇数个,减少资源浪费;

Redis主从复制

分为三个阶段:建立连接、数据同步阶段、命令传播阶段。

  1. 建立连接:建立master和slave之间的连接,

  2. 数据同步阶段:

Master接收到Slave的同步数据命令psync2后,执行bgsave命令生成RDB文件,并创建命令缓冲区;Master将RDB文件发送给Slave,Slave接收到文件后,清空数据,执行RDB文件恢复数据;恢复完成后通知Master,Master再将命令缓冲区中的信息发送给Slave,Slave执行bgrewriteof,恢复数据。

Java面试题--Framework_第2张图片

  1. 命令传播阶段:当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播

    master将接收到的数据变更命令发送给slave,slave接收命令后执行命令

    如果在命令传播阶段出现了断网现象:

    ​ 短时间中断:部分复制

    ​ 长时间中断:全量复制

  2. 心跳机制:进入命令传播阶段后,master和salve之间会通过心跳机制保持在线

reids集群备份数据的时候会不会影响对外提供服务,为什么?

会。

master数据量巨大,数据同步阶段应避开流量高峰期,避免造成master阻塞,影响业务正常执行

为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,建议关闭此期间的对外服务

集群怎么监控?

搭建Redis Sentinel集群,Sentinel集群不存放数据,只是用来监控Master和Slave节点;

监控:

  1. 获取各个sentinel的状态(是否在线)
  2. 不断的检查master和slave是否正常运行
  3. 获取master的状态,获取所有slave的状态(根据master中的slave信息)

通知:当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知

故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接新的master,并告知客户端新的服务器地址

怎么把新的redis加⼊到集群中?

如果redis集群采用Cluster Node,则需要先将Redis添加到集群中,此时cluster nodes查询该节点中是没有插槽的,没有插槽,意味着Key是无法存储到这个节点的,因此需要为其分配插槽。分槽需要从其他的节点中取出来为其分配。

redis集群中插槽有多少个?怎么计算?(哈希槽计算出节点分配)

插槽的总数是固定的:16384个(0-16383),由Cluster负责维护

将这些插槽分配给所有的Master节点,每一份代表一部分存储空间;

通过Hash算法计算key的插槽值,然后根据插槽值所在的节点,存入对应的节点插槽中;

计算 插槽值:计算时,将key的有效部分使用CRC16算法计算出哈希值,再将哈希值对16384取余,得到插槽值。

​ key分为全部有效,或部分有效;当key中不存在"{}"时即为全部有效,若存在,则有效部分为括号中的部分,不能为空;

为什么使用消息系统?
  1. 异步:发出消息和消息处理并不是同时进行的,而是存在消息队列中,等待消费方消费。
  2. 解耦:允许你独立的扩展或修改两边的处理过程,只要确保最受同样的接口约束。
  3. 灵活性 & 削峰填谷:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  4. 冗余:消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失的风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确指出该消息已经被消费完毕,从而确保你的数据被安全的保存直到你使用完毕。
  5. 扩展性:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
Kafka中的Consumer是推还是拉?

Provider将消息推送到broker中,Consumer主动从broker中拉取。

如果采用broker推送(push)给Consumer,这样由broker决定消息的推送速率,若是Consumer的消费能力不同,就会出现,有些Consumer总是处于空闲状态,有些Consumer处于高压状态,甚至宕机;因此Kafka选择了传统的由Consumer主动拉取pull模式。

Pull模式另一个好处是。Consumer可以自主决定一次消费的消息的数量。Push模式下,broker是不知道Consumer的消费能力的;因此,采用Pull模式,让Consumer自主决定消费策略。

但是,One drawback:如果broker中没有消息,将导致Consumer不断轮询,直到有消息到达。为了避免这个问题,Kafka有个参数可以让Consumer阻塞,直到有消息到达,或消息数量达到某个值。

fetch.min.bytes:指定消费者从broker获取消息的最小字节数,即等到有足够的数据时才把它返回给消费者

Kafka分布式,如何保证消息的顺序消费?

kafka分布式中,存储消息的单位是partition,同一个topic具有至少一个partition,在partition中使用write ahead log组织,可以保证一个partition内的消息是先进先出的顺序;不同的partition之间无法保证消息顺序。因此只要将消息发送到一个partition中就可以保证顺序性;

Producer发送一条消息可以指定topic、partition、key三个参数,指定同一个partition就可以了;或者指定key,具有相同key的所有消息会发往同一个partition。

Consumer端可以使用单线程消费,但吞吐量太低;或者使用队列,将具有相同key的消息存到一个Java内置队列中,每个线程消费一个队列即可。

Kafka如何保证消息不丢失?

Kafka提供了副本机制;

Kafka是由多个broker组成,每个broker是一个节点;创建一个topic,这个topic可被划分为多个partition,这些partition均匀的分散在不同的broker上,每个partition就放一部分数据。

为了避免某个broker宕机导致消息丢失,Kafka提供了HA(High Available)机制,每一个partition的数据都会同步到其他机器上,形成自己的多个副本。每一种partition都会有一个Leader,其他的都是Follower,生产者和消费者都跟Leader打交道,这样就可以保证高容错性。

写数据:生产者写leader,leader先写入本地磁盘,其它follower开始从leaderpull拉取,拉取完成之后,告诉leader一个ACK,leader再返回给生产者。

读数据:消费者从leader读,是当所有follower同步完成数据之后,才开始消费。(木桶原理,数据一致性)

如果是消费端丢失了消息呢?

结合项目,我们在项目中用户提交了文章后,会向admin端发送一个消息包含文章的id,若是admin在消费时丢失了消息,此时不进行任何操作,而是通过分布式定时任务来弥补消息丢失的问题。

或者使用在Zookeeper中创建了一个节点去监听指定节点,若消费方消费成功后,会修改指定节点的值,此时消息发送方会取出值判断消息是否消费成功。消费失败,或超时,则重新发送(设置两次,防止一直发送)。--------引出消息的幂等性问题

Kafaka如何保证消息不重复消费?保证消息的幂等性?
  1. 在生产者发送消息时,在消息中添加一个全局唯一的id,当消费者拿到消息之后,先根据这个消息去redis中查询一下,如果消费过了,就丢弃该任务;若没有消费过,就去处理。
  2. 对于写数据库的数据,先根据主键查询,有了就update就行了
  3. 采用数据库的唯一约束来解决
Kafka集群如何保证消息的一致性?

Java面试题--Framework_第3张图片

为了防止出现Consumer读取到不一致的数据,Kafka采用了木桶原理;Consumer只能消费所有的分区中都存在的消息,对于Leader分区中的没有被Follower同步的消息,Consumer是无法消费的,为了防止Leader宕机,Consumer访问其它Follower出现数据不一致情况;kafka为此设置了一个High water mark,该参数存放的是所有分区中偏移量最小的值,Consumer只能访问该参数以内的消息。

消息队列的缺点?
  1. 系统可用性降低:一旦消息服务宕机,则无法进行任务处理,整个系统就陷入瘫痪的局面。从而为了保证高可用而搭建集群。
  2. 系统复杂度提高:需要考虑消息是否消费成功、消息顺序消费、消息不被重复消费、消息丢失等问题。
RabbitMQ、RocketMQ、Kafka的比较?

ActiveMQ:java语言实现,万级数据吞吐量,处理速度ms级,主从架构,成熟度高
RabbitMQ :erlang语言实现,万级数据吞吐量,处理速度us级,主从架构,
RocketMQ :java语言实现,十万级数据吞吐量,处理速度ms级,分布式架构,功能强大,扩展性强
kafka :scala语言实现,十万级数据吞吐量,处理速度ms级,分布式架构,功能较少,应用于大数据较多

ElasticSearch怎么搞?

RestHighLevelClient client; 该对象用来操作文档

client.bulk() 批量操作

查询所有文档: QueryBuilders.matchAllQuery()

词条查询(不分词):QueryBuilders.termQuery("title", "手机");

词条分词查询(取并集): QueryBuilders.matchQuery("title", "华为手机");

模糊查询: QueryBuilders.wildcardQuery("title", "华*");
正则查询: QueryBuilders.regexpQuery("title", "\\w+(.)*");
前缀查询: QueryBuilders.prefixQuery("brandName", "三");

范围查询: QueryBuilders.rangeQuery("price"); 设置范围: rangeQuery.gte(2000); 介于两者之间 rangeQuery.lte(4000);

多重查询:QueryBuilders.queryStringQuery("华为手机").field("title") .field("categoryName") .field("brandName") .defaultOperator(Operator.AND);

布尔查询: QueryBuilders.boolQuery(); 可以使用 must 、filter 来嵌套其它的查询方式

聚合查询

高亮查询

IndicesClient indices = client.indices(); 用来操作索引

索引重构:

保证任务完成后;删除旧索引;新建索引,起别名为原索引名,替换旧索引,保证对外服务没有感知,做到无缝切换。

SpringCloud怎么搞?

服务治理、远程调用、网关、配置管理、服务降级、熔断、限流、隔离等。

eureka注册中⼼实现原理

ribbon继承rest实现负载均衡原理

zuul实现原理

⾃⼰实现配置中⼼思路

hystrix熔断机制原理

什么是springcloud,为什么要⽤springcloud

什么是SOA

微服务架构体系

微服务的优点

易于维护

服务可以独立扩展

容错性高

可以采用不同的技术实现

appllo和springcloudconfig有什么区别

微服务监控⽤的是什么技术

版本出现冲突怎么办

回退到之前版本

版本合并

gitlab和git区别

git和svn区别

创建分⽀

常⽤的命令

服务器与服务器之间怎么传输⽂件

服务器时间怎么同步

2T的txt⽂件怎么去除空格

怎么安装⽂件

tar -zxvf zxvf分别是什么意思

查看占⽤某端的进程

查看某进程监听的端

查看系统负载

查看进程中线程的状态

你们是如何管理日志文件的?

在Java开发中日志的管理有很多种。我一般会使用过滤器,或者是spring的拦截器进行日志的处理。如果是用过滤器比较简单,只要对所有的.do提交进行拦截,然后获取action的提交路径就可以获取对每个方法的调用。然后进行日志记录。使用过滤器的好处是可以自己选择性的对某一些方法进行过滤,记录日志。但是实现起来有点麻烦。

另外一种就是使用Spring的AOP了。这种方式实现起来非常简单,只要配置一下配置文件就可以了。可是这种方式会拦截下所有的对action的每个操作。使得效率比较低。不过想做详细日志这个方法还是非常好的

分布式架构的缺点?
  1. 架构设计变得复杂(尤其是其中的分布式事务)
  2. 部署单个服务会比较快,但是如果一次部署需要多个服务,部署会变得复杂
  3. 系统的吞吐量会变大,但是响应时间会变长
  4. .运维复杂度会因为服务变多而变得很复杂
  5. 架构复杂导致学习曲线变大
  6. 测试和查错的复杂度增大
  7. 技术可以很多样,这会带来维护和运维的复杂度
  8. 管理分布式系统中的服务和调度变得困难和复杂

你可能感兴趣的:(java,spring,spring,boot)