# Java-interview **Repository Path**: javacoderup/java-interview ## Basic Information - **Project Name**: Java-interview - **Description**: Java面试真题总结 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 1 - **Created**: 2020-11-16 - **Last Updated**: 2025-06-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 1. ArrayList和LinkedList区别? > ArrayList: 底层实现是使用的动态数组,实现了RandomAccess随机访问接口,支持随机访问,ArrayList扩容是1.5倍,初始化容量是10。 > > LinkedList:底层数据结构是双向链表,不支持随机访问,查询效率不高,但是新增删除修改效率要比ArrayList效率高。 ### 2. HashMap底层原理? > 1. 概述: > > > HashMap实现了Map接口,key和value支持为Null,但是key不能重复,HashMap也是无顺序,且是一个线程不安全的集合。 > > > > Note:HashMap扩容是一个很耗时的任务,所以能固定Map的容量尽量控制一个初始化值,避免多次扩容,多线程环境使用juc包下面的ConcurrentHashMap代替,这里提一下HashTable,因为hashTable使用了同步关键字synchronized导致性能不好,所以被遗弃了个集合类 - (Hashtable则不允许null作为key)。 > > 2. jdk各版本之间HashMap差异: > > > jdk7版本的HashMap底层使用的数组+链表=散列的数据机构存储元素,jdk8版本的HashMap有变化,在数据长度存储长度大于等于8的时候散列转红黑树,提高查询性能。 > > 2. put方法原理 > > > + 判断HashMap是否为空,为空则进行第一次扩容,然后进判断table[i]==null,计算出hashcode值,根据hashcode值直接插入进去。 > > + 第二次进入,判断table[I]不等于null,进行key比较,如果想等直接覆盖,在进行判断数组长度是否大于等于8,不大于的情况直接继续存储在散列中。 > > + 第三次进入,基于上面流程,走到判断数据容量是否大于等于8,条件成立直接将散列转成红黑树存储。 > > 2. HashMap容量极限 = 负载因子数 * 数组桶容量 > 3. HashMap的初始化容量是16,hashtabel是11 ,两者的填充因子都是0.75。 > 4. HashMap的扩容机制是2倍方式扩容,hashtabel是2倍+1 > 5. HashMap解决Hash冲突使用 - 链地址法 ### 3. 多线程创建方式有几种? > 创建线程方式: > > + 使用 new Thread方式创建 > + 实现Runnable接口方式(没有返回) > + 实现Callable接口方式创建(有回调) > + 可以使用线程池创建 ### 4. 线程池创建方式有几种 > 创建线程池的方式: > > + newFixedThreadPool: 创建一个指定工作线程数量的线程池,每提交一个任务就会创建一个工作线程,达到工作线程的最大数量,对讲任务存如到池队列中。 > + newSingleThreadExecutor:创建一个单线程化的Executor,即只创建唯一的工作线程赖执行任务,保证所有任务执行。 > + newScheduledThreadPool:创建一个定长的线程池,支持定时间执行周期任务。 > + newCachedThreadPool: 创建一个可以缓存的线程池,如果线程池长度超过处理需求,可以灵活回收空闲线程,若没有可以回收的线程,则创建一个线程。 ### 5. Java中锁,乐观锁和悲观锁的应用场景 ### 6. 数据库的引擎有几种? > MySql有两种分别是MyISAM和InnoDB > > **Innodb引擎**:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。 > > **MyIASM引擎**(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。 > > **MEMORY引擎**:所有的数据都在内存中,数据的处理速度快,但是安全性不高。 ### 7. 数据库索引的底层结构? > 数据库底层结构上是B树和B+树,通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。 > > 索引的优点: > > + 可以大大加快数据检索的速度,这也是创建索引的主要原因。 > + 通过使用索引,可以在查询过程中,使用优化隐藏器,提高系统性能。 > > 索引缺点: > > + 时间方面:创建索引和维护索引需要耗费时间。 > + 操作方面::使用索引会导致DML执行效率变低。 > + 空间方面:索引需要占物理空间。 ### 8. SQL优化了解多少? > + 在where条件的列建索引,提高查询效率 > + 在order by 排序的列进行建索引,提高查询效率 > + 在查询返回全列的时候避免使用select * 。会导致全表扫码。影响查询效率 > + 在where条件后面避免不要出现数学运算和函数调用,会导致索引不能正常使用,影响查询效率 > + 避免列的纸使用Null,使用0代替。 > + 在多表联合查询的时候使用,需要指定列名。 > + 查询不需要的数据使用limit解决。 > + 如果查询重复的数据,建议在服务端做缓存。 > > **创建索引的原则(重中之重)** > 索引虽好,但也不是无限制的使用,最好符合一下几个原则 > > 1) 最左前缀匹配原则,组合索引非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 > > 2)较频繁作为查询条件的字段才去创建索引 > > 3)更新频繁字段不适合创建索引 > > 4)若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低) > > 5)尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。 > > 6)定义有外键的数据列一定要建立索引。 > > 7)对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。 > > 8)对于定义为text、image和bit的数据类型的列不要建立索引。 > > ### 9. 数据结构B树和B+树的区别? > B树的存储接口会在叶子节点存储key和value, 新的B+树种没有在最下层叶子节点存储数据,而是存储的索引信息,上层的所有非叶子节点只存放索引信息,这样的结构可以让单个节点存放下更多索引值,增大度Degree的值,提高命中目标记录的几率。 ### 10. 数据库中间件常用有哪些? > MySQL Route 官方推荐的 ### 11. 分库分表依据有哪些? > 分库根据业务模块来分,垂直切割和水平切割。 ### 12. 数据库事务隔离级别有哪些? > 数据库事务特性:原子性,一致性,持久性,隔离性 > > 数据库事务隔离级别: > > + Read uncommitted: 读未提交 > > > 老板开启了一个事务,提交数据但是没有提交事务,小张开启一个事务去查询,查询出来是老板已经提交的数据,,等老板发现错了,回滚事务,但是小张读取的数据错误的,这是一个脏读的现象,解决脏读可以使用 -- 读已提交。 > > + Read committed:读已提交 > > > 老板去查数据,第一次查询数据正常,就在他第二次操作之前,小张开启了一个事务把那条数据改了,改成不对了,当然老板在查第二次需要等小张开启的事务提交了才能执行,这个时候老板郁闷发现数据不对了 - 这个时候出现了不可重复读的现象。 > > + Repeatable read:重复度 > > > 衔接上面例子,老板发现数据不对,决定去修改数据,这个时候再去查一次数据,确保数据是之前那样,开启事务去修改,这个时候,小张还想捣乱去修改,发现不行了,只能等老板修改完成,提交事务才能继续操作,这个时候insert操作是可以的,这就导致了幻读现象。 > > + Serializable :串行化 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。 > > MySQL使用:可重复读 > > Oracle使用:读已经提交 ### 13. Spring IOC原理? > IOC意为控制反转,传统中由程序代码操作对象的调用的操作权交给了容器,通过容器来实现对象组件的装配和管理,通俗的说“控制反转”,就是将控制组件对象的权力移交给SpringIOC管理,本质上是从程序代码中移交到外部容器。 > > 作用: > > > 帮助我们管理对象的创建和依赖关系的维护,实现解耦,由容器维护具体的对象。 > > 优点: > > > 降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象 > > 设计的功能: > > > + 依赖注入 > > + 依赖检查 > > + 自动装配 > > + 支持集合 > > + 指定初始化方法和销毁方法 > > + 支持某些回调方法(但是具有入侵性) > > 依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在Spring中创建被调用者的工作由Spring来完成,然后注入调用者,即所谓的依赖注入or控制反转。 注入方式有两种:依赖注入和设置注入; > ### 14. Spring AOP原理? > AOP是面向切面编程基于IOC基础上,对OOP有益补充。 > > AOP利用横切的技术,剖开封装过的对象内部,并将那些影响多个类的公共代码进行封装成为可以复用模块,并命名为Aspect,切面,通俗的说,就是讲那些与业务无关代码,但是又要提供给业务调用的逻辑封装起来,比如日志,权限等等,可以降低业务系统的耦合度和增加代码复用,同时也有利于未来代码拓展和维护。 > > 举个例子:AOP代表是一个横切的技术,可以把对象比作一个空心的圆柱体,其中封装了对象的属性和方法,然后面向切面编程的方法,将这个圆柱体以切面形式剖开,然后选择性的提供业务逻辑,而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。 > > AOP实现技术: > > 第一种采用动态代理方式实现,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行; > > 第二种是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。 > > Spring实现AOP:JDK动态代理和CGLIB代理 > > + JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理; > 其核心的两个类是InvocationHandler和Proxy。 > + CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。 > > AOP使用场景: > > + 日志记录 > + 缓存 > + 权限检查 > + 错误处理 ### 15. Spring Bean执行流程和生命周期? > 1. Spring对Bean进行实例化 > 2. Spring将值和bean的引用注入到Bean对应属性中。 > 3. 如果Bean实现了BeanNameAware接口,Spring将Bean的id传递给setBeanName() > 4. 如果Bean实现了BeanFactoryAware接口,将调用setBeanFactory()方法,将BeanFactory实例传入容器 > 5. 如果Bean实现了ApplicationContextAware接口,将调用setApplicationContext()方法,将Bean所在应用的上下文引用传入进来。 > 6. bean实现了BeanPostProcessor接口,Spring调用postProcessBeforeInitialzation()方法 > 7. 如果Bean实现了InitializingBean接口,,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用initmethod声明了初始化方法,该方法也会被调用。 > 8. 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法。 ### 16. Spring事务隔离级别有几种? > Spring事务隔离级别有5种: > > 1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么; > 2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读); > 3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别; > 4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别; > 5. ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读 > > Note: > > > 脏读:一个事务真正修改,还没有提交事务,但是另一个查询事务已经查询到了第一个事务修改的数据,但是第一个事务没有提交并回滚了,所以第二个事务查询的数据是不对的,导致了脏读。 > > > > 不可重复读: > > > > 第一次查询数据的时候是正确的值,更新事务去update修改数据,并提交事务,第二次查询却发现数据不对了。(前提就是,第二次查询必须等更新事务提交完成之后才能操作,),导致两次查询的结果不一样。 > > > > 幻读:当一个事务开启去修改数据的时候,不允许其他事务的进行update操作,可以解决不可重复读,但是可以insert操作,可能导致幻读 ### 17. Spring事务传播机制有几种? Spring传播机制有7种: > 1. PROPAGATION_REQUIRED: 如果当前没有事务就创建一个事务,如果当前事务存在,就加入当前事务。 > 2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。 > 3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。 > 4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。 > 5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 > 6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 > 7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。 ### 18. SpringMVC 执行流程 > 1. 用户发起请求,DispatcherServlet接收到请求。 > 2. DispatcherServlet接收到请求后,调用HanderMapping处理器映射器,请求Hander。 > 3. 处理器映射器根据请求url找到具体的处理器适配器,生成处理器对象及拦截器一并返回给DispatcherServlet。 > 4. DispatcherServlet 调用 HandlerAdapter处理器适配器;。 > 5. HanderAdapter经过适配调用具体处理器(,可以理解为controller) > 6. Hander执行完成返回ModelAndView对象 > 7. HandlerAdapter将Hander执行结果ModelAndView返回给DispatcherServlet > 8. DispatcherServlet再将ModelAndView对象传递给ViewResolver试图解析器,进行解析。 > 9. ViewResolver解析完成返回View对象给DispatcherServlet > 10. DispatcherServlet将view进行渲染视图。 > 11. 最后DispathcherServlet响应给用户。 ### 19. SpringMVC常用的注解有哪些? > + @Controller(该类标记为是一个SpringMVC控制器对象) > > + @RestController (相当于Controller和ResponseBody组合) > > + @RequestMapping(用于处理请求地址的映射到具体的类或者方法) > > + @ResponseBody(标记这个类不反悔html页面,而是返回json和xml对象) > > + @RequestBody (注解实现接收http请求的json数据,将json转换为java对象。) > > + @PathVariable和@RequestParam区别 > > > 请求路径上有个id的变量值,可以通过@PathVariable来获取 @RequestMapping(value = “/page/{id}”, method = RequestMethod.GET) > > > > @RequestParam用来获得静态的URL请求入参 spring注解时action里用到。 ### 20. Mybatis框架的特性? > **优点** > > 与传统的数据库访问技术相比,ORM有以下优点: > > - 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用 > - 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接 > - 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持) > - 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护 > - 能够与Spring很好的集成 > > **缺点** > > - SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求 > - SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库 ### 21. Mybatis和Hibernate区别? > 相同点: > > > 都是对jdbc的封装,都是持久层的框架,都用于dao层的开发。 > > 不同点: > > > MyBatis 是一个半自动映射的框架,配置Java对象与sql语句执行结果的对应关系,多表关联关系配置简单 > > > > Hibernate 是一个全表映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂 ### 22. Mybatis执行流程? > ![](./images/mybatis执行流程.png) > > 1) 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。 > > 2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。 > > 3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。 > > 4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。 > > 5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。 > > 6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。 > > 7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。 > > 8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。 ### 23. Mybatis多表查询的时候使用的注解有哪些? ```java @Many 多 @One 一 ``` > 多表操作配置: > > > 有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的**association,collection**节点配置一对一,一对多的类就可以完成. > > > > 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过配置association,collection,但另外一个表的查询通过select节点配置。 ### 22. Mybatis 一级缓存和二级缓存的区别? > 一级缓存:基于PerpetualCache 的HashMap实现的,使用key/value键值对形式存储,作用在一次session范围内,当session被刷新或者关闭,所有的缓存都会被清除,默认开启了一及缓存。 > > 二级缓存:二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置`` ; ### 25. SpringBoot和Spring区别? > SpringBoot简化了Spring的繁琐配置,提供了各种启动Starter,快速开箱使用。 > > SpringBoot简化了部署应用,内嵌tomcat服务器。 > > 没有代码生成,也不需要XML配置,只需要结合JavaConfig方式配置就行。 > > 避免大量的 Maven 导入和各种版本冲突。 ### 26.SpringBoot的启动流程? > ### 27. SpringBoot的自动装配原理? > 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心, > > @EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。 > > 筛选有效的自动配置类。 > > 每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能 ### 28.SpringBoot有哪些重要的注解? > 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解: > > @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。 > > @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。 > > @ComponentScan:Spring组件扫描。 ### 27. Redis有哪些数据类型,并说明每一种数据类型的应用场景? > 数据类型: > > + String > > > 做简单的键值对缓存 > > + List > > > 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据 > > + Hash > > > 结构化的数据,比如一个对象 > > + Set > > > 交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一个交集 > > + ZSet > > > 去重但可以排序,如获取排名前几名的用户 ### . Redis中RDB文件和AOF文件区别? > RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。 > > 优点: > > - 1、只有一个文件 dump.rdb,方便持久化。 > - 2、容灾性好,一个文件可以保存到安全的磁盘。 > - 3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能 > - 4.相对于数据集大时,比 AOF 的启动效率更高。 > > 缺点: > > - 1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候) > - 2、AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式完全持久化存储)保存为 aof 文件。 > > AOF:持久化 > > AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。 > > 优点: > > - 1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。 > - 2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。 > - 3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall)) > > 缺点: > > - 1、AOF 文件比 RDB 文件大,且恢复速度慢。 > - 2、数据集大的时候,比 rdb 启动效率低。 > > 优缺点是什么? > > - AOF文件比RDB更新频率高,优先使用AOF还原数据。 > - AOF比RDB更安全也更大 > - RDB性能比AOF好 > - 如果两个都配了优先加载AOF ### 31. Redis缓存穿透 > **缓存穿透**是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。 > > 解决方案: > > 1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截; > 2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击 > 3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力 ### 32. Redis缓存预热 > **缓存预热**就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据! > > 解决方案: > > 1. 直接写个缓存刷新页面,上线时手工操作一下; > 2. 数据量不大,可以在项目启动的时候自动进行加载; > 3. 定时刷新缓存; ### 33.Redis缓存击穿 > **缓存击穿**是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。 > > 解决方案: > > 1. 设置热点数据永远不过期。 > 2. 加互斥锁,互斥锁 ### 34.Redis缓存雪崩 > **缓存雪崩**是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。 > > 解决方案: > > > 设置缓存数据过期时间为随机,这样可以避免同一时间大量的key过期 > > > > 并发量不多的情况,建议使用加锁排队。 > > > > 给每个过期的缓存增加标识,如果过期了,添加新的缓存数据。 ### 35.Redis分布式锁 > Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。 > > 使用SETNX命令获取锁,若返回0(key已存在,锁已存在)则获取失败,反之获取成功 > > 为了防止获取锁后程序出现异常,导致其他线程/进程调用SETNX命令总是返回0而进入死锁状态,需要为该key设置一个“合理”的过期时间 > > 释放锁,使用DEL命令将锁数据删除 > > ### RedLock > > > Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 *Redlock*,此种方式比原先的单节点的方法更安全。它可以保证以下特性: > > > > 1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁 > > 2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区 > > 3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务 ### 36. SpringCloud核心组件? > ### 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成为了一等公民。 ### 37. SpringCloud和SpringBoot关系 > SpringBoot专注于快速方便的开发单个个体微服务。 > > SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来, > > 为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务 > > SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系 > > SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。 ### 38. 微服务的优缺点分别是什么?说下你在项目开发中碰到的坑 > **优点** > > - 每一个服务足够内聚,代码容易理解 > - 开发效率提高,一个服务只做一件事 > - 微服务能够被小团队单独开发 > - 微服务是松耦合的,是有功能意义的服务 > - 可以用不同的语言开发,面向接口编程 > - 易于与第三方集成 > - 微服务只是业务逻辑的代码,不会和HTML,CSS或者其他界面组合 > > ​ 开发中,两种开发模式 > ​ 前后端分离 > ​ 全栈工程师 > > - 可以灵活搭配,连接公共库/连接独立库 > > **缺点** > > - 分布式系统的负责性 > - 多服务运维难度,随着服务的增加,运维的压力也在增大 > - 系统部署依赖 > - 服务间通信成本 > - 数据一致性 > - 系统集成测试 > - 性能监控 ### 39. 微服务技术栈有哪些? > - 维度(springcloud) > - 服务开发:springboot spring springmvc > - 服务配置与管理:Netfix公司的Archaiusm ,阿里的Diamond > - 服务注册与发现:Eureka,Zookeeper,Consul,Nacos(新起之秀) > - 服务调用:Rest RPC gRpc > - 服务熔断器:Hystrix > - 服务负载均衡:Ribbon Nginx > - 服务接口调用:Fegin > - 消息队列:Kafka Rabbitmq activemq > - 服务配置中心管理:SpringCloudConfig > - 服务路由(API网关)Zuul > - 事件消息总线:SpringCloud Bus ### 40. Eureka和ZooKeeper都可以提供服务注册与发现的功能,请说说两个的区别 > 1.ZooKeeper保证的是CP,Eureka保证的是AP > > ZooKeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的 > > Eureka各个节点是平等关系,只要有一台Eureka就可以保证服务可用,而查询到的数据并不是最新的 > > 自我保护机制会导致 > > Eureka不再从注册列表移除因长时间没收到心跳而应该过期的服务 > > Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点(高可用) > > 当网络稳定时,当前实例新的注册信息会被同步到其他节点中(最终一致性) > > Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper一样使得整个注册系统瘫痪 > > 2.ZooKeeper有Leader和Follower角色,Eureka各个节点平等 > > 3.ZooKeeper采用过半数存活原则,Eureka采用自我保护机制解决分区问题 > > 4.Eureka本质上是一个工程,而ZooKeeper只是一个进程 ### 41.注册中心eureka与consul区别 > consul 特点: > > + 服务发现 > + 健康检查 > + key/value检查 > + 安全服务通信 > + 多数据中心 > > Zookeeper和Consul 采用了 CP设计,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足 > > Eureka 采用了 AP原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化。 > > 总结: > > 分布式系统中P,肯定要满足,所以只能在CA中二选一。 > 没有最好的选择,最好的选择是根据业务场景来进行架构设计。 > 如果要求一致性,则选择zookeeper、Consul,如金融行业。 > 如果要求可用性,则Eureka,如电商系统。 ### 42. Ribbon和Feign的区别? > 1.Ribbon都是调用其他服务的,但方式不同。 > 2.启动类注解不同,Ribbon是@RibbonClient feign的是@EnableFeignClients > 3.服务指定的位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。 > 4.调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign需要将调用的方法定义成抽象方法即可。 > > Ribbon负载均衡策略: > > 随机策略/轮询策略/重试策略/最低并发策略/可用过滤策略/响应时间加权策略/区域权衡策略 ### 41.什么是SpringCloudConfig? > 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。 > > 使用: > 1、添加pom依赖 > 2、配置文件添加相关配置 > 3、启动类添加注解@EnableConfigServer ### 44. 什么是Hystrix? > 防雪崩利器,具备服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard) > 服务降级: > 双十一 提示 哎哟喂,被挤爆了。 app秒杀 网络开小差了,请稍后再试。 > 优先核心服务,非核心服务不可用或弱可用。通过HystrixCommand注解指定。 > fallbackMethod(回退函数)中具体实现降级逻辑。 ### 45. 请讲一下Zookeeper 是什么? > ZooKeeper是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 > > 分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能 > > 特性: > > - 顺序一致性 > - 原子性 > - 单一视图 > - 可靠性 > - 实时性(最终一致性) ### 46. Zookeeper有哪些角色? > **Leader** > > - 事务请求的唯一调度和处理者,保证集群事务处理的顺序性 > - 集群内部各服务的调度者 > > > > **Follower** > > - 处理客户端的非事务请求,转发事务请求给Leader服务器 > - 参与事务请求Proposal的投票 > - 参与Leader选举投票 > > **Observer** > > 3.3.0版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力 > > - 处理客户端的非事务请求,转发事务请求给Leader服务器 > - 不参与任何形式的投票 ### 47. Zookeeper节点宕机怎么处理? > Zookeeper本身也是集群,推荐配置不少于3个服务器。Zookeeper自身也要保证当一个节点宕机时,其他节点会继续提供服务。 > 如果是一个Follower宕机,还有2台服务器提供访问,因为Zookeeper上的数据是有多个副本的,数据并不会丢失; > 如果是一个Leader宕机,Zookeeper会选举出新的Leader。 > ZK集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。 > 所以 > 3个节点的cluster可以挂掉1个节点(leader可以得到2票>1.5) > 2个节点的cluster就不能挂掉任何1个节点了(leader可以得到1票<=1) ### 48. Zookeeper负载均衡和nginx负载均衡区别 > zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件;但是nginx的吞吐量比zk大很多,应该说按业务选择用哪种方式。 ### 49. Zookeeper 常用命令 > 常用命令:ls get set create delete等。 ### 50. Zookeeper的应用场景 > Zookeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布和订阅。 > > 通过对Zookeeper中丰富的数据节点进行交叉使用,配合Watcher事件通知机制,可以非常方便的构建一系列分布式应用中年都会涉及的核心功能,如: > > - 数据发布/订阅 > - 负载均衡 > - 命名服务 > - 分布式协调/通知 > - 集群管理 > - Master选举 > - 分布式锁 > - 分布式队列 ### 51. Zookeeper 分布式锁 > 一个机器接收到了请求之后,先获取 zookeeper 上的一把分布式锁(zk会创建一个 znode),执行操作;然后另外一个机器也**尝试去创建**那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等待,等第一个机器执行完了方可拿到锁。 > > 使用 ZooKeeper 的顺序节点特性,假如我们在/lock/目录下创建3个节点,ZK集群会按照发起创建的顺序来创建节点,节点分别为/lock/0000000001、/lock/0000000002、/lock/0000000003,最后一位数是依次递增的,节点名由zk来完成。 > > ZK中还有一种名为临时节点的节点,临时节点由某个客户端创建,当客户端与ZK集群断开连接,则该节点自动被删除。EPHEMERAL_SEQUENTIAL为临时顺序节点。 > > 根据ZK中节点是否存在,可以作为分布式锁的锁状态,以此来实现一个分布式锁,下面是分布式锁的基本逻辑: > > 1. 客户端调用create()方法创建名为“/dlm-locks/lockname/lock-”的临时顺序节点。 > > 2. 客户端调用getChildren(“lockname”)方法来获取所有已经创建的子节点。 > > 3. 客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,就是看自己创建的序列号是否排第一,如果是第一,那么就认为这个客户端获得了锁,在它前面没有别的客户端拿到锁。 > > 4. 如果创建的节点不是所有节点中需要最小的,那么则监视比自己创建节点的序列号小的最大的节点,进入等待。直到下次监视的子节点变更的时候,再进行子节点的获取,判断是否获取锁。 > > =================================================== > > **先说Reids:** > > 1. Rdis只保证最终一致性,副本间的数据复制是异步进行(Set是写,Get是读,Reids集群一般是读写分离架构,存在主从同步延迟情况),主从切换之后可能有部分数据没有复制过去可能会**丢失锁**情况,故强一致性要求的业务不推荐使用Reids,推荐使用zk。 > 2. Redis集群各方法的响应时间均为最低。随着并发量和业务数量的提升其响应时间会有明显上升(公有集群影响因素偏大),但是极限qps可以达到最大且基本无异常 > > **再说ZK:** > > 1. 使用ZooKeeper集群,锁原理是使用ZooKeeper的临时节点,临时节点的生命周期在Client与集群的Session结束时结束。因此如果某个Client节点存在网络问题,与ZooKeeper集群断开连接,Session超时同样会导致锁被错误的释放(导致被其他线程错误地持有),因此ZooKeeper也无法保证完全一致。 > 2. ZK具有较好的稳定性;响应时间抖动很小,没有出现异常。但是随着并发量和业务数量的提升其响应时间和qps会明显下降。 ### 52. 什么是RabbitMQ?为什么使用RabbitMQ? > RabbitMQ 是开源的,使用Elrang语言开发的,深入源码的学习成本比较高,需要有Elrang语言功底, > > 它是一款基于AMQP协议的消息中间件,作用:异步,解耦,削峰。 ### 53.RabbitMQ有什么优点? > 优点:解耦、异步、削峰; > > 缺点: 降低了系统的稳定性:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低;增加了系统的复杂性:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。 ### 54. 如何保证RabbitMQ不被重复消费? > 先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除; > > 但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。 > > 针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性; > > 比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过; ### 55. 如何保证RabbitMQ消息的可靠传输? > 消息不可靠的情况可能是消息丢失,劫持等原因; > > 丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息; > > 生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息; > > transaction机制就是说:发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降; > > confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后; > > rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了; > > 如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。 > > 消息队列丢数据:消息持久化。 > > 处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。 > > 这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。 > > 这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。 > > 那么如何持久化呢? > > 这里顺便说一下吧,其实也很容易,就下面两步 > > 1. 将queue的持久化标识durable设置为true,则代表是一个持久的队列 > 2. 发送消息的时候将deliveryMode=2 > > 这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据 > > > > 消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可! > > 消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息; > > 如果这时处理消息失败,就会丢失该消息; > > 解决方案:处理消息成功后,手动回复确认消息。 ### 56. 如何保证RabbitMQ消息的顺序性? > 单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息; ### 57. Dubbo的主要应用场景? > 透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。 > > 软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。 > > 服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。 ### 58. Dubbo的核心功能? > 主要就是如下3个核心功能: > > - **Remoting:网络通信框架**,提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。 > - **Cluster:服务框架**,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。 > - **Registry:服务注册**,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。 ### 59. Dubbo服务注册与发现的流程? ![](../java-interview/images/duboo流程说明.png) **流程说明:** - Provider(提供者)绑定指定端口并启动服务 - 指供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息发送至注册中心存储 - Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心 - 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。 - Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。 - Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer **设计的原因:** - Consumer 与Provider 解偶,双方都可以横向增减节点数。 - 注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台 - 去中心化,双方不直接依懒注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用 - 服务提供者无状态,任意一台宕掉后,不影响使用 ### 60. Dubbo集群提供了哪些负载均衡策略? > - **Random LoadBalance:** 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀; > - **RoundRobin LoadBalance:** 轮循选取提供者策略,平均分布,但是存在请求累积的问题; > - **LeastActive LoadBalance:** 最少活跃调用策略,解决慢提供者接收更少的请求; > - **ConstantHash LoadBalance:** 一致性Hash策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动; > > 缺省时为Random随机调用 ### 61. Dubbo的集群容错方案有哪些? > - **Failover Cluster** > - 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。 > - **Failfast Cluster** > - 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。 > - **Failsafe Cluster** > - 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。 > - **Failback Cluster** > - 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。 > - **Forking Cluster** > - 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2″ 来设置最大并行数。 > - **Broadcast Cluster** > - 广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息。 ### 62. Dubbo和Spring Cloud的区别 > Dubbo是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 > > Spring Cloud诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spirng、Spirng Boot的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是一个生态。 > > 最大的区别:Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。 > > 而SpringCloud是基于Http协议+Rest接口调用远程过程的通信,相对来说,Http请求会有更大的报文,占的带宽也会更多。但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。 ### 63.你了解Netty服务吗? > Netty是一个异步事件驱动的网络应用程序框架,用于开发快速可维护的高性能协议服务器和客户端,Netty底层主要是基于NIO,对jdk中的NIO进行进一步的封装,提高使用的灵活性。 > > ![](../java-interview/images/Netty架构设计图.png) > > Netty特点: > > + 高性能:基于NIO开发(非阻塞IO),的网络通信框架,相比于传统BIO(阻塞IO),它大大提高并发性能。 > + 传输快:Netty的传输依赖于零拷贝,减少不必要的内存拷贝,提高传输性能。 > + 封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。 > > Netty优势: > > - 使用简单:封装了 NIO 的很多细节,使用更简单。 > - 功能强大:预置了多种编解码功能,支持多种主流协议。 > - 定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。 > - 性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。 > - 稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。 > - 社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。 > > 高性能方面: > > - IO 线程模型:同步非阻塞,用最少的资源做更多的事。 > - 内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。 > - 内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。 > - 串形化处理读写:避免使用锁带来的性能开销。 > - 高性能序列化协议:支持 protobuf 等高性能序列化协议 > > Netty应用场景: > > + 典型的应用有:阿里分布式服务框架 Dubbo,默认使用 Netty 作为基础通信组件,还有 RocketMQ 也是使用 Netty 作为通讯的基础。 ### 64. BIO、NIO和AIO的区别? > **BIO:**一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。 > 伪异步IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。 > > **NIO:**一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 > > **AIO:**一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理, > > BIO是面向流的,NIO是面向缓冲区的;BIO的各种流是阻塞的。而NIO是非阻塞的;BIO的Stream是单向的,而NIO的channel是双向的。 > > NIO的特点:事件驱动模型、单线程处理多任务、非阻塞I/O,I/O读写不再阻塞,而是返回0、基于block的传输比基于流的传输更高效、更高级的IO函数zero-copy、IO多路复用大大提高了Java网络应用的可伸缩性和实用性。基于Reactor线程模型。 > > 在Reactor模式中,事件分发器等待某个事件或者可应用或个操作的状态发生,事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。如在Reactor中实现读:注册读就绪事件和相应的事件处理器、事件分发器等待事件、事件到来,激活分发器,分发器调用事件对应的处理器、事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。 ### 65.Netty和WebSocket的区别? > Netty: > > + 作为http服务器 > + 开发rpc服务 > + socket通信的并发 > > WebSocket: > > + 长连接是全双工的 > + websocket是基于http协议的 > > + websocket也可以用于非浏览器业务情景 ### 66. Netty 和 Tomcat 的区别? > - 作用不同:Tomcat 是 Servlet 容器,可以视为 Web 服务器,而 Netty 是异步事件驱动的网络应用程序框架和工具用于简化网络编程,例如TCP和UDP套接字服务器。 > - 协议不同:Tomcat 是基于 http 协议的 Web 服务器,而 Netty 能通过编程自定义各种协议,因为 Netty 本身自己能编码/解码字节流,所有 Netty 可以实现,HTTP 服务器、FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、MySQL 的 Proxy 服务器等等。 ### 67. jwt具体怎么生成token > 一个JWT实际上就是一个字符串,它由三部分组成,**头部**、**载荷**与**签名**。 ### 68. 谈谈项目中单点登录的实现原理? > 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数 > > sso认证中心发现用户未登录,将用户引导至登录页面 > > 用户输入用户名密码提交登录申请 > > sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌 > > sso认证中心带着令牌跳转会最初的请求地址(系统1) > > 系统1拿到令牌,去sso认证中心校验令牌是否有效 > > sso认证中心校验令牌,返回有效,注册系统1 > > 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源 > > 用户访问系统2的受保护资源 > > 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数 > > sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌 > > 系统2拿到令牌,去sso认证中心校验令牌是否有效 > > sso认证中心校验令牌,返回有效,注册系统2 > > 系统2使用该令牌创建与用户的局部会话,返回受保护资源 ### 69. 了解JVM,说说JVM有什么模块组成? > 1. 类加载子系统: > > + 类加载器加载字节码->链接->初始化 > > 2. 运行数据区: > > + Java栈-本地方法栈-程序计数器(线程私有私有) > + 方法区-堆内存(线程共享) > > 3. 执行引擎 > 4. 本地方法栈 ### 70. 说明一下双亲委派机制? > 类加载器分类: > > + 引导类加载器 > + 拓展类加载器 > + 应用程序类加载器 > > 当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中**检查是否加载过**,如果有那就无需再加载了。如果没有,那么会拿到**父加载器**,然后调用父加载器的**loadClass**方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,知道到达**Bootstrap classLoader**之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出**ClassNotFoundException**。 ### 71. Java 中都有哪些引用类型? > - 强引用:发生 gc 的时候不会被回收。 > - 软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。 > - 弱引用:有用但不是必须的对象,在下一次GC时会被回收。 > - 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。 ### 72. 怎么判断对象是否可以被回收? > 垃圾收集器在做垃圾回收的时候,首先需要判定的就是哪些内存是需要被回收的,哪些对象是「存活」的,是不可以被回收的;哪些对象已经「死掉」了,需要被回收。 > > 一般有两种方法来判断: > > - 引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题; > - 可达性分析算法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。 ### 73. 说一下 JVM 有哪些垃圾回收算法? > - 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。 > - 复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。 > - 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。 > - 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法 ### 74. 说一下 JVM 调优的工具? > JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。 > > - jconsole:用于对 JVM 中的内存、线程和类等进行监控; > - jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。 ### 75. 常用的 JVM 调优的参数都有哪些? > - -Xms2g:初始化推大小为 2g; > - -Xmx2g:堆最大内存为 2g; > - -XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4; > - -XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2; > - –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合; > - -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合; > - -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合; > - -XX:+PrintGC:开启打印 gc 信息; > - -XX:+PrintGCDetails:打印 gc 详细信息。