一、JavaSE
1、jdk1.8和其他版本有哪些区别?
1.jdk1.8 新增了 Lambda 表达式
2.jdk1.8 新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型
3.jdk1.8 新增了接口的静态方法和默认方法
4.jdk1.8 更新了日期时间API:LocalDate、LocalTime、LocalDateTime、DateTimeFormatter(实现日期时间和字符串之间的相互转换)
5.jdk1.8 新增了 StreamAPI:创建 Stream、中间操作、终止操作
6.jdk1.8 将方法区的实现永久代改为了元空间
2、解释下java中的Stream()流
当项目中需要把 map 集合转换成 list 集合时,本来需要写很多代码,现在用 Stream()流可以很简单的就实现了
3、hashMap的底层原理 * 6
JDK1.71.映射关系被封装为HashMap.Entry类型2.底层是数组 + 链表3.形成链表时,采用头插法4.饿加载创建数组JDK1.81.映射关系被封装为HashMap.Node类型2.底层是数组 + 链表 + 红黑树3.形成链表时,采用尾插法4.懒加载创建数组5.当数组长度大于64,链表长度8时,转为红黑树存储名词解释:树化阈值:8树化阈值:6最小树化容量:64初始化容量:16默认负载因子:0.75,太大hash冲突会比较严重,太小数组扩容的频率太频繁扩容的临界值 = 数组的容量 * 负载因子数组的容量:2 的 n 次幂,因为扩容 2 倍 为什么链表结构要转换为红黑树结构?1.当链表长度过长时,查询效率低2.链表的时间复杂度为 O(n)、红黑树的时间复杂度为 O(lgn)
4、HashMap的扩容机制
以JDK1.8为例:1.计算 key 的哈希值,如果 key 为 null,会放在index[0] 的位置2.判断是否为第一次 put ,如果是第一次则初始容量为16的数组,临界值为123.然后计算得到 index,判断 index 位置上的数据是否为空4.如果为空,则判断数组是否需要扩容5.如果小于临界值,则添加成功,否则扩容2倍,再添加成功6.如果 index 位置不为空,则判断链表的长度7.如果链表的长度小于8,调用 equals 方法判断元素是否相等,相等则覆盖8.不相等则添加到链表中9.如果链表的长度大于8,且数组的长度大于64,则转换为红黑树存储
5、set,list,map的区别 * 4
set
|
list
|
map
|
无序,不可重复
|
有序,可重复
|
key:无序,不可重复;value:无序,可重复
|
HashSet、LinkedHashSet、TreeSet
|
ArrayList、LinkedList、Vector
|
HashMap、TreeMap、HashTable
|
ArrayList
|
Vector
|
LinkedList
|
线程不安全
|
线程安全
|
线程不安全
|
动态数组
|
动态数组
|
双向链表
|
JDK1.7之前底层是默认长度为10的数组,默认扩容1.5倍,JDK1.7之后底层默认是空数组
|
底层默认是长度为10的数组,默认扩容2.0倍
|
当频繁在集合中插入、删除元素时,效率较高,但是查找遍历的效率较低
|
6、hashmap 和 hashtable 和 hashset 区别?* 2
hashmap
|
hashtable
|
hashset
|
底层是哈希表,线程不安全,速度快
|
底层是哈希表,线程安全,速度慢
|
底层是hashmap
|
key ,value可以存储 null 值
|
key,value不可以存储 null 值
|
可以存储null值
|
7、反射是什么
通过反射机制,可以获得运行期间对象的类型信息,在很多框架中都有使用,比如spring、mybatis,利用反射可以实现工厂模式和代理模式等
8、new String(“123”)创建了几个对象
第一次:创建了两个,堆空间一个,常量池一个
第二次:创建了一个,堆空间一个,常量池中已存在不会在创建
9、你认为的编码规范的风格是什么样子的
10、stringbuffer和stringbuilder的区别说一下/为什么是线程安全的
String
|
不可变字符序列,字符串常量存储在常量池中,属于引用数据类型,底层使用 char 型数组
|
StringBuilder
|
可变的字符序列,底层是长度为16的 char 型数组,默认扩容2倍+2,线程不安全
|
StringBuffer
|
和 StringBuilder类似,但是在所有的方法上都加了 synchronized 同步锁,线程安全
|
执行效率
|
StringBuilder > StringBuffer > String
|
11、IO说一下?
1.Java中 I/O 操作主要是指使用 java.io 包下的内容,进行输入、输出操作
2.输入流:把数据从其他设备上读取到内存中的流
3.输出流:把数据从内存中写入到其他设备上的流
4.字节流:以字节为单位,读写数据的流
5.字符流:以字符为单位,读写数据的流
6.ObjectOutputStream(序列化):内存中的对象--->存储中的文件、通过网络传输出去
7.ObjectInputStream(反序列化):存储中的文件、通过网络接收过来 --->内存中的对象
12、java里面"=="和"equals"的区别
==
|
equals
|
适用于基本数据类型:比较变量的值是否相等
|
只适用于引用数据类型:未重写的情况下等 == 作用一样
|
适用于引用数据类型:比较两个引用的地址是否相等
|
开发中一般重写 equal 方法,用于比较两个对象的实体内容是否相等
|
13、深拷贝与浅拷贝的理解
深拷贝和浅拷贝就是指对象的拷贝,一个对象中存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用。1.浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象2.深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象
二、JavaWEB
三、SpringMVC
1、你们Controller层的接口是否直接暴露给前端
2、前端调用接口,后端寻找对应微服务的的具体链路
3、springMVC执行流程
(1)用户发送请求至前端控制器 DispatcherServlet
(2) DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器,请求获取Handle方法
(3)处理器映射器根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
(4)DispatcherServlet 调用 HandlerAdapter 处理器适配器
(5)HandlerAdapter 经过适配调用具体处理器(Handler,也叫后端控制器)
(6)Handler执行完成返回 ModelAndView
(7)HandlerAdapter 将 ModelAndView 返回给 DispatcherServlet
(8)DispatcherServlet 将 ModelAndView 传给ViewResolver 视图解析器进行解析
(9)ViewResolver 解析后返回具体的视图 View
(10)DispatcherServlet 对视图 View 进行渲染视图(将模型数据填充至视图中)
(11)最后 DispatcherServlet 响应给用户
四、Spring
1、Spring是怎么解决Bean之间的循环依赖的
2、spring事务传播机制
REQUIRED
|
支持当前事务,如果不存在,就新建一个
|
SUPPORTS
|
支持当前事务,如果不存在,就不使用事务
|
MANDATORY
|
支持当前事务,如果不存在,抛出异常
|
REQUIRES_NEW
|
如果有事务存在,挂起当前事务,创建一个新的事务
|
NOT_SUPPORTED
|
以非事务方式运行,如果有事务存在,挂起当前事务
|
NEVER
|
以非事务方式运行,如果有事务存在,抛出异常
|
NESTED
|
如果当前事务不存在,就新建一个事务运行,如果存在,则嵌套事务执行
|
3、Spring的IOC、AOP讲一下
概念
|
IOC 即控制反转,将创建对象和对象间的依赖关系放入 IOC 容器中交给 Spring 管理
|
作用
|
管理对象的创建(生命周期)、维护对象间的依赖关系、解耦
|
原理
|
IOC 的实现原理就是工厂模式加反射机制
|
4、Aop是什么?项目用到了吗? * 2
概念
|
AOP 也就是面向切面编程,是面向对象的一种补充,在不修改源代码的基础上对业务功能进行增强
|
作用
|
减少系统的重复代码、降低系统的耦合度、提高了系统的可维护性、常用于事务、日志处理等
|
原理
|
基于动态代理( JDK 的动态代理、Cglib动态代理)
|
AOP实现缓存:1.自定义缓存注解@GmallCache和注解的属性(类似于事务@Transactional)2.编写切面类,使用环绕通知实现缓存的逻辑封装3.在业务方法上添加自定义注解,即可完成缓存功能
五、Mybatis
1、mybatis单表查询和多表查询有什么区别嘛? ×
2、mybatis里resultmap可以继承吗?怎么实现 ×
3、resultmap和resulttype的区别作用是什么(假如我的JavaBean里有List字段我该使用什么) ×
4、mybatis用什么接收参数的 #{}和${}的区别
#{}
|
${}
|
预编译处理
|
字符串替换
|
处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值
|
处理 ${}时,就是把{}内的值替换成变量的值
|
可以有效的防止SQL注入,安全
|
有 SQL 注入的风险,不安全
|
5、mybatis使用公共的sql是怎么使用的 ×
1.使用sql标签抽取重复出现的SQL片段
<sql id="mySelectSql"> select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp </sql>
2.使用include标签引用声明的SQL片段
<include refid="mySelectSql"/>
六、Redis
1、Redis中的一个key的value是多少
一个Redis中字符串value最多可以是512M
2、redis数据结构有哪些
1.Redis字符串(String): String的数据结构为简单动态字符串
2.Redis列表(List) : 底层是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差
3.Redis集合(Set) : 功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的
4.Redis哈希(Hash): Redis hash是一个string 类型的 field 和 value 的映射表,hash特别适合用于存储对象
5.Redis有序集合Zset(sorted set): zset与普通集合set非常相似,不同之处是有序集合的每个成员都关联了一个评分,可以实现排序
6.Bitmaps:合理地使用操作位能够有效地提高内存使用率和开发效率
7.HyperLogLog:是一种用来做基数统计的算法
8.Geospatial:该类型,就是元素的2维坐标,在地图上就是经纬度
3、redis的set和zset有什么区别
zset与普通集合set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分,可以实现排序
4、redis在项目中哪些地方用到
1.商品刚加入购物车时缓存实时价格
2.将当前用户的购物车信息存入Redis
3.首页内容信息的展示,把分类列表的查询结果放入缓存
4.把库存的锁定信息保存到 redis 中
5.将防重的唯一标识、订单号存入 Redis 缓存
6.购物车异步写入数据库异常时,会将异常的userId缓存到Redis,将来定时任务定时进行 MySQL 数据同步
7.一个商品对应多个用户评论,并且按照时间顺序显示评论,为了提高查询效率,因此我们选择了redis的list类型将商品评论放在缓存中
8.在统计模块中,我们有个功能是做商品销售的排行榜,因此选择redis的zset结构来实现
5、redis缓存写的一致性问题 * 2
5.1 双写模式:会产生写的一致性问题
先写 Redis
|
写 Redis 成功 (新值)
|
写 MySQL 失败,回滚 (旧值)
|
数据不一致
|
|
先写 MySQL
|
写 MySQL 成功 (新值)
|
写 Redis 成功 (新值)
|
代码异常服务器宕机,MySQL 回滚 (旧值)
|
数据不一致
|
5.2 失效模式:高并发会产生写的一致性问题
a用户(写)
|
先删 Redis (空)
|
再写 MySQL
|
提交 MySQL (新)
|
b用户(读)
|
|
查询数据,先查 Redis (空),再查 MySQL (旧),放入 Redis (旧)
|
|
a用户(写)
|
写 MySQL 成功,删 Redis (空)
|
|
提交 MySQL (新)
|
b用户(读)
|
|
查询数据,先查 Redis (空),再查 MySQL (旧),放入 Redis (旧)
|
|
5.3 双删模式:完美解决写的一致性问题
1. 当要写 MySQL 的时候先删除Redis
2. 写 MySQL
3. 提交事务,MySQL 数据更新为新数据
4. 异步删除 Redis
5. 再读的话会查询数据库,然后写入Redis,实现数据一致
6、redis缓存并发读问题
并发读问题
|
概念
|
解决方案
|
缓存穿透
|
大量请求访问数据库不存在的数据,此时缓存中没有大量请求直达数据库
|
数据为空也进行缓存、使用布隆过滤器
|
缓存雪崩
|
缓存时间相同,导致大量 key 同时过期,导致大量请求会直达数据库
|
给缓存时间添加随机值
|
缓存击穿
|
一个热点 key 过期,导致大量请求会直达数据库
|
加分布式锁
|
7、分布式锁的设计细节 * 2
实现方式
|
可靠性
|
实现复杂度
|
性能
|
推荐使用
|
基于 Redis 实现
|
较高
|
简单
|
最高
|
√
|
基于关系型数据库实现
|
最高
|
中等
|
较差
|
|
基于 zookeeper 实现
|
较高
|
困难
|
中等
|
|
第一步
|
多个客户端同时尝试获取锁(setnx,独占排他)
|
第二步
|
获取成功,执行业务逻辑,执行完成释放锁(del)
|
第三步
|
其他客户端等待重试(自旋锁)
|
问题一
|
setnx 刚好获取到锁,业务逻辑出现异常,导致锁无法释放 ,可能导致死锁问题
|
解决
|
设置过期时间,自动释放锁,set 时直接指定过期时间(保证加锁原子性)
|
问题二
|
当业务逻辑的执行时间大于设置的锁的过期时间,可能会导致释放其他服务器的锁,出现误删的情况
|
解决
|
setnx 获取锁时,设置一个唯一的 UUID,释放前获取这个值,判断是否自己的锁
|
问题三
|
删除操作仍然缺乏原子性,判断 UUID正确 后,执行删除操作前,锁刚好过期,这时仍然会出现误删的情况
|
解决
|
使用官方推荐使用的 LUA 脚本保证删除的原子性(保证解锁原子性)
|
问题四
|
程序的子任务代码也加锁,需要等待锁释放之后,再次获取锁成功,才能继续往下执行,可能出现死锁问题
|
解决
|
基于 Redis Hash + lua脚本 实现可重入锁
|
问题五
|
当业务逻辑的执行时间大于设置的锁的过期时间,多个线程执行业务逻辑,有线程安全问题和误删问题
|
解决
|
设置锁的过期时间自动续期(Timer定时器 + lua脚本),判断自己的锁是否存在,存在则重置过期时间
|
问题六
|
集群情况下锁机制可能失效
|
解决
|
使用RedLock算法(红锁算法,redis特有的)
|
9、redis分布式锁怎么解决死锁?
1.设置锁的过期时间
2.实现可重入锁
具体见分布式锁的设计细节
10、redis分布式锁如果过期时间到了怎么办?
自动续期:设置锁的过期时间自动续期(Timer定时器 + lua脚本),判断自己的锁是否存在,存在则重置过期时间
11、还有哪些地方用到了分布式锁?* 2
验库存、锁库存(保证原子性)
12、redis为什么性能好?
1.因为是内存层面,不用进行IO操作
2.数据类型多,应用场景多,容易解决各种问题
3.单线程,多路IO复用
13、redis为什么是单线程的?
1)绝大部分请求是纯粹的内存操作
2)采用单线程,避免了不必要的上下文切换和竞争条件
14、redis的消息是怎么传递的?
Redis 的发布订阅,实际开发时发布订阅方面的需求还是要找专门的消息队列产品来完成
15、redis集群搭建过吗
1.为什么要搭集群:Redis的数据是存放在内存中的,这就意味着redis不适合存储大数据,因此redis主要用来处理高并发的,一台单独的redis是不能足够支持我们的并发,这就需要我们扩展多台设备协同合作,即用到集群
2.Redis搭建集群的方式:客户端分片、Twemproxy、Codis,我们项目采用无中心结构的redis-cluster集群,集群这块直接说是公司运维搭建
3.具体的搭建架构:我们项目中redis集群主要搭建了6台,3主(为了保证redis的投票机制)3从(高可用)
16、redis缓存机制 rdb和aof的使用场景和优缺点
|
Redis半持久化之RDB
|
Redis全持久化之AOF
|
定义
|
指定的时间间隔内将内存中的数据集快照写入磁盘
|
以日志的形式来记录每个写操作(写指令)
|
使用场景
|
对数据完整性和一致性要求不高更适合使用 适合大规模的数据恢复
|
对数据完整性和一致性要求比较高适合使用
|
优点
|
节省磁盘空间
|
1.备份机制更稳健,丢失数据概率更低
|
|
恢复速度快
|
2.是可读的日志文本,可以处理误操作
|
缺点
|
会丢失最后一次快照后的所有修改
|
更占用磁盘空间、速度慢、存在bug
|
17、你们缓存用的是什么?你们redis有做持久化么?是用什么来做的?
半持久化、全持久化一起使用
19、你们不是用reids来做缓存嘛,如果缓存失效就会查数据库对吧,那你是怎么判断缓存失效的?
在代码层面控制缓存失效
19、redis服务器的的内存是多大?
配置文件中设置redis内存的参数
该参数如果不设置或者设置为0,则redis默认的内存大小为:
32位下默认是3G、64位下不受限制
一般推荐Redis设置内存为最大物理内存的四分之三,也就是0.75
命令行设置config set maxmemory <内存大小,单位字节>,服务器重启失效
config get maxmemory获取当前内存大小
永久则需要设置maxmemory参数,maxmemory是bytes字节类型,注意转换
20、为什么Redis的操作是原子性的,怎么保证原子性的?
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?不一定, 将get和set改成单命令操作,incr
使用Redis的事务,或者使用Redis+Lua==的方式实现原子性
21、你还用过其他的缓存吗?这些缓存有什么区别?都在什么场景下去用?
比较项
|
reids
|
memcache
|
存储方式
|
redis可以持久化其数据
|
数据全部存在内存之中,断电后会挂掉,不能持久化
|
数据支持
|
支持丰富的数据类型
|
仅支持字符串
|
value值大小
|
Redis 最大可以达到 512M
|
value 不能超过 1M 字节
|
速度
|
单线程,阻塞多路IO
|
多线程异步 IO 的方式,可以合理利用 CPU 多核的优势,性能非常优秀
|
数据备份
|
支持主从模式的数据备份,能够提供高可用服务
|
当容量存满时,会剔除的过期 key 进行清理,还会按 LRU 策略对数据进行剔除
|
应用场景
|
适用于读写效率高、业务复杂、安全性高的系统
|
适合多读少写,大数据量的情况
|
22、redis的过期策略以及内存淘汰机制有了解过吗
redis采用的是定期删除+惰性删除策略+内存淘汰机制定期删除:用一个定时器来负责监视key,过期则自动删除惰性删除策略:获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除内存淘汰机制:因为惰性删除会失效,导致内存会越来越高,所以有了内存淘汰机制在redis.conf中有一行配置:maxmemory-policy volatile-lruvolatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰no-enviction(驱逐):禁止驱逐数据,新写入操作会报错ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致
23、说说Redis哈希槽的概念?
Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通 过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽
24、redis有事务吗?
Redis的事务定义:Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。执行过程中,不会被其他客户端发送来的命令请求所打断Multi、Exec、discard1.从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,命令队列中的命令才会依次执行。2.组队的过程中可以通过discard来放弃组队。3.组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。4.执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
25、是否了解过redis的安全机制?
漏洞介绍:redis默认情况下,会绑定在bind 0.0.0.0:6379,这样就会将redis的服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在访问目标服务器的情况下未授权访问redis以及读取redis的数据,攻击者就可以在未授权访问redis的情况下可以利用redis的相关方法,成功在redis服务器上写入公钥,进而可以直接使用私钥进行直接登录目标主机;比如:可以使用FLUSHALL方法,整个redis数据库将被清空解决方案:
(1)禁止一些高危命令。修改redis.conf文件,用来禁止远程修改DB文件地址,比如 rename-command FLUSHALL "" 、rename-command CONFIG"" 、rename-command EVAL “”等;
(2)以低权限运行redis服务。为redis服务创建单独的用户和根目录,并且配置禁止登录;
(3)为redis添加密码验证。修改redis.conf文件,添加requirepass mypassword;
(4)禁止外网访问redis。修改redis.conf文件,添加或修改 bind 127.0.0.1,使得redis服务只在当前主机使用;
(5)做log监控,及时发现攻击;
(6)服务器不安装
26、你对redis的哨兵机制了解多少?(redis2.6以后出现的)
哨兵机制:监控:监控主数据库和从数据库是否正常运行;
提醒:当被监控的某个redis出现问题的时候,哨兵可以通过API向管理员或者其他应用程序发送通知;
自动故障迁移:主数据库出现故障时,可以自动将从数据库转化为主数据库,实现自动切换;具体的配置步骤面试中可以说参考的网上的文档。要注意的是,如果master主服务器设置了密码,记得在哨兵的配置文件(sentinel.conf)里面配置访问密码
七、RabbitMQ
1、RabbitMQ 在你们项目中哪里用到了 * 2
1、新增商品、修改商品时发送消息给search微服务执行索引库同步数据库
3、商品价格修改后发送消息给购物车微服务同步实时价格,用于比价
4、新增商品、修改商品、库存为零时发送消息给商品详情页,商品详情页重新生成静态页面
5、进行短信验证时,MQ异步发短信验证码给用户
6、order微服务订单创建成功后发送消息给购物车微服务删除购物车
7、order微服务如果订单创建失败发消息给wms微服务,wms微服务立马释放库存;发送消息给oms微服务标记订单为无效订单
8、order微服务订单创建成功,发送延时消息,将来定时关单并解锁库存
9、order微服务关单成功发送消息解锁库存
10、wms 微服务锁库存成功,发送延时消息,将来定时解锁库存
12、payment微服务支付成功发送消息给oms,更新订单状态为已支付
12、更新订单状态成功,发送消息给wms减库存、加销量
13、更新订单状态成功,发送消息给ums更新用户积分
2、RabbitMQ 消息中间件和延时队列加死信队列
1.声明延时交换机
2.声明延时队列1.x-message-ttl:指定TTL时间2.x-dead-letter-exchange:死信转发所需的死信交换机(DLX)3.x-dead-letter-routing-key:转发死信时的routingKey(DLK)
3.延时队列绑定到延时交换机
4.声明死信交换机(DLX)
5.声明死信队列(DLQ)
6.死信队列绑定到死信交换机,rontingKey 要包含第2步绑定的 DLK。
3、RabbitMQ 的如何防止消息丢失
生产者确认:
none-不确认 simple-同步确认阻塞 性能不高 correlated-异步监听方法确认
spring.rabbitmq.publisher-confirm-type=none/simple/correlated
spring.rabbitmq.publisher-returns=true # 确认消息是否到达队列RabbitMQ 本身设置持久化消费者确认
none-不确认模式,只要消费者获取到消息,就被确认
auto-自动确认模式,如果消费者程序没有异常,就被确认,如果有异常,会无限重试
manual-手动确认模式,代码确认:channel.basicAck/basicNack/basicReject()
spring.rabbitmq.listener.simple.acknowledge-mode=none/auto/manual
4、RabbitMQ 持久化的类型
1.声明队列必须声明为支持消息持久化的队列
2.声明交换机必须声明为支持消息持久化的交换机
3.发送消息的时候,必须支持为可持久化的消息
5、你们项目的事务用的什么 (我没说seata我说的MQ最终一致性)
MQ最终一致性:1.创建订单时,如果防重、验价、限购、验库存、锁存库都完成了,但是保存的订单的时候如果发生异常,2.此时会发送消息,oms获取消息标记为无效订单;wms获取消息解锁库存
6、如何保证消息不被重复消费?
(1)数据要写库操作,最好先根据主键查一下,如果这数据都有了,就不再执行insert操作,可以update
(2)写入redis,redis的set操作天然幂等性
(3)你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单id之类的东西,然后你这里消费到了之后,先根据这个id去比如redis里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个id写redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。(防止订单重复提交)
7、如何解决消息队列的延时以及过期失效问题?有几百万的消息持续积压几小时,说说如何解决?
场景一:线上故障,消息持续积压
解决方案:临时紧急扩容1.重新部署一个程序,将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据2.等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息场景二:rabbitmq设置过期时间,大量的数据直接丢失
解决方案:批量重导1.在用户活跃度较小的夜晚,写个临时程序,把丢失的数据一点一点的查出来,然后重新灌入mq里面去
8、如果让你写一个消息队列,该如何进行架构设计?说一下思路
(1)首先这个mq得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统 (2)其次你得考虑一下这个mq的数据要不要落地磁盘吧?那肯定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的。 (3)其次你考虑一下你的mq的可用性啊? (4)能不能支持数据0丢失啊?
八、boot、cloud微服务
1、springboot启动流程说一下? * 2
1.Springboot在启动的时候会调用run方法
2.run方法会执行refreshContext()方法刷新容器
3.会在类路径下找到springboot-boot-autoconfigure/springboot-boot-autoconfigure.jar/META-INF/spring-factories文件
4.该文件中记录中众多的自动配置类,容器会根据我们是否引入依赖是否书写配置文件的情况,将满足条件的Bean注入到容器中,于是就实现了springboot的自动装配
2、你们项目和一个单体的Boot项目有啥区别
Spring Boot框架是基于Spring框架的扩展,传统的Spring项目需要一些比较复杂的配置文件。现在Spring Boot不需要像Spring项目那样复杂的进行配置,自动配置在很大的程度上提高了开发效率
1.效率高
2.支持高并发
3.支持大数据量
3、微服务与Boot的区别 * 2
1.SpringBoot专注于方便的开发单个微服务
2.SpringCloud是关注于全局的微服务协调治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来.为各个微服务之间提供配置管理,服务发现,断路器,路由,分布式会话等集成服务.
3.SpringBoot可以离开SpringCloud单独使用,而SpringCloud离不开SpringBoot
4、Boot也可以搭建集群啊
可以
5、微服务治理有哪些组件
网关:Gateway作用:负载均衡 过滤请求 验证令牌(统一鉴权) 全局熔断原理:断言、路由、过滤器总结:经过过滤器链的过滤之后,将满足指定断言规则的请求路由到指定位置负载均衡:Ribbon作用:Spring Cloud Ribbon是一套客户端负载均衡的工具(Ribbon+RestTemplate)远程调用:open Feign作用:就像调用本地方法一样来调用远程方法原理:1,SpringCloud 为每一个 FeignClient 生成一个代理对象2,代理对象会分析类与方法上的注解,就可判断出服务名与请求方法名的路由3,从注册中心获取指定服务名的所有真实地址4,利用负载均衡策略选择一个最佳地址,利用RestTemplate进行调用 5,等待接收返回服务的注册与配置中心:nacos介绍:Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台作用:服务发现和服务健康监测、动态配置服务(nacos = Eureka + Spring Cloud Config)服务的熔断降低限流:sentinel(替代Hystrix)作用:流量控制、熔断降级、系统负载保护微服务链路追踪:sleuth+zipkin作用:1,提供链路追踪。通过sleuth可以很清楚的看出一个请求都经过了哪些服务;可以很方便的理清服务间的调用关系。2,可视化错误。对于程序未捕捉的异常,可以结合zipkin分析。3,分析耗时。通过sleuth可以很方便的看出每个采样请求的耗时,分析出哪些服务调用比较耗时。当服务调用的耗时随着请求量的增大而增大时,也可以对服务的扩容提供一定的提醒作用。4,优化链路。对于调用频繁的服务,可以并行调用或针对业务做一些优化措施等。
6、springboot你们项目是怎么使用的?用了多久?
7、springboot哪些注解会生成Bean?
九、MySQL
1、数据库隔离级别?* 2
隔离级别
|
不可重复读
|
脏读
|
幻读
|
读未提交
|
√
|
√
|
√
|
读已提交
|
×
|
√
|
√
|
可重复读
|
×
|
×
|
√
|
可串行化
|
×
|
×
|
×
|
2、数据库用的是mysql吗?
是
3、你们使用Mysql存放数据的原因
开源免费/跨平台/使用方便/占用内存小
4、Mysql的优化 * 5
1.观察服务器运行状态是否存在周期性波动
2.如果存在周期性波动,加缓存更改缓存失效策略
3.如果不存在周期性波动,或者加缓存后仍然有延迟或者卡顿,就开启慢查询,使用 explain 查看 SQL 的执行细节
4.如果 SQL 的等待时间长,就调优服务器参数
5.如果 SQL 的执行时间长,就优化索引、优化多表 join,优化数据库表
6.如果不是 SQL 的问题,说明 SQL 到达瓶颈,此时需要进行读写分离优化、分库分表优化
5、where和having的区别
where
|
having
|
where 是一个约束声明,用来约束数据库的数据
|
having 是一个过滤声明,对查询结果进行过滤
|
where 用在返回结果集之前(group by)
|
having 用在查询结果集之后
|
where 中不能使用聚合函数
|
having 后可以使用聚合函数
|
6、sql语句的执行顺序
1.首先去缓存查询,如果查到则返回结果
2.如果缓存没查到,则会进入解析器,进行语法解析、语义解析、生成语法树
3.然后进入查询优化器进行索引优化、多表join优化等,生成执行计划
4.最后,执行器真正的负责了MySQL中数据的存储和提取,执行器执行完毕后会返回结果,并把结果存入缓存
7、聚簇索引和非聚簇索引的区别?
聚簇索引
|
非聚簇索引
|
索引的字段一般主键ID,而且要自增
|
索引的字段不是主键ID
|
索引即数据,不需要进行回表操作
|
需要进行回表操作
|
不用主动定义,默认就有,且只有一个
|
需要主动定义,一张表可以有多个非聚簇索引
|
排序和范围查找速度快
|
增删改效率较高
|
8、数据库没有的字段,你设计类的时候怎么处理的
@TableField(exist=false)
9、@tableLogic注解是什么?
@TableLogic:表示逻辑删除注解,在字段上加上这个注解再执行BaseMapper的删除方法时,删除方法就会变成修改
10、索引优化type等级是哪几个?你对索引优化有什么了解?
11、MySQL索引失效的情况和解决办法 * 2
索引失效的情况:1.全值匹配,要查找的所有字段必须是联合索引的所有字段2.最佳左前缀原则,检索数据时从联合索引的最左边,一旦跳过某个字段,索引后面的字段都无法被使用3.主键插入顺序,如果主键不自增的话,会造成页面分裂,损耗性能4.计算、函数、类型转换会导致索引失效5.不等于(!= 或者<>)会导致索引失效6.范围条件右边的列会导致索引失效7.is not null无法使用索引,is null可以使用索引8.like以通配符%开头会导致索引失效9.OR 前后存在非索引的列,索引失效 ,只要有条件列没有进行索引,就会进行 全表扫描
解决办法:1.在where中使用不到的字段,不要设置索引2.数据量小的表最好不要使用索引3.有大量重复数据的列上不要建立索引4.避免对经常更新的表进行过多的索引5.不建议用无序的值作为索引6.删除不再使用或者很少使用的索引7.不要定义冗余或重复的索引8.限制索引的数目
12、复合索引 如何考虑怎么创建
13、mysql 的引擎 InnoDB 和 MyISAM 的主要区别
InnoDB
|
MyISAM
|
支持事务、分布式事务
|
不支持
|
支持行锁
|
支持表锁
|
支持外键
|
不支持外键
|
表结构:.frm/.idb
|
表结构:.frm/.myd/.myi
|
索引即数据
|
索引和数据是分开的
|
5.5之后的默认引擎
|
5.5之前的默认引擎
|
读写效率较差、占用空间大
|
读写效率高、占用空间小
|
14、MySQL的视图了解吗?使用视图会影响性能吗
1.视图是将一段查询 SQL 封装为一个虚拟的表。 这个虚拟表只保存了 SQL 逻辑,不会保存任何查询结果。
2.封装复杂sql语句,提高复用性
3.逻辑放在数据库上面,更新不需要发布程序,面对频繁的需求变更更灵活
4.使用视图会大大提升性能
15、有没有设计过数据表?你是如何设计的?
第一范式(1NF)
|
要求数据库表的每一列都是不可分割的原子数据项
|
第二范式(2NF)
|
需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)
|
第三范式(3NF)
|
需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关(在2NF基础上消除传递依赖)
|
16、B+Tree与B-Tree区别
1.B+Ttree根节点和枝节点没有存储行数据的地址值,每次加载时,一leaf能够存储更多的索引节点。
2.B+Ttree叶子节点有指向相邻叶子节点的双向指针,便于范围查询。B+Tree:
明确概念:目录项页、数据项页;叶子节点、内节点、根节点
根节点位置自始至终不变,只有一个;目录项页之间构成双向链表、目录项存储索引对应的字段+指向的下层页的地址、目录项之间构成单向链表;数据项页之间构成双向链表、数据项存(聚簇索引:表中的一条记录;二级索引:索引的字段+主键值)、数据项之间构成单项链表;
十、JUC
1、多线程synchronized和Lock区别?
synchronized
|
Lock
|
synchronized是 Java 的关键字,通过修饰代码块或者方法实现同步
|
Lock 是一个类,通过调用这个类的 API 实现同步访问
|
synchronized不需要手动释放锁,系统会自动释放对锁的占用
|
Lock 必须手动的释放锁,否则可能会出现死锁的现象
|
synchronized 是阻塞同步
|
Lock 底层机制 AQS(volatile + CAS)是非阻塞同步
|
独占排他
|
独占排他
|
不支持共享锁、公平锁
|
支持共享锁、公平锁
|
不支持响应中断
|
支持响应中断
|
2、synchronized和volatile的区别
3、线程池参数? * 3
corePoolSize
|
线程池的核心线程数
|
maximuPoolSize
|
线程池能容纳的最大线程数
|
keepAliveTime
|
空闲线程的存活时间
|
unit
|
存活时间的单位
|
workQueue
|
存放提交但未执行任务的队列
|
hreadFactory
|
创建线程的工厂
|
handler
|
等待队列满后拒绝策略
|
4、threadlocal怎么传递参数的?
5、readlock的弊端呢?
只能读不能写
6、线程池的创建方式
1.继承 Thread 类:继承 Thread 类、重写 Thread类的 run() 方法、创建 Thread 类的子类对象、调用 start 方法
2.实现Runnable接口:创建一个实现Runnable接口的类、实现Runnable接口中的抽象方法run()、创建实现类的对象,作为参数传递到Thread类的构造器中、创建Thread类的子类对象,调用start方法
3.实现Callable接口
创建一个实现Callable接口的实现类、实现Callable接口的call()方法、创建Callable接口的实现类的对象,作为参数传递到FutureTask构造器中,创建FutureTask类的对象,作为参数传递给Thread、创建Thread类的子类对象,调用start方法
4.使用线程池(常用、优点)
提高响应速度(减少了创建新线程的时间)、降低资源消耗(重复利用线程池中线程,不需要每次都创建)、便于线程管理
7、线程池的4个拒绝策略?
1.AbortPolicy(默认):eg:商品详情/购物车/首页
当线程数大于maximumPoolSize+workQueue 数时,直接抛出RejectedExecutionException异常阻止系统正常运行。
2.CallerRunsPolicy:
“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。当线程数大于maximumPoolSize+workQueue 数时,谁调用的线程就返回给谁。
3.DiscardOldestPolicy:
抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
4.DiscardPolicy:
直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。Eg:单纯的展示某一项数据的情况 文章的浏览量/点赞个数
8、线程池队列你了解哪些
ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表结构组成的有界阻塞队列(大小默认值为integer.MAX_VALUE)
DelayQueue:使用优先级队列实现的延迟无界阻塞队列,只有当其指定的延迟时间到了,才能够从队列中获取到该元素(阻塞消费者)
PriorityBlockingQueue:支持优先级排序的无界阻塞队列,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
LinkedTransferQueue:由链表组成的无界阻塞队列。
LinkedBlockingDeque:由链表组成的双向阻塞队列。
9、项目核心线程数你们配置的多少? * 2
CPU 密集型:CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。CPU密集型任务配置尽可能少的线程数量:一般公式:CPU核数+1个线程的线程池IO 密集型:由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数*2,I0密集型,即该任务需要大量的I0,即大量的阻塞。在单线程上运行I0密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。I0密集型时,cpu核数 / (1-0.9),核数为4的话,一般设置 40
10、如何查看死锁
1.可以通过jstack命令来进行查看,jstack/jvisualvm命令中会显示发生了死锁的线程
2,或者两个线程去操作数据库时,数据库发生了死锁,这是可以查询数据库的死锁情况1、查询是否锁表
show OPEN TABLES wtere In_use 》e;
2、查询进程
show processlist;
3、查看正在锁的事务
SELECT * EROM INFORMATION_SCHEMA.INNODB_LOCKS;
4、查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
11、java死锁如何避免
造成死锁的几个原因:
1.一个资源每次只能被一个线程使用
2.一个线程在阻塞等待某个资源时,不释放已占有资源
3.一个线程已经获得的资源,在未使用完之前,不能被强行剥夺
4.若干线程形成头尾相接的循环等待资源关系这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满足其中某一个条件即可。而其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。在开发过程中:
1.要注意加锁顺序,保证每个线程按同样的顺序进行加锁
2.要注意加锁时限,可以针对锁设置一个超时时间
3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决
12、为什么要使用线程池
主要特点:线程复用;控制最大并发数:管理线程。
第一:降低资源消耗。通过重复利用己创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进 行统一的分配,调优和监控
13、线程池底层工作原理
1.在创建了线程池后,线程池中的线程数为零。
2.当调用 execute() 方法添加一个请求任务时, 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务。
3.如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
4.如果这个时候队列满了且正在运行的线程数量还小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务。
5.如果队列满了且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
6.当一个线程完成任务时,它会从队列中取下一个任务来执行。
7.当一个线程无事可做超过一定的时间(keepAliveTime)时,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。
8.线程池的所有任务完成后,线程数最终会收缩到 corePoolSize 的大小。
十一、JVM
1、oom 异常的原因和解决方法
异常的原因(OOM):1.堆溢出异常:针对年轻代、老年代整体 Full GC 后,内存空间还是放不下新产生的对象 2.元空间异常:方法区中加载的类太多了(典型情况是框架创建的动态类太多,导致方法区溢出)3.栈溢出异常:申请栈空间不足时
解决方法(JVM调优):1.堆空间和元空间调优:堆空间和元空间的参数设置2.GC调优:优先使用 G1 垃圾回收器3.代码调优:尽量用动态字符串(StringBuffer)、不用的大对象及时释放(流、connection)、少使用 try...catch 、少嵌套 for循环4.硬件调优:加内存条、选择合适的磁盘、调优服务器性能
2、什么情况下会导致栈溢出异常
栈溢出异常:方法每调用一次都会在栈空间申请一个栈帧,用来保存本次方法执行所用的数据,但是一个没有退出机制的递归调用,会不断的申请栈空间,而又不释放空间,这样就会耗尽栈空间,导致栈溢出异常
3、你们项目用的GC算法是什么?
G1垃圾回收器
4、jvm结构有哪些?
1.功能区(绿色)
类加载器:文件系统加载 class文件
垃圾回收器:对方法区,Java堆,直接内存进行垃圾回收
字节码执行引擎:将 JVM 可识别的字节码转换为操作系统可识别的机器码
2.线程私有区(蓝色)
Java栈:创建线程的时候栈被创建
本地方法栈:用于本地方法调用
PC寄存器:指向 JVM 下一条执行指令的地址
3.线程共享区(橙色)
方法区:存放类的方法代码、变量名、方法名、访问权限、返回值等
堆:用于存放实例对象
直接内存:指的是直接去内存条申请内存,而不是在JVM内存中申请,一般用于 NIO
5、jvm怎么保证出栈顺序
6、jvm垃圾回收判定
1.引用计数法
2.可达性分析算法
7、jvm堆中老年区保存的都是什么对象?
保存的主要是生命周期很长的对象,例如:IOC容器对象、线程池对象、数据库连接池对象等等
十二、Java高级框架
3、dubbo的工作原理
Apache Dubbo是一款高性能的Java RPC框架,可以和Spring框架无缝集成。RPC并不是一个具体的技术,而是指整个网络远程调用过程。
工作流程:
1.服务容器启动,加载,运行
2.服务提供者在启动时,向注册中心注册自己提供的服务
3.服务消费者在启动时,向注册中心订阅自己所需的服务
4.注册中心会把服务提供者的地址列表给消费者
5.消费者会基于负载均衡算法选择一台提供者调用
6.服务提供者和消费者,会定时的把调用次数和调用时间发送给监控中心进行数据统计
4、Zookeeper了解吗?
Zookeeper 是 Dubbo官方推荐使用的服务注册中心
5、zookeeper的工作原理
6、Seata的实现原理
Seata 的三个组件:
1.TC:事务协调器,负责维护全局事务的运行状态,协调并驱动全局事务的提交和回滚
2.TM:事务管理器,负责开启一个全局事务,并最终发起全局事务的提交或回滚的决议
3.RM:资源管理器,负责接收事务协调器的指令,并驱动分支事务的提交和回滚
Seata的实现原理:
1. TM 向 TC 申请开启一个全局事务,全局事务创建成功后生成一个全局唯一的 XID
2. XID 会在微服务调用链路的上下文中传播
3. RM 向 TC 申请注册一个分支事务,并将其纳入到 XID 对应的全局事务的管辖
4. TM 向 TC 发起针对 XID 的全局提交或回滚的决议
5. TC 会调度 XID 管辖下的全部分支事务完成提交或回滚
7、seata核心注解?
@GlobalTransactional
8、介绍下SpringSecurity你们在项目中的使用
Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证和授权的过程RBAC模型(基于角色的访问控制)就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系认证过程:只需要用户表就可以了,在用户登录时可以查询用户表t_user进行校验,判断用户输入的用户名和密码是否正确授权过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色t_role,再根据角色查询对应的权限t_permission以及资源
十三、Java高级技术
1、CAP解释一下
Consistency(一致性):在分布式系统中,在同一时刻数据是否有同样的值
Avalibility(可用性):集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
Partition tolerance(分区容错性):分布式系统大多分布在多个子网络中,每个子网络就相当于一个区,分区容错性就是区间通信可能会失败
由于网络会出现延迟丢包等问题,所以分区容错性是无法避免的,所以分布式框架中一般选择满足CP或者AP
2、你都熟悉那些设计模式
工厂模式
|
Spring 通过 BeanFactory 或者 ApplicationContext 创建 Bean 对象的时候用到了工厂模式
|
单例模式
|
Spring 中 bean 的作用域默认为是单例模式的(singleton)
|
代理模式
|
Spring AOP 基于动态代理,如果要代理的对象实现了某个接口,
SpringAOP 会使用 JDK 的动态代理创建代理对象;
如果对象没有实现接口,Spring AOP会使用 CGLib 生成一个被代理对象的子类作为代理对象
|
模板方法模式
|
Spring 中的 jdbcTemplate 等以 Template 结尾的对数据库操作的类就使用了模板方法模式
|
适配器模式
|
SpringAOP 的增强(Advice)使用了适配器模式、SpringMVC的 HandlerAdapter 也是适配器模式
|
观察者模式
|
定义的对象有一对多的依赖关系,当一个对象的状态发生改变时,
所有依赖于它的对象都会得到通知被自动更新,Spring事件驱动模型就是观察者模式一个很经典的应用
|
3、手写单例模式
// 饿汉式
public class Singleton01{private Singleton01(){};final static Singleton01 singleton01 = new Singleton01();public static Singleton01 getInstance(){return songleton01;}
}// 懒汉式
public class Singleton02{private Singleton02(){};final static Singleton02 singleton02 = null;public synchronized static Singleton02 getInstance(){if(singleton02 == null){singleton02 = new Singleton02();}return singleton02;}
}
4、手写代理模式
// Person 类
public interface Person{public void study();
}// 被代理类
public class Zhangsan implements Person{@Overridepublic void study(){System.out.println("自学java"); }
}// 代理工厂类
public class JdkProxyFactory(){static Zhangsan zhangsan = new Zhangsan();public static void main (String[] args){Person proxyInstance = (Person) proxy.newProxyInstance(zhangsan.getClass().getClassLoader(),zhangsan.getClass().getInterface(),new InvocationHandler{@Overridepublic Object invoke(Object proxy,Method method,Object[] args){System.out.println("增强前");Object invoke = method.invoke(zhangsan,args);System.out.println("增强后"); }});proxyInstance.study();}
}
5、ik分词器怎么配置的?
main.dic:单词词典
stopword.dic: 停用词,这里只记录了英文的一部分单词,比如: a、an、and、are、as、at、be、but、by等
6、Linux我要查找日志但是这个日志很大我怎么查看日志最后几行的信息或者前面的信息 * 2
netstat -anp | less # 分屏查看
cat test.log | tail -n 20 # 查看test.log倒数20行
cat test.log | head -n 20 # 查看test.log前20行
tail -n 5 word # 查看文件末尾5行的内容
7、Linux的tail命令了解吗 还有top
tail:1.使用 -F 参数实时查看文件末尾新增的内容2.使用 -n 参数可以查看文件末尾的内容
top:1.实时查看系统运行情况和健康状态2.参数 -d ,间隔秒数3.参数 -i ,不显示任何闲置或者僵死进程 4.参数 -p 进程id ,通过进程id监控单一进程
8、手写一个冒泡排序
public class BubbleSort {public static void main(String[] args) {int[] arr = {6,5,3,1,8,7,2,4};for(int i = 0;i < arr.length - 1;i++){for (int j = 0;j < arr.length - 1 - i;j++){if(arr[j] > arr[j + 1]){int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}for (int i = 0;i < arr.length;i++){System.out.println(arr[i] + " ");}}
}
9、手写二分查找
public class BinarySearch{main{int[] arr = {5,8,9,10,12,20,31,45,78,80};dest = 20;int low = 0;int end = arr.length - 1;Boolean flag = true;while(low <= end){int mid = (low + end) >> 1;if(dest = arr[mid]){sout("找到了,下标为:" + mid);flag = false;break;}if(dest < arr[mid]){end = mid - 1;}eles{low = mid + 1;}}if(flag){sout("未找到");}}
}
十四、项目
1、项目中你主要负责哪些模块
主要负责购首页模块、商品详情页模块、购物车模块、订单模块、单点登录模块(后台商品模块、搜索模块、支付秒杀模块不负责)
2、首页模块
(知识点:缓存、分布式锁、布隆过滤器、写的一致性问题、读并发问题、AOP的使用)1.首页模块主要是商品分类列表展示,进入首页界面,默认只会展示一级分类,然后鼠标移动到一级分类列表上就会展示二、三级分类列表。 2.为了加快页面的响应速度和减轻数据库的压力,这里会把二、三级分类列表缓存到 Redis 中,当下次再查询的时候会先查询缓存,缓存命中则直接返回结果,否则再查询数据库,更新缓存。3.使用了缓存功能后,又会引发一些问题,比如写的一致性问题,使用双删解决,具体见 Redis 系列;还有读并发问题,为解决缓存击穿问题引出分布式锁、布隆过滤器一系列问题,分布式锁实现方案 Redis 原生实现或者 redisson 框架实现,Redis 原生实现见 Redis 系列。4.redisson 框架实现分布式锁步骤 1.判断缓存中是否有数据,如果命中就直接返回数据,未命中就获取锁,上锁2.由于获取锁和上锁不具有原子性,所以上锁之后需要在判断缓存中是否有数据,如果命中就直接返回数据3.否则就查询数据库并写入缓存4.为解决缓存穿透,即使为null值也缓存或者集成布隆过滤器5.为解决缓存雪崩,给缓存时间添加随机值6.释放锁
3、商品详情页模块
(知识点:异步编排、线程池、页面静态化、远程接口调用)1.需要的数据:品牌、分类、spu描述信息、spu的名称、spu的规则分组和规格参数;sku的基本信息、图片、促销信息、库存、销售属性;skuId及销售属性的关系;销售属性组合与skuId映射关系2.由于商品详情页的逻辑非常复杂,需要的大量的数据,有的甚至需要远程调用十几个接口,如果同步调用,按顺序执行的话,假设调用每个 接口花费0.5s,至少也要5、6s的时间,这样用户的体验会很差,所以为了提高响应速度,这里使用了异步编排的思想来实现,具体的实现是使用了jdk8新增的 CompletableFuture 这个类。3.具体的实现步骤:创建异步任务,提交给线程池执行,由线程池负责调用执行,编排所有异步任务完成执行完之后调用 allOf 方法完成。4.即使采用了异步编排,但是由于商品详情页会展示图片,而加载一些高清图片也比较耗时,又由于商品详情页是、读多写少的场景,所有又使用了页面静态化对商品详情页进行优化,模板引擎使用了 thymeleaf,因为官方推荐的。5.具体使用步骤:当打开一个商品详情页的时候,会异步创建一个 HTML 页面在共享硬盘上,Nginx 可以直接访问到这个共享硬盘,当下次在访问商品详情页的时候,会先访问静态页面,命中则直接返回,否则在查询数据库,更新静态页面。6.这个操作类似于缓存,为什么不使用缓存?1.比缓存快2.数据量较大,缓存是基于内存的,不适合使用缓存
4、单点登录模块
(知识点:jwt、cookie、网关的统一鉴权)一、Auth微服务实现登录1.根据用户提交的信息去数据库查询用户信息,如果获取不到用户信息的话就提示用户不存在或者账号密码不正确2.获取到用户信息后,把用户ID、用户名、IP存入载荷生成JWT token,然后把 token 存入 cookie 中3.把昵称也存入 cookie 中,之后重定向到登录前的页面二、网关微服务实现拦截鉴权1.用户发送请求时,首先获取用户的请求路径,判断是否在拦截名单中,不在则放行2.在拦截名单则拦截,获取请求中的 token,先从请求头中获取,为空再从 cookie 中获取,还为空则重定向到登录页面3.如果 token 不为空,则使用公钥解析 token,解析失败则重定向到登录页面4.如果解析成功,则校验 token 中的 IP 地址,判断是否盗用,如果盗用则重定向到登录页面5.如果都验证通过,就将用户信息传递给后续服务,放行请求
5、购物车模块
(知识点:Redis缓存、 Spring Task的异步功能、异步异常处理器、MQ、xxl-job、ThreadLocal)一、技术选型1.项目中采用 Redis + MySQL 来实现购物车功能2.查询时,从 Redis 查询提高性能;写入时,采用双写模式,异步写入 MySQL 中的数据用于数据统计,对可靠性要求不严格,为防止数据 库数据偏差较大,异步写入 MySQL 发生异常的异常信息(userId)会被缓存到 Redis,后续通过 xxl-job 定时任务进行数据同步3.Redis 的数据结构采用 hash 结构,Map<UserId/userKey,Map<skuId, cartJson>>二、校验用户状态流程由于支持离线购物车功能,所以对购物车的增删改查分为登录状态和未登录状态两种情况,项目中直接使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程的局部变量传递给后续服务,具体流程如下:1.获取登录头信息中的 userKey ,如果为空就随机制造一个 userKey 放入 cookie 中并存入 UserInfo 中2.获取用户的登录信息 token ,解析 token 中 的 jwt 将 userId 也存入 userInfo 中3.把 userInfo 放入 ThreadLocal 中传递给后续服务(userInfo 中一定有 userKey ,不一定有 userId)三、添加购物车流程1.获取用户的登录信息,登录取 userId ,未登录取 userKey2.Map<UserId/userKey,Map<skuId, cartJson>> 根据外层的key获取内层的 map 结构(购物车)3.判断购物车里是否包含该商品4.如果包含则更新商品数量5.如果不包含则新增商品6.缓存实时价格到 Redis ,后续用于比价7.将购物车信息同步缓存到 Redis,异步写入 MySQL四、查询购物车流程1.先查询未登录状态的购物车,查询时要查询实时价格缓存2.判断是否登录,未登录则直接返回3.已登录则合并购物车1.获取登录状态的购物车2.判断是否存在未登录状态的购物车,有则遍历合并1.判断登录购物车是否存在该商品,有更新,没有则新增4.合并完成之后要删除未登录的购物车5.查询登录状态所有的购物车信息,返回购物车列表,查询时要查询实时价格缓存五、购物车如何比价1.设计购物车字段时 加入一个 实时价格字段1.添加购物车时会有一个加入购物车时的价格和一个实时价格,实时价格会被缓存到 Redis2.当后续价格发生改变时,通过 MQ 异步通知 cart 微服务进行价格同步,会重新设置 Redis 中的实时价格3.计算加入购物车时的价格和 Redis 中缓存的实时价格 就可以得到价格浮动
6、订单模块
知识点:异步编排、线程池、MQ、redisson分布式锁、雪花算法、ThreadLocal一、配置网关过滤器和拦截器:因为订单相关操作需要登录状态下才可以完成,所以使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程变量传递给后续服务,通过网关的局部过滤器,拦截未登录状态下提交订单跳转到登录页面二、订单确认页:1.需要的数据:收货人信息、地址、支付方式、送货清单(包含购物车中的商品信息)、优惠信息2.由于存在大量的远程调用,所以使用异步编排做优化,类似于商品详情页3.生成订单确认页的时候要生成防重的唯一标识(ID生成工具,封装了雪花算法),缓存在 Redis 中,防止重复下单三、提交订单:1.验证 Redis 中防重的唯一标识,由于验证完要立即删除,为保证原子性,使用 luo 脚本实现2.验总价:页面总价格 和 数据库实时总价3.验库存并锁库存(为保证原子性,防止超卖,使用redisson分布式锁),锁库时发送延时消息,将来定时解锁库存4.创建订单1.订单创建成功,发送延时消息,将来定时关单并解锁库存2.订单创建异常,发送消息回滚,oms获取消息标记为无效订单,wms获取消息解锁库存(MQ的最终一致性)3.订单创建成功,MQ 异步删除购物车中对应的商品
有用户表嘛?有哪些字段?
用户ID、用户名、密码、盐、昵称、手机号、邮箱、头像、性别、生日、职业、个性签名、用户等级、用户来源、购物积分、状态、注册时间
你们项目组的架构,你们来一个新的需求,从哪入手
项目做了多久?
当你的想法于项目当前的项目不吻合,你怎么办 * 2
你们公司的架构
你们小组的架构
7、与实际开发相关的问题
1.生产环境服务器数量:66
Nginx2台(主备);Nacos 3台(官方推荐最少3台);项目中有服务数量*2(19*2=38)共需要38台;redis无中心化集群6台(3主3从),如果是主从哨兵集群5台;mysql数据库(读写分离的情况下,1主2从 4*3 =12)共12台;ES集群3台;rabbitmq集群2台2.你们项目的并发量是多少:600-10003.你们项目的商品表有多少条:10W件商品4.你们项目有多少用户:20W5.你们项目的订单数有多少:8W6.技术部10-12人开发5人前端2人测试3人运维2人
十五、其他
自我介绍 * n
讲一下前几份工作时间和地点
你还有什么想问我的
2022最新面试题-更新中相关推荐
- 计网/数据库面试题(更新中ing~~)
计网/数据库面试题(更新中ing~~) <计算机网络> 1. OSI七层模型.设备 (传输层)协议的多路分用和复用 2. TCP/IP四层模型==五层模型 (传输层)TCP和UDP协议对比 ...
- 2022最新面试题(含css、html、js、es6、Vue)
2022.4.22 这段时间准备换工作,陆陆续续面了一下外包和正经公司,总结一下遇到的面试问题和答案.持续更新. 本文只记录重点答案,每条下面会贴一个详细讲解这个问题的链接,想要详细了解问题答案及原理 ...
- (2022最新面试题)JAVA基础面试题
整理了一些比较基础的面试题,一方面是为了自己以后时常复习,同时也是为了一些准备面试的开发小伙伴所准备的,如一些方面不完全不吝赐教,希望大家都能找到满意工作. 目录 一.JAVA基础 1.JRE和JDK ...
- html中给div设置的属性怎么样才能拿得到_前端基础高频面试题(更新中)
页面渲染的全过程 输入url后,先拿到html文件,html下载完以后会开始对它进行解析 html在解析的过程中,如果文本里有外部资源链接,比如css.js和img时,会立即启用其他线程下载这些静态资 ...
- [转载] 常用应届生Java开发笔试面试题(更新中)
参考链接: Java中的循环的重要事项 Java开发面试题 Java基础篇Java8大基本数据类型Java的三大特性面向对象如果让你推销一款Java产品,你会怎么推销呢?(java的特点)JVM与字节 ...
- 前端提示框定位在鼠标的右下_前端基础高频面试题(更新中)
页面渲染的全过程 输入url后,先拿到html文件,html下载完以后会开始对它进行解析 html在解析的过程中,如果文本里有外部资源链接,比如css.js和img时,会立即启用其他线程下载这些静态资 ...
- Redis【2022最新面试题】
文章目录 概述 什么是Redis Redis有哪些优缺点 为什么要用 Redis /为什么要用缓存 为什么要用 Redis 而不用 map/guava 做缓存? Redis为什么这么快 数据类型 Re ...
- 2022最新面试题JavaScript、Vue
JavaScript typeof 和 instanceof的区别 typeof 会返回一个变量的基本类型,instanceof返回的是一个布尔值 instanceof 可以准确的判断复杂引用数据类型 ...
- 青龙面板拉库命令大全最新【实时更新中......】
脚本目录 前言
最新文章
- android 中的常用组件
- RxJava2 源码解析(一)
- 这Hadoop分布式文件系统
- Spring Cloud异常
- UIKit框架各个类的简介
- 通过Dapr实现一个简单的基于.net的微服务电商系统(十七)——服务保护之动态配置与热重载...
- Leetcode之仅仅反转字母
- Flex 学习随笔 ---- 使用WebService 与数据库连接
- oracle wmsys.wm_concat函数
- SGD和带momentum的SGD算法
- 深度探索C++对象模型复习和学习 第三章 Data 语义学(The Semantics of Data )
- Windows平台上使用Qt(MinGW)调用基于VS编写的周立功CAN卡Dll文件
- [Python]使用QRCode生成彩色二维码
- 自然数 素数 质数_俄罗斯娃娃素数
- python alipay接口文档参考注解
- 高德导航里的时间是怎么计算的?
- STM32CubeIDE开发(三), stm32应用开发过程涉及的术语简称表
- android百度车载导航,百度CarLife车机端
- NLP【05】pytorch实现glove词向量(附代码详解)
- 只问耕耘,不问收获,其实收获却在耕耘中
热门文章
- 使用python下载wallpaper Engine订阅的壁纸/视频
- kafka消息队列应用总结
- 原来iPhone手机这么好用!点2下屏幕就能长截屏,实用又方便
- 英文期刊催稿信模板_SCI投稿委婉催稿信模板
- 干货!适应环境变化的公平意识在线元学习
- kill -9 PID无法杀死一个进程
- Word排版-表格跨页续表
- 基于Mybatis插件方式实现数据脱敏处理
- samba 找不到网络路径 的解决办法.
- SAFNet 基于相似性感知的三维语义分割融合网络