1 微服务Feign[/feɪn/]的底层调用原理
Feign是⼀个轻量级RESTful的HTTP服务客户端(⽤它来发起请求,
远程调⽤的) ,底层依赖于Java的动态代理机制,对原生Java Socket或者Apache HttpClient进行封装,实现了基于Http协议的远程过程调用。
@EnableFeignClients注解是开启Fiegn功能的关键,我们通常会在该注解中添加FeignClient的所在包,以便Spring容器能够扫描到所有的FeignClient,并进行托管。后面可以使用@Autowired注解自动注入,但是注入的是一个代理对象,然后通过代理对象调用方法,方法执行前后可以加入一些增强逻辑,比如调用负载均衡策略完成 server 的选择,发起http请求。
2 Mybatis 二级缓存有了解吗
一级缓存默认开始,二级缓存手动开启。
⼆级缓存的原理和⼀级缓存原理⼀样,第⼀次查询,会将数据放⼊缓存中,然后第⼆次查询则会直接去 缓存中取。但是⼀级缓存是基于sqlSession的,⽽⼆级缓存是基于mapper⽂件的namespace的,也 就是说多个sqlSession可以共享⼀个mapper中的⼆级缓存区域,并且如果两个mapper的namespace 相同,即使是两个mapper,那么这两个mapper中执⾏sql查询到的数据也将存在相同的⼆级缓存区域中。
3 rabbitMQ,Kafka,Rocket MQ的区别,你们的项目为什么选了 Kafka
我觉得 Kafka 相比其他消息队列主要的优势如下:
- 极致的性能 :基于 Scala 和 Java 语言开发,设计中大量使用了批量处理和异步的思想,
最高可以每秒处理千万级别的消息。 - 生态系统兼容性无可匹敌 : Kafka 与周边生态系统的兼容性是最好的没有之一,尤其在大
数据和流计算领域
4 怎么优化慢查询,各种优化手段发现sql还是很慢,怎么处理
在 MySQL 中,会引发性能问题的慢查询,大体有以下三种可能:
- 索引没有设计好,需要通过紧急创建索引来解决
- SQL 语句没写好导致没有使用上索引,可以通过改写索引来处理
- MySQL 选错了索引,应急方案就是给这个语句加上 force index
依旧很慢,会不会是表数据已经达到极限了,需要分表分库?
5 Mysql 的索引,索引失效的场景,主键为什么要设置自动递增?
性能层面
插入新记录的时候可以不指定 ID 的值,系统会获取当前 ID 最大值加 1 作为下一条记录的 ID 值。即每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。
而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。在分布式系统里,不采用 UUID 作为分布式 ID,也是由于 UUID 是无序的,会引起索引节点的频繁变动,影响性能。
存储层面
假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?
由于每个非主键索引的叶子节点上都是主键的值。如果用身份证号做主键,那么每个二级索引的叶子节点占用约 20 个字节,而如果用整型做主键,则只要 4 个字节,如果是长整型(bigint)则是 8 个字节。显然,主键长度越小,辅助索引的叶子节点就越小,辅助索引占用的空间也就越小。
6 高并发除了加锁还有什么解决方案
- 分布式锁
- 采用MQ异步处理,达到削峰效果
- 配置限流,用的是阿里的 sentinel[/ˈsentɪnl/] (哨兵)
-
页面静态化
image.png
7 Kafka持久化内存满了除了迁移还能怎么办?Kafka具体业务的使用场景。
每个分区各⾃存在⼀个记录消息数据的⽇志⽂件。
数据清理
- 日志删除
- 日志压缩
8 Redis 为什么这么快?Redis 过期key的删除策略?Redis 的淘汰策略?I/O多路复用的epoll机制原理?Redis具体业务的使用场景。
Redis为什么用单线程
Redis 单线程是指它对网络 IO 和数据读写的操作采用了一个线程,而采用单线程的一个核心原因是避免多线程开发的并发控制问题。
但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
Redis 为什么这么快
- Redis 的大部分操作在内存上完成,再加上它采用了高效的数据结构,例如哈希表和跳表
- Redis 采用了多路复用机制,使其在网络 IO 操作中能并发处理大量的客户端请求,实现高吞吐率
Redis 过期key的删除策略
- 定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时 器在键的过期时
间来临时,立即执行对键的删除操作。 - 惰性删除:但是每次查到key都检查是否过期,过期,就删除该键;没有过期,就返回该键。
- 定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
Redis 的淘汰策略
I/O多路复用的epoll机制原理
在 Redis 只运行单线程的情况下,Linux的多路复用机制允许内核中可以同时存在多个监听 Socket 和已连接 Socket,一旦有请求到达,就会触发相应的事件,这些事件会被放进一个事件队列,然后通知 Redis 单线程对事件队列的事件进行处理。
Redis具体业务的使用场景
- String:存储图片id
- List:适用于展示最新评论列表、排行榜等场景
- Set:统计手机 App 每天的新增用户数和第二天的留存用户数,交集,并集计算
- Sorted Set:可以按分值排序,适用于各种排行榜,如:点击排行榜、销量排行榜、关注排行榜等
- Hash:适用于对象的存储,常用于购物车
- bitmap:点赞,签到,打卡
- 分布式锁
-
计算器,统计验证码操作次数
image.png
9 多线程的使用场景,具体的实战经验
10 es的倒排索引原理,具体使用场景,用的分词器是什么,数据怎么从db同步到es
具体使用场景
存储商品数据,用户数据,订单数据,用于商家端搜索,对实时性要求不那么高的场景。
es的倒排索引原理
当我们在搜索框输入商品名称,点击查询。es的分词器会对商品名称进行分词,形成一个或多个term。然后根据 term 去查询内存中的 term index(一棵前缀树),通过 term index 可以定位到 term 在 term dictionary 中的位置,找到对应的倒排列表(存的文档id,地址偏移量等),拿到相关的文档id,根据文档ID找到对应的文档数据返回。
用的分词器是什么
IK 分词器
数据怎么从db同步到es
11 Spring Aop 的具体使用场景,底层原理,Spring用到了哪些设计模式,Spring怎么解决循环依赖
Spring Aop
AOP(Aspect Oriented Programming )本质:在不改变原有业务逻辑的情况下,运用动态代理技术,在运行期间对需要使用业务逻辑的方法进行增强。增强逻辑代码往往是权限校验代码、⽇志代码、事务控制代码、性能监控代码。
Spring 实现 AOP 思想使⽤的是动态代理技术。Spring 会根据被代理对象是否实现接⼝来选择使⽤ JDK 还是 CGLIB,默认使用 JDK 动态代理。
JDK:被代理对象需要实现接⼝
CGLIB:被代理对象没有实现接⼝
Spring用到了哪些设计模式
- 动态代理:AOP,声明式事务
- 工厂模式:通过BeanFactory和ApplicationContext来创建对象
- 单例子模式:Bean默认为单例模式
- 适配器模式:
image.png
Spring怎么解决循环依赖
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B, B依赖于C, C⼜依赖于A。
image.png
通过三级缓存解决set注入导致的循环依赖问题
image.png
12 平时开发用到的设计模式,具体的业务场景
策略模式:价格计算,地址组装
13 java的锁有哪些,什么是锁升级,两个版本的 synchronized
java的锁有哪些
- ReentrantLock 可重入锁
- Lock
- synchronized
synchronized 关键字可以加在方法上,不需要指定锁对象(此时的锁对象为 this),也可以新建一个同步代码块并且自定义 monitor 锁对象;而 Lock 接口必须显示用 Lock 锁对象开始加锁 lock() 和解锁 unlock(),并且一般会在 finally 块中确保用 unlock() 来解锁,以防发生死锁。
与 Lock 显式的加锁和解锁不同的是 synchronized 的加解锁是隐式的,尤其是抛异常的时候也能保证释放锁。
// Lock 可以不完全按照加锁的反序解锁,比如可以先获取 Lock1 锁,再获取 Lock2 锁
// 解锁时则先解锁 Lock1,再解锁 Lock2,加解锁有一定的灵活度
lock1.lock();
lock2.lock();
...
lock1.unlock();
lock2.unlock();
// synchronized 解锁的顺序和加锁的顺序必须完全相反
synchronized(obj1){
synchronized(obj2){
...
}
锁升级
针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用偏向锁(做标记)优先同一线程然后再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就会短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为重量级锁。
两个版本的 synchronized
在 Java 5 以及之前,synchronized 的性能比较低,但是到了 Java 6 以后,发生了变化,因为 JDK 对 synchronized 进行了很多优化,比如自适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等,所以后期的 Java 版本里的 synchronized 的性能并不比 Lock 差。
14 内存泄漏怎么排查出来的,JVM的垃圾回收器有哪些,项目用的是哪个
分代垃圾回收
java 的堆进行分代管理是为了方便垃圾回收。
年轻代
新创建的对象分配在 Eden 区,Eden区发生一次GC后,存活的对象移到S0区,满了之后移到S1区。
老年代
没发生一次Minor GC,年龄就加1,达到默认的15之后,晋升到老年代,进入Old区。
垃圾回收算法
- 标记-清除,容易产生内存碎片
- 标记-整理,对存活的对象和垃圾对象进行标记,然后将所有存活对象都向一端移动,然后直接清理掉端边界以外的内存,不会产生内存碎片
- 复制:算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上,再把已使用过的内存空间一次清理掉。
- 分代收集:新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,选用复制算法。
老年代中,对象存活率高,没有额外空间进行分配担保,使用“标记 - 清除”或“标记 - 整理”算法来进行回收。
垃圾回收器
- 串行收集器
- 并行收集器:Parallel Old收集器,公司用的也是并行收集器
- 并发收集器:CMS,G1
CMS:标记清除,用户线程与GC线程并发执行,会产生内存碎片
G1:标记-整理,标记复制的算法,不会产生内存碎片