Java面试题zs
2022-01-08 14:55:47 0 举报
AI智能生成
java面试题 全
作者其他创作
大纲/内容
zookeeper工作原理
分布式协调组件
zookeeper(2)
ik分词器怎么配置的
elasticsearch
第一层:service层,接口层,给服务提供者和消费者来实现的第二层:config层,配置层,主要是对dubbo进行各种配置的第三层:proxy层,服务代理层,透明生成客户端的stub和服务端的skeleton第四层:registry层,服务注册层,负责服务的注册与发现第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控第七层:protocol层,远程调用层,封装rpc调用第八层:exchange层,信息交换层,封装请求响应模式,同步转异步第九层:transport层,网络传输层,抽象mina和netty为统一接口第十层:serialize层,数据序列化层
dubbo工作原理
1.provider向注册中心注册
2.consumer从注册中心订阅服务,注册中心会通知consumer注册好的服务
3.consumer调用provider
4.consumer和provider都异步的通知监控中心
工作流程
1.面向接口的远程方法调用
2.智能容错和负载均衡
3.服务自动注册与发现
三大核心能力
dubbo
tail -f -n 最后几行
1.Linux查找日志但是这个日志很大我怎么查看最后几行信息或者最前面几行
1.tail -f可以查询文件实时变化情况
2. top命令用于实时显示 process 的动态
tail命令和top命令
Linux
1.自我介绍
电商项目
2.介绍一下做过的项目、得意的项目
3.你们公司的架构
2前端5后端3测试
4.你们组的架构
5.当你的想法与当前项目不吻合,你怎么办(2)
使用自定义注解(AOP+分布式锁)实现首页分类数据的缓存
布隆过滤器
2.首页商品分类
1.页面静态化
2.异步编排
3.商品详情页
4.单点登录
5.购物车
6.订单
6.你负责的哪些模块(3)
7.你知道dao领域吗?
8.你们项目组的架构,来了一个新的需求,从哪下手
1.clean
2.default
3.site
9.Maven的生命周期
10.算法题,开心数
11.手写一个除单例以外的设计模式
1.分布式执行效率快
2.容灾能力更强
3.容纳的数据量更大
12.你们项目和一个单体boot项目的区别
13.上线流程
14.测试流程
15.代码量
其他问题
针对集合迭代器的增强,能够更高效的完成聚合,过滤,或者大批量的操作,流与lambda表达式结合,编码效率大大提高类型转换非常方便,比如我想把一个List转换成Map,不用流的话我需要写很多代码,流只需要一行代码就能搞定
1.解释下java中的Stream流
1.HashMap是基于哈希表对Map接口的实现,HashMap]与HashTable相比允许使用null值和null键(源码的注释),并且是线程不安全的
源码注释:在时间和空间上做的一个权衡;加载因子大于0.75会减少头部以上的空间,增加遍历花费的时间
2.HashMap初始化是在首次调用put()方法时,默认创建长度为16的数组,负载因子0.75
3.jdk7底层结构只有:数组+链表。jdk8底层:数组+链表+红黑树
如果hash值一样就以链表形式存储
h = key的hashCode值 异或 h>>>16
hash算法
4.插入元素时,先调用hash()方法,获取hash值:元素角标(n-1) & hash(n是集合长度) 根据hash值来决定插入的值存放在哪个位置hash = (key==null) ? 0 : (h = key.hashCode()) ^ (h>>>16)
5.存取无序
6.HashMap继承AbstractMap实现Map;设计上的一个小错误
因为如果是2的n次方 可以极大减少HashMap碰撞的次数,可以使得元素更加均匀的分配到数组中。
初始容量给负数会报错;给超过2的30次方(最大值)不会报错,会分配最大容量。
7.初始容量必须为2的n次方
int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
8.如果初始容量不给2的n次方:会返回一个大于给定值的最小的2的n次方的一个值
根据泊松分布概率统计得出的结论,8的时候概率已经几乎为0了
9.当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组长度 > 64时,反树化值为6
1.手动执行map.put()
2.先初始化一个table[] 也就是HashMap
3.会根据元素的Hash值判断该位置是否有值
4.如果没有,直接newNode,新建一个元素
5.如果有值并且当前已经是链表了,就要遍历链表并存值
6.如果有值并且已经树化,就要用树的方式存值
7.key的Hash相同了,会进行值覆盖
10.put方法
补充:LinkedHashMap: HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。
2.hashMap(5)
1.list有序可重复,set无序不可重复,map是键值对,map中的key相当于一个set
3.set查询慢,增删快,增加和删除不会引起元素位置改变,list和数组类似,可以动态增长,查找效率高。
Map和Set关系补充 :Set的内部实现其实是一个Map。即HashSet的内部实现是一个HashMap,TreeSet的内部实现是一个TreeMap,LinkedHashSet的内部实现是一个LinkedHashMap。
4.set list map的区别
1.通过反射可以知道任意一个类的所有属性和方法
2.通过反射可以调用任意一个对象的所有方法和属性
3.这是一种动态获取信息和动态调用对象的方法的功能
1.类型名.class
2.对象.getClass()
3.Class.forName(类型全类名)
4.ClassLoader的类加载器.loadClass(全类名)
4.获取类对象
5.反射是什么
1.lambda表达式
2.Stream流
3.函数式接口
4.永久代-->元空间
6.jdk 1.8和其他版本的区别
1.Array是动态数组的数据结构,Link是链表的数据结构
2.查找Array效率高(get,set)、增删(add,remove)Link效率高
3.Array需要手动设置固定大小的容量,Link动态随数据量的变化而变化
7.ArrayList和LinkedList主要区别(2)
Vector、Collections.synchronizedList()、CopyOnWriteArrayList
1.ArrayList
CopyOnWriteArraySet、Collections.synchronizedSet()
2.HashSet
HashTable(不能存储null)、ConcurrentHashMap、Collections.synchronizedMap()
3.HashMap
8.集合的线程安全问题
1.stringBuilder速度快,线程不安全
2.stringBuffer速度慢,线程安全
9.stringbuffer和stringbuilder区别?为什么是线程安全
1.单例模式
2.工厂模式
3.装饰模式
4.代理模式
10.设计模式
1.深拷贝和浅拷贝就是指对象的拷贝,一个对象存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用
2.浅拷贝:只会拷贝基本数据类型的值,以及实例对象的引用,并不会复制一份引用地址所指向的对象,内部类属性指向的是同一个对象
3.深拷贝:既拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,内部的类执行指向的不是同一个对象
11.深拷贝和浅拷贝的理解
JavaSE
1.开源免费,同时也支持收费版
2.可以跨平台
3.用起来也特别的方便
1.使用MySQL存数据的原因
1.选择过滤性好的字段(手机号,身份证)
2.组合索引的时候,过滤性好的字段越靠前越好
3.组合索引遇到范围查询时,尽量把这个字段放在索引次序的最后面
4.选择组合索引的时候,尽量包含where中更多的索引
1.计算、函数导致索引失效
2.范围条件右边的列索引失效
3.!=或者<>索引失效
4.is not null不能使用索引,is null 可以
5.like以通配符%开头索引失效
6.类型转换索引失效
5.尽量避免索引失效的情况
单表优化
1.左外、右外连接,要保证被驱动表的关联字段被索引
2.选择小表作为驱动表(但是具体哪个表还是需要根据实际业务决定)
3.inner join时,mysql自动选择小表或者连表字段没有索引的表作为驱动表
4.straight_join 可以优化inner join 强制使用一张表作为驱动表(straight_join前面的表)
多表优化
1.SQL优化一般在哪些字段上面加索引
2.MySQL的优化
1.排序必须加limit过滤(limit10)
2.排序后面的字段要和创建索引时顺序一致
3.排序时字段不要一个升序一个降序(年龄升,id降)
4.group by没有过滤条件,也可以用上索引
5.group by 先排序在分组,遵循最佳左前缀法则
2.排序、分组优化
3.MySQL优化的具体场景
1.READ_UNCOMMITTED:读未提交
2.READ_COMMITTED:读已提交
乐观锁+版本机制解决(MVCC)
可能出现幻读
3.REPEATABLE_READ:可重复读 (默认)
4.SERLALIZABLE:串行化
4.数据库隔离级别(2)
2.where是一个约束声明,用来约束来自数据库的数据,having是一个过滤声明
3.where是在结果返回之前起作用的,having是在查询返回结果集以后,对查询结果进行的过滤
5.whrer和having的区别
1.from
2.join
3.on
4.where
5.group by
7.having
8.select
9.distinct
10.order by
11.limit
6.sql语句的执行顺序
聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,
非聚簇索引叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本书的目录,
2.非主键索引都是非聚簇索引
3.MyISAM引擎不支持聚簇索引,只有主键索引
1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的3、聚簇索引适合用在排序的场合,非聚簇索引不适合
7.聚簇索引和非聚簇索引的区别
system->const->eq_ref->ref->range->index->all
8.索引优化的type值
1.Innodb支持事务,MyISAM不支持
2.Innodb支持外键,MyISAM不支持
3.Innodb是聚簇索引,使用B+Tree作为索引结构,MyISAM是非聚簇索引
innodb和myisam的区别
9.mysql的引擎
1、设计良好的数据库结构, 允许部分数据冗余, 尽量避免 join 查询, 提高效率。
2、选择合适的表字段数据类型和存储引擎, 适当的添加索引。
3、MySQL 库主从读写分离。
4、找规律分表, 减少单表中的数据量提高查询速度。
5、书写高效率的 SQL。不要使用 SELECT * FROM TABEL,用什么字段就查什么字段
6.show profiles查看最近的SQL执行时间
10.mysql怎么优化(explain)
1.主键、where后面字段、外键或者关联字段、排序、分组字段;优先组合索引
11.复合索引,如何考虑怎么创建
1.模糊查询LIKE以%开头
2.数据类型错误(类型转换)
3.函数sum...
4.索引列的null(空)
5.索引列进行四则运算
6.最佳左前缀(复合索引不按索引列最左开始查找)
7.全表查找预计比索引更快
12.索引失效
不会影响性能
复杂复杂的sql语句,提高复用性,只保存sql逻辑,不保存任何查询结果
13.mysql的视图会影响性能吗?
针对失效的原因对sql进行优化
14.索引失效解决办法
1.日志分析工具mysqldslow
15.mysql慢日志查询
事务是不可再分的
1.原子性
操作前与操作后数据保持一致
2.一致性
多个事务执行时,互不影响
3.隔离性
一旦执行成功,永久保存到数据库
4.持久性
16.事务的4个特性(ACID)
可以读取到另一个事务未提交的数据
1.脏读
一个事务可以读取另一个事务已提交的数据,单条记录前后不匹配
2.不可重复度
读取到另一个事务已提交的数据,读取的数据前后多了或少了
3.幻读
17.事务并发引起的问题
根据业务进行拆分,把一张表的字段给拆分,一张是数据不经常发生变化的,一张是数据经常发生变化的
1.垂直拆分
一般是数据量太大,需要拆分,分担压力
2.水平拆分
18.mysql的分库分表(mycat拦截sql语句)
19.char最大长度是255字符,varchar(动态长度)最大长度是65535个字节。
MySQL
属性注入失败
1.构造器循环依赖
1.@Scope(\"prototype\")多例
属性注入成功
2.单例
2.feild属性注入循环依赖
存放已经经历了完整生命周期的Bean对象
1.一级缓存 singletonObjects
存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完)
2.二级缓存 earlySingletonObjects
存放可以生成Bean的工厂
3.三级缓存 singletonFactories
3.三级缓存
1.A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
2.B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存 找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
3.B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态) 然后回来继续创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后 完成创建,并将A自己放到一级缓存里面。
4.debug查看
1.Spring创建bean主要分为两个步骤,创建原始Bean对象,接着去填充对象属性和初始化
2.每次创建之前,都会从缓存中查下有没有该bean,因为是单例,只能有一个
3.Spring解决循环依赖依靠的是Bean的“中间态”的一个概念,即对象已经实例化但是还初始化,实例化的过程又是通过构造器创建的,如果A还没创建出来怎么可能提前暴露,所以构造器的循环依赖问题无法解决
5.总结
1.getSingleton():获取单例对象
2.doCreateBean():创建对象
3.polulateBean():初始化对象
4.addSingleton():添加单例对象到某个容器
6.核心方法
由于spring中bean的创建过程是先实例化,再初始化将实例化好的对象暴露出去,提供给其他对象调用,然后使用构造器注入,如果是必须要构造器完成对象的初始化的操作,那就陷入死循环了。
1.为什么构造器注入属性无法解决循环依赖问题?
不能,在三个级别的缓存中存储的对象是有区别的,一级缓存是实例化并且初始化完成的对象,二级缓存是实例化但是还未初始化的对象,如果只有一级缓存,在并发操作下,可能取到实例化但未初始化的对象。
2.一级缓存能不能解决循环依赖问题
理论上可以,但是三级缓存中存放匿名内部类的原因是需要创建代理对象,比如A类需要创建代理对象,A类实例化在三级缓存中存放的是生成具体对象的一个匿名内部类,这个类可能是代理类也可能是普通对象,使用三级缓存可以保证无论是否需要代理对象,都可以保证使用的是同一个对象。
3.二级缓存能不能把解决循环依赖问题
7.其他衍生问题
1.Spring是怎么解决Bean之间的循环依赖的(2)
1.REQUIRED:支持当前事务,如果事务不存在,就新建事务
2.SUPPORTS:支持当前事务,如果事务不存在,就以非事务方式运行
3.MANDATORY:支持当前事务,如果事务不存在,就抛出异常
4.REQUIRES_NEW:挂起当前事务,以新事务运行
5.NOT_SUPPORTS:挂起当前事务,以非事务方式运行
6.NEVER:以非事务运行,如果事务存在,就抛出异常
7.NESTED:嵌套事务,基于jdbc3.0提高的SavePoint技术实现
2.Spring事务传播机制(一个事务方法被另一个事务方法调用时,这个事务方法该如何进行)
问题:@Component这个注解把该类注入到spring容器中了,但是在拦截器之中不生效,再在Utils使用到dao,我们就不能直接注入了
解决方案:使用ApplicationContextAware解决问题。而ApplicationContextAware实现了这个接口的bean,当spring容器初始化的时候,会自动的将ApplicationContext注入进来
3.@Component注解部分场景不生效问题
1.请求到达DispatcherServlet,doDispatch()方法
2.DispatchServlet收到请求调用HandlerMapping处理器映射器
3.处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(集合)一并返回给DispatchServlet
4.DispatchServlet调用HandlerAdapter处理器适配器
5.HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
6.Controller执行完成返回ModelAndView
7.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
8.ViewReslover解析器后返回具体视图
9.DispatchServlet根据View进行视图渲染(将模型数据填充至视图中),响应给用户
4.SpringMVC执行流程
1.setter注入
2.构造方法
3.接口注入(Spring不支持)
三种方式
DI
<bean class=\"类的全路径\" ><construct-args ></bean>
1.构造方法
UserFactory.getUser(); class UserFactory{ User getUser(){ return new User(); } }
<bean class=\"静态工厂全路径\" factory-method=\"getUser\"/>
2.静态工厂
UserFactory factory = new UserFactory();User user = factory.getUser()<bean id=\"factory\" class=\"UserFactory类的全路径\" /><bean id=\"user\" factory-ref=\"factory\" factory-method=\"getUser\"/>
3.实例化工厂
<bean id=\"User\" class=\"FactoryBean的实现类的全路径\"/>
4.工厂bean FactoryBean接口
创建对象四种方式
@Controller、@Service、@Repository、@Component
1.对象初始化注解
1.如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到就抛出异常
2.如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛异常
3.如果指定了type,则从上下文中找类型匹配的唯一bean进行装配,找不到或找到多个,都会抛出异常
4.如果都没有指定,就自动按照byName方式进行装配;如果没有匹配,就回退一个原始类型进行匹配。
装配顺序
@Resource
默认根据类型进行匹配,如果Spring上下文中存在不止一个接口类型的bean时,就会抛出异常
@Autowired
配合@Autowired使用,可以指定一个名字,通过名字进行匹配
@Qualifier
@Autowised、@Resource、@Qualifier
2.依赖注入
RabbitConfig的初始化方法上@PostConstruct
@PostConstruct、@PreDestroy
3.生命周期
@Scope(Singleton、prototype、request、session、globalsession)
4.作用域
四种注解
IOC
实现InvocationHandler接口的invoke方法 Proxy:newProxyInstance
jdk代理
实现MethodInterceptor接口的intercept方法:Enhance.create
cglib代理
基于动态代理
被增强的对象(源对象)
目标对象
增强后的对象
代理对象
可以被增强的点,spring只支持方法级别的连接点
连接点
被增强的连接点
切点
前置通知:目标方法执行前执行
@Before
返回后通知:目标方法正常返回之后执行
@AfterReturning
异常后通知:目标方法抛出异常后执行
@AfterThrowing
后置通知,最终通知:类似于finally。5.3之前在return之前执行,5.3之后在最后执行
@After
1.Object类型的返回值
2.必须要有一个ProceedingJoinPoint连接点类型的参数
3.必须抛出一个Throwable异常
4.必须手动执行目标方法
环绕通知:在前后都可以执行
@Around
通知
在切面中可以定义通知方法
@Aspect
切面
相关概念
AOP
5.Spring源码看过吗?
自定义缓存注解:基于分布式锁和AOP实现
6.AOP是什么,项目中哪里用到
1.resultType,resultMap
2.association对一,collection对多
3.javaType,ofType对多
7.mybatis单表查询和多表查询有什么区别嘛?
和java一样extends
10.mybatis的resultMap可以继承吗?怎么实现
1.resultMap可以自定义结果集比如一些映射或者级联关系的结果
2.resultType返回的是一个Pojo类的结果集使用
3.有List字段就使用resultMap,然后指定一个collection的关联关系
11.resultMap和resultType区别是什么,作用是什么,假如JavaBean里有List字段该怎么使用
1.#{}是预编译的,${}不是预编译的
2.#{}预编译成?,${}是直接字符串拼接的
3.预编译可以防止SQL注入
4.#{}不需要做数据类型转换,${}如果传入数值类型需要'${a}'转换成字符串
5.传入一个参数时#{}可以使用任意参数名接收参数,而${}默认使用${value}接收,在Mapper接口使用@Param(\"\")指定参数名
12.#{}和${}的区别
1.提取公共SQL,指定一个id,别处要使用就refid=\"id\"
13.mybatis使用公共的sql怎么使用
1.配置文件的形式
2.@Bean注解
3.BeanFactory
14.怎么样让一个对象交给Spring来管理
SSM
512M
因为程序出异常mysql可以回滚,redis不会回滚,所以不管先写哪个都会出现数据不一致
1.双写模式
先删redis或者先写mysql:单线程没问题,多线程会出现线程不一致
2.失效模式
删缓存-->写mysql-->提交事务-->再次异步删除缓存(借助AOP后置通知)
3.双删模式★
4.阿里开源canal中间件(主从复制思想,伪装成一个从机,然后通过读取Mysql中继日志,进行数据同步)
2.Redis怎么保证数据一致性
序列化方式保存,最大支持512mb的字符串
1.String
value值为null的一个hash结构
2.set
底层结构是hash,value值用来存分数,key用来存值,可以按照分数对key进行排序
3.zset
添加顺序有序,两端操作效率高
4list:双向连表
一般用来描述对象
5.hash:由多对kv组成
3.redis数据结构有哪些?
1.登录校验的token存储在redis中
2.购物车商品实时价格
3.用户的购物车信息
5.分布式锁
6.订单的唯一ID
4.哪些地方用到redis
访问不存在的key
1.缓存穿透
大量key同时过期
2.缓存雪崩
热点key过期
3.缓存击穿
缓存问题(2)
5.redis缓存会出现哪些问题
1.添加过期时间防止死锁,set key value ex 30 nx
2.可重入,hash + lua脚本 防止误删并且保证了原子性
3.解锁:先判断再删除
6.redis分布式锁怎么解决死锁
先尝试获取锁,获取到锁,执行以下步骤
1.判断自己的锁是否存在
2.是自己的锁则重置过期时间
自动续期
7.redis分布式锁过期时间到了怎么办?
4.首页的分类信息:一二三级分类
1.为了防止缓存击穿,添加分布式锁
8.还有哪些地方用到了分布式锁
1.基于内存
2.非阻塞IO,多路IO复用
9.redis为什么性能好
惰性删除,读/写一个已经过期的key时,会直接删除这个key
1.被动删除
定期淘汰一批已过期的key
2.主动删除
3.当前已用内存超过maxmemory值时,触发主动清除策略
10.redis对于过期key的删除策略
LRU:上一次使用时间离现在最久的
1.针对有过期时间的key/针对所有key,使用lru算法淘汰
LFU:最近使用频率低的
2.针对有过期时间的key/所有的key,使用lfu算法
11.redis内存淘汰策略
1.效率高,节省磁盘
2.无法保证数据的高可用性:比如系统在定时持久化之前宕机,导致数据丢失
3.RDB是通过fork子进程来协助完成数据持久化,当数据集较大时,可能会导致服务器停止服务几百毫秒,甚至一秒
1.RDB:二进制快照方式,直接把内存中的数据持久化到一个dump.rdb文件中
1.效率低,占用磁盘
2.可以保证数据的高可用性,哪怕是在写入的过程中服务器宕机,也不会破坏日志文件中已经存在的内容,在redis下一次启动之前,可以通过,redis-check-aof工具来帮助我们解决数据一致性问题
2.AOF:日志拼接方式把执行的命令写到一个.aof文件中
12.redis持久化
13.redis扩容机制
1.代码逻辑写死
14.redis缓存,怎么判断缓存失效
Redis集群有16384个哈希槽,每个key通 过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
15.redis哈希槽
1.禁止外网访问 bind 127.0.0.1
2.设置密码
16.redis安全机制
Redis
1.在对首页及搜索页面商品进行增删改操作的时候,向es同步数据时使用了。
2.购物车中价格变化,用缓存+MQ实现了实时价格
3.订单提交的分布式事务:MQ+补偿机制
4.延时队列和死信队列实现定时关单、定时解锁库存
5.用户注册成功后,如果三天没有登录就发短信进行提醒
6.用户发起退款,如果三天没有得到处理则通知相关运营人员
1.在项目的哪里用到了RabbitMQ
1.消息中间件
2.延时队列
3.死信队列
2.相关概念
用过类似的消息中间件,RabbitMQ。
3.Kafka了解过吗?
1.生产者确认
2.消费者确认:basicAck
1.交换机持久化、队列持久化、消息持久化(三者都要持久化):重启服务消息、队列、交换机依然存在
3.消息持久化
4.MQ如何防止消息丢失
1.如果是数据库的写入操作,就先根据查询一下,如果已经存在,就不再执行insert操作,可以执行update
2.如果是写入redis,redis的set操作本身具有幂等性,所以不用担心
3.生成一个唯一id,防止表单重复提交
5.如何保证消息不被重复消费
如果说积压了几百万的数据的话,这个时候修复了消费者,让他恢复速度,那么也得等几个小时才能恢复,所以我们采取的措施是临时的紧急扩容
1.先修复消费者的问题,确保其恢复消费速度,然后将现有的消费者都停掉
2.新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量
3.然后写一个临时的分发数据的consumer程序,部署上去消费积压的数据,消费之后不做耗时的处理,直接轮询写入临时建立好的10倍数量的queue
4.接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时的queue的数据
5.这个做法相当与临时将队列和消费者资源扩容10倍,以正常10倍的速度来消费数据
6.等快速消费完积压的数据之后,就恢复原先的部署架构,重新用原先的消费者机器来消费消息
6.几百万的消息持续积压几小时,如何解决?
7.如果这些消息的设置了TTL的就意味着大量的消息丢失,如何解决?
RabbitMQ
1.用户发送请求,到达新增的方法,此时触发TM,发送一个开启全局事务的请求给TC, TC收到请求之后生成一个全局事务和一个唯一标识XID,XID会在微服务的调用中传播
2.当这个新增的方法开始调用其他微服务的方法时,RM会携带XID给TC,TC会给这个XID的全局事务 注册一个分支事务
3.整个调用链执行完毕之后,TC才算是完成了整个事务的注册工作,对应的新增方法也执行完毕
4.新增方法执行完毕后,TM会携带XID向TC发起事务的提交或回滚,TC会命令各个分支事务的提交或回滚
以项目为例
1.Seata的实现原理
1.TM向TC注册一个全局事务,TC生成一个全局事务,并返回一个全局事务的唯一标识XID
2.XID会在微服务的调用链路中传播
3.RM向TC注册一个本地事务,作为XID相对应的全局事务的一个分支事务
4.TM向TC发起XID相应的全局事务的提交或回滚
5.TC驱动RM完成XID相应的全局事务下的所有分支事务的提交和回滚
2.Seata的工作流程
Seata
1.synchronized是JDK提供的关键字,lock是一个接口
2.lock支持读共享,轻量级锁,更加灵活;synchronized重量级锁(读写排他)
3.lock可以避免出现死锁(tryLock()),synchronized无法避免死锁
4.lock支持中断,synchronized不支持中断
5.都是可重入锁
6.lock支持非公平和公平锁,sychronized只支持非公平
7.lock发生异常时需要手动释放锁,synchronized发生异常自动释放锁
8.lock性能高,sychronized性能低
1.多线程synchronized和lock的区别
1.volatile只能保证变量的修改可见性不能保证原子性,sychronized可以保证原子性和可见性
2.volatile不会造成线程的阻塞,sychronized可能会造成线程的阻塞
3.volatile只能用在变量上,sychronized可以用在变量、方法、和类上
4.volatile本质是JVM当前变量在工作内存中的值是不确定的,需要从主内存中读取, sychronized是锁定当前变量,只有当前线程可以访问,其他线程会被阻塞
2.synchronized和volatile的区别
项目主要用于算术运算:cpu核数+1
1.cpu密集型
主要是逻辑运算:核数/(1-0.9)
2.IO密集型
1.核心线程数corePoolSize
2.最大线程数maximumPoolSize
3.多余空闲线程存活时间keepAliveTime
4.存活时间的单位unit
1.ArrayBlockingQueue:规定大小的阻塞队列(FIFO)
2.LinkedBlockingQueue:大小不固定的阻塞队列,大小有限制,但不指定大小(FIFO)
3.PriorityBlockingQueue:类似于Link,但是排序不是FIFO,自然排序或者根据构造函数的Comparator决定
4.SynchronizedQueue:特殊的阻塞队列,操作必须是放和取交替完成
5.任务队列workQueue
6.线程工厂threadFactory,一般使用默认的
1.AbortPolicy默认的,抛异常
2.CallerRunsPolicy调用者运行,将某些任务回退到调用者
3.DiscardOldestPolicy:抛弃队列中等待最久的任务,把当前任务加入队列中,尝试提交当前事务
4.DiscardPolicy:丢弃无法处理的任务,如果允许任务丢失,这是最好的策略
7.拒绝策略handler(2)
3.线程池参数(2)
Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置。
4.线程池的创建方式
1.通过jstack命令查看:jstack/jvisualvm命令中显示发生了死锁的线程
show OPEN TABLES where In_user > e;
1.查询是否锁表
show processlist
2.查询进程
select * from information_schema.innodb_locks
3.查询正在锁的事务
select * from information_schema.innodb_lock_waits
4.查看等待锁的事务
2.或者两个线程去操作数据库时,数据库发生了死锁,可以查询死锁情况的SQL
5.如何查看死锁
1.一个资源每次只能被一个线程使用
2.一个线程在阻塞等待某个资源时,不释放已占有的资源
3.一个线程已获得的资源,在未使用完之前,不能被强行剥夺
4.若干线程形成头尾相接的循环等待资源关系
1.死锁原因
1.注意加锁顺序,保证每个线程按同样的顺序进行加锁
2.注意加锁时限,可以针对锁设置一个超时时间
3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并解决
2.注意
6.死锁原因及如何避免死锁
join,yeild
7.就绪--->运行
1.创建
Thread.start()
2.就绪
CPU执行线程 run方法执行
3.运行
Object.wait()
Object.notify()
4.等待
Thread.sleep(long time)
5.休眠
run执行完毕,Thread.destroy()
6.消亡
8.线程状态/生命周期
普通线程
守护线程
9.线程分类
JUC
1.栈溢出异常,可能导致OutOfMemory
1.超大对象,通常是一个大数组
2.超出预计的访问量,比如各种秒杀活动
3.内存泄漏,大量对象引用没有释放,JVM无法对其自动回收
2.堆溢出异常
3.元空间异常
1.如果是因为超大对象,比如一次性查询了数据库全部结果,需要对结果集进行过滤
2.添加内存条
3.如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接
解决
1.OOM异常的原因和解决方法
1.局部数组过大
2.递归调用层次太多
3.指针或数组越界
2.什么情况下导致栈溢出异常
实现简单、解决不了循环引用问题,容易造成内存泄漏
1.引用计数法(基本不用)
实现简单,遍历两边堆内存,效率低容易造成内存碎片
2.标记清除法(老年代)
解决了内存碎片问题,劣势遍历三遍,效率更低
3.标记整理清除法(老年代)
效率高,浪费内存空间
4.复制算法(年轻代)
3.项目中用的GC算法是什么?
4.JVM结构
遵循\"先进后出/后进先出\"原则,栈顶就是当前的方法,执行完毕就弹出栈,依次执行下面的方法
5.JVM怎么保证出栈顺序
1.强引用:Object obj = new Object();
在系统将要OOM之前,回收
2.软引用:SoftReference<Object> sf = new So...;
GC工作时,无论内存是否够,都会回收
3.弱引用:WeakReference<Object> wf = new ...;
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知
4.虚引用:PhantomReference<Object> pf = new ...;
1.引用计数法:当一个变量或者实例没有被引用时(四种引用)
2.可达性分析算法:扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收
6.JVM垃圾回收判定
1.经过15次Minor GC,如果一个对象还活着,就会从From区跑到老年代
-XX:PretenureSizeThreshold(设置一个大对象的标准)
2.当一个对象超过指定的大小时,
3.当伊甸园区经过minorGC之后,存活的对象大小之和+from区存活大小之和如果大于to区大小,直接从伊甸园区跑到老年代
大对象或者长期存活的对象
7.JVM堆中老年区保存的都是什么对象?
1.启动引导类加载器
2.拓展类加载器
3.应用类加载器
4.自定义类加载器
8.类加载器分类
1.当某个类加载器要加载.class文件时,会先把这个任务委托给自己的上一级加载器
1.保证系统核心类库不被随意篡改,沙箱安全机制
2.避免类的重复加载
作用
9.双亲委派机制
存静态变量、类信息、常量
JDK1.7:永久代PermGen
JDK1.8:元空间Metaspace
1.运行时常量池:非字符串常量池
2.string table:保存string信息的
3.JDK1.7以后,字符串常量池跑堆中,运行时常量池还在方法区
运行时常量池
10.方法区
JVM
1.微服务是一种概念,boot是一种具体实现
1.Boot和微服务架构有啥区别
2.SpringBoot启动过程说一下(2)
1.自动配置
2.依赖管理,只需引入一个框架的场景启动器即可使用
3.SpringBoot怎么使用的,使用了多久
SpringBoot
因为eureka sc2的时候停止维护。只能作为注册中心,如果想要配置还需要单独加一个Spring Cloud Config。nacos集中配置,利用namespace隔离配置,版本回滚,中间件加的越多越不稳定。
为什么不用eureka
1.服务注册与发现,配置nacos
未来替换方案Spring Cloud LoadBalancer
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,由nginx实现转发请求。即负载均衡是由服务器端完成的
Ribbon本地负载均衡,在调用微服务接口的时候,会在注册中心获取服务列表然后缓存到JVM本地,从而在本地实现RPC远程服务调用
Ribbon VS Nginx
核心组件Irule
2.负载均衡服务调用Ribbon
1.SpringCloud为每个FeignClient生成一个代理对象
2.代理对象会分析类与方法上的注解,然后判断出服务名与请求方法名的路由
3.从注册中心获取指定服务名的所有真实地址
4.利用负载均衡策略选择一个最佳地址,利用RestTemplate进行调用
5.等待结果返回
1.执行原理
不是每次都需要,Feign维护了一个服务与地址的关系清单List<>,当我们使用Nacos(RPC),注册中心的某个服务的地址变化时,nacos将数据实时推送(长连接)给SpringCloud;然后更新关系清单,确保我们每次服务调用这个清单都是最新的
2.对于服务的地址,每次请求都需要去注册中心获取吗?
Feign会进行重试,可以进行配置:重试次数,超时时间,请求处理时间
3.如果选择的最佳地址由于某种原因无法使用该如何处理?
默认不会,Feign会从注册中心根据服务名查询服务地址,然后发起请求
4.服务与服务之间的调用是否会经过网关?
3.服务接口调用OpenFeign(支持MVC注解)
服务降级Fallback
服务熔断Breaker
服务限流Flowlimit
4.断路器Hystrix,SpringCloud Alibaba Sentinel
1.Route(路由)
2.Predicate(断言)
3.Filter(过滤)
4.请求过滤,统一鉴权,负载均衡
5.网关gateway
6.分布式链路追踪 sleuth+zipkin
1.SpringCloud有哪些组件
经过各个过滤器的过滤,将满足指定断言规则的请求路由到指定位置
1.基于redis★:setnx
2.基于关系型数据库:唯一键索引
3.基于zookeeper:结构类似linux,文件路径唯一性
实现方式
1.使用setnx保证独占排他,客户端获取锁,服务器立马宕机会导致死锁
2.set key value ex 30 nx,解决了死锁问题,并且保证了加锁和设置过期时间的原子性
3.解决不可重入导致的死锁问题:hset类似于嵌套map的结构,第一个key是锁的名字,第二个key是uuid用于确保可重入,加锁时lockName和uuid都相同就将value+1。
4.防误删:删除之前先判断是否是自己的锁:hexists判断key+uuid确认是否是自己的锁,如果value的值大于1就-1,如果=0就完成解锁。
5.自动续期:Timer定时任务;每过三分之一时间,就重新执行expire key 30
实现步骤
Lua脚本确保操作执行的原子性
2.分布式锁的设计细节(2)
2.锁必须要有一个超时强制释放机制,避免服务器宕机,锁无法得到释放
3.取锁之前和取锁之后都获取当前时间,endTime-beginTIme > TTL(超时时间),表示刚获取到锁就过期了,获取锁失败
4.如果获取锁失败,那么要在所有redis节点执行释放锁操作。避免由于超时情况,有些节点获取到锁,无法得到释放
3.redlock讲一下你的理解
1.宕机重启之后,2个客户端拿到同一把锁。---延时重启
时钟跳跃可能会导致机器宕机后立马重启
2.时钟跳跃
3.客户端大延迟(比如full GC),2个客户端拿到同一把锁
4.redlock的弊端
网关经过各个过滤器之后将符合断言的请求路由到具体的微服务接口
5.前端调用接口,后端寻找对应微服务的具体链路
1.一致性
2.可用性
3.分区容错性
6.CAP解释一下
1.通过set方法可以直接存入一个参数
2.get方法可以直接拿出参数
4.先获取当前线程,T t
5.ThreadLocalMap map = getMap(t)
7.threadlocal怎么传递参数的
1.路由id:唯一标识
2.uri:lb://ums-service
3.predicates: - Path=/ums/**
4.filter过滤器
负载均衡配置
8.网关怎么配置路由
实现GlobalFilter,顺序实现Ordered
1.全局过滤器
1.继承AbstractGatewayFilterFactory抽象类
2.在需要过滤的微服务路由中配置该过滤器
3.定义一个读取配置的静态内部类
4.实现shortcutFieldOrder(),指定接收参数字段顺序
5.实现shortcutType(),指定读取字段的结果集类型
6.构造方法:super(KeyValueConfig.class);指定静态内部类作为抽象类的泛型
2.局部过滤器
9.网关过滤器有哪些
SpringCloud
采用redis+mysql的方式进行存储:不管有没有登录都将用户信息存入数据库,方便以后进行数据分析查询时,从redis查询提高查询速度;写入时,同步操作redis,异步操作mysql进行写入。
1.购物车比较特殊,是读多写多的一个场景,所以处理方式和别的业务有区别
1.需求分析
1.编写自定义拦截器实现HandlerInterceptor接口,重写前置方法
2.编写配置类实现WebMvcConfigurer接口,重写addInterceptor方法,配置拦截路径
1.先获取当前线程,T t
2.通过getMap(t)方法获取局部变量ThreadLocalMap
3.ThreadLocal真正的局部变量就是Map,key是当前的ThreadLocl,value是传入的变量
4.通过调用get,set方法就可以完成参数的传递
3.将用户的信息存入ThreadLocal线程局部变量中,确保用户信息可以在微服务的调用中传递下去
springboot自定义拦截器
2.用户信息使用拦截器同一获取
1.未登录:使用UUID生成userKey作为用户id进行存储
2.已登录:使用该用户的userId进行存储
1.判断是否登录
调用其他的微服务查询该商品的销售属性、营销属性、库存信息等,并将价格存入实时价格缓存中
1.没有:新增
2.有:更新数量
2.判断该用户的购物车中是否有该商品
3.新增时将商品价格加入实时价格缓存,因为用户将商品加入购物车后,商品的价格可能会有变化。缓存+MQ; 商品价格改变后发送消息给购物车微服务,向redis进行价格同步,mysql中的价格采用xxl-job定时任务每隔一个星期更新一次
1.核心线程数
2.最大线程数
3.空闲线程的存活时间
4.存活时间单位
5.工作队列
6.线程工厂
7.拒绝策略
1.配置线程池
2.配置异步异常处理器
1.编写一个类实现AsyncConfigurer
2.新建一个异步的service类,因为同一个service中,使用分布式事务会有问题
4.对mysql的操作使用SpringTask的异步任务完成的(本地异步性能高)双写、如果追求高一致性就双删
3.新增购物车
1.不管有没有登录,都先用userKey查询未登录的购物车
2.判断登录状态,userId == null,直接返回未登录的购物车
3.已登录:判断未登录购物车是否为空,为空:就直接返回已登录购物车
1.相同:更新数量(此时未登录购物车中商品依然存在,后续需要删除)
2.不相同:新增购物车记录(将userKey设置为userId即可)
4.非空:就把未登录的购物车合并到已登录的购物车中(判断是否有相同的商品)
5.查询并返回已登录购物车
6.清空未登录的购物车(相同商品依然存在未登录购物车中,所以需要删除)
4.查询购物车
1.购物车的具体流程(4)
1.用户的收货地址列表
2.商品清单
3.购物积分
4.库存、营销信息、销售属性
1.雪花算法
2.用户id+时间戳
3.手机号+时间戳
4.uuidgenerator(百度)
5.leaf(美团)
6.mybatis提高的idworker
1.分布式id:主机和进程的机器码/timeId
5.当前页生成一个唯一的orderToken确保订单提交的幂等性 页面携带一份,redis存一份,提交时进行比较,提交完成后删除。
1.订单确认(异步编排优化获取)
1.根据页面传过来的orderToken和redis中的orderToken进行比较
2.验证通过后要把redis中orderToken进行删除,为了保证验证和删除操作的原子性,使用lua脚本
1.防重
1.把页面上的总价格和数据库中的总价格进行比较,防止因为一些促销活动的过期导致价格不一致的问题
2.验总价
3.限购数量
1.验库存和锁库存在同一个接口中完成,保证原子性
2.获取仓库的集合,根据大数据提供的接口获取离该用户最近的一个仓库,对库存进行加锁
3.锁完库存立马发送延时消息25小时后自动解锁库存,防止系统异常导致库存永久被锁
4.验库存并锁库存(防超卖)
1.由于远程调用可能会超时,所以也需要发送消息给订单微服务,将订单修改为失败订单
2.订单提交后,立刻发送一个延时消息24小时未支付,自动关单,将订单修改为无效订单
5.创建订单
发送消息给购物车微服务删除购物车中对应的记录
6.异步删除购物车中记录
2.订单提交(异步编排)
订单由待发货--->无效订单
1.当用户点击确认退单时
2.发送消息给订单微服务去更新订单的状态
3.发送消息给库存微服务更新的数量
3.退单
2.订单的具体流程
使用UUID随机生成的user-key作为数据库的userId存放在数据库中。
3.游客数据怎么存的
实时价格:使用缓存+MQ的最终一致性
4.当用户添加购物车,但后续商品价格浮动,如何展示给用户
idworker:主机和进程的机器码
5.你们订单号的实现(雪花算法),有什么问题吗?
先经过网关
6.你们Controller层的接口是否直接暴露给前端
7.介绍下SpringSecurity在你们项目中的使用
1.获取用户的请求路径,判断该路径是否在拦截名单中;不在名单中就直接放行
2.获取请求中的token,判断是否同步请求;同步请求从cookie中获取,异步请求从请求头中获取(一个网站有很多cookie,使用cookie方式传递token,网络传输压力太大)
3.判断token是否为空,为空就设置状态码302并重定向到登录页面
4.使用公钥解析token,并判断IP地址是否相同,不相同就重定向到登录界面(解析出现异常也重定向到登录)
5.将登录信息传递给后续服务,后续服务不用再解析JWT了
6.放行
8.授权中心怎么做的,大概流程说一下
1.为了防止缓存击穿,添加的分布式锁,基于Redisson实现
9.分布式锁在你们项目中的使用
核数+1
1.CPU密集型
4核;40个
核数/(1-0.9)
10.核心线程数你们配置的多少?
1.后台系统使用seata
2.前台系统MQ的最终一致性+补偿机制
11.项目的事务用的什么
1.商品表10w记录
2.用户20w
3.订单8w
nginx:2
nacos:3
微服务:40左右
mysql:12
redis:主从哨兵:5
rabbitmq:2
ES:3
4.服务器60-70台
5.并发600-1000,甲方数据拿不到
arthas组件
6.系统出问题平时怎么查看?
1.开发人员将代码提交到Gitlab代码仓库
2.Jenkis从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库
3.Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器
4.用户就可以访问容器了
7.生产环境部署:docker
12.细节
第一范式:字段原子性
第二范式:主键关联性
第三范式:字段之间不出现传递关系
1.三大范式
2.根据页面分析所需的数据,设置一个冗余字段备用(以购物车为例)
13.数据库表的设计
项目
Java面试题
0 条评论
回复 删除
下一页