本文已参与「新人创作礼」活动,一起开启掘金创作之路。
7.3:为啥使用Spring框架
Spring的核心功能IOC(控制反转,依赖注入),AOP(面向切面的编程)
IOC:我们在使用过程中不用关注于对象是怎么创建的,只用应用过去,sping自动帮我们完成注入,对象的创建,spring默认创建对象是单例,这样减少了频繁创建对象,让对象重复利用,所有的对象都是放在BeanFactory 工厂的
AOP:面向切面的编程,我们可以把一些公共的东西模块化,做成一个切面,在方法的运行过程中织入进去,好处是解耦,提高代码的重复利用率
7.4:限流如何实现,介绍细节
有几种方法可以实现限流操作
1.计数器限流。可以设置一秒钟通过指定数量的请求,每一个请求进来,请求数量+1,多余请求拒绝。等一秒钟结束后,请求数量归零,重新计算。 会出现突刺问题。前1mc请求达到最大数量,后999mc全部执行拒绝操作
2.漏桶算法。可以创建一个队列,保存暂时处理不了的请求,然后通过一个线程池定期从队列中获取请求来执行。
3.gateway+redis实现令牌桶限流算法 (☆)令牌生成速度恒定,请求没有拿到令牌,系统就执行拒绝
7.5:MySQL的binlog日志
binlog用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志。
逻辑日志:可以简单理解为记录的就是sql语句。
binlog的主要使用场景有两个,分别是主从复制和数据恢复。
- 主从复制:在
Master端开启binlog,然后将binlog发送到各个Slave端,Slave端重放binlog从而达到主从数据一致。 - 数据恢复:通过使用
mysqlbinlog工具来恢复数据。 binlog的刷盘时机mysql通过sync_binlog参数控制biglog的刷盘时机,取值范围是0-N:
- 0:不去强制要求,由系统自行判断何时写入磁盘;
- 1:每次
commit的时候都要将binlog写入磁盘;(最安全,默认的) - N:每N个事务,才会将
binlog写入磁盘。
7.6:什么情况不走索引
- 数据库有隐式类型转换,索引类型不一样,不走索引
- like查询前模糊匹配不走索引,后模糊匹配可能会走索引
- NULL列不走索引
- not,not in ,!=,or不走索引
- 在建的索列上有函数操作,都不走索引,比如EXPLAIN select FROM tb_test where substr(mobile,1,3)='159'
- 组合索引必须满足最左匹配原则,比如:abc,a,ab,abc,ac
7.7:redis如何实现分布式锁
redis分布式锁其实就是往redis设置一个key和value同时设置一个有效时间,并且redis是单线程的,不会并发操作,(执行任务完成后),再把redis的key删除掉(解锁),但是在使用的过程中可能有2个问题,使用过程中我们必须保证设置key和value和设置时间保证它的原子性(LUA),另外还是锁超时问题,比如:上锁2秒钟,但是任务执行超过2秒,我们一般用redission框架,它底层是lua脚本实现,可以保证设置值和时间的原子性,另外还有看门狗的机制,watch dog,我们上锁3秒,但是任务执行5秒,它会自动加时间
7.8: jVm内存模型,以及里面存的变量
7.9:垃圾回收器
简单介绍一下GC垃圾回收器:GC 就是负责回收程序不用的内存。一般的高级语言里面会自带 GC,比如 Java、Python、JavaScript 等,也有无 GC 的语言,比如 C、C++ 等
8.0:垃圾回收算法
标记清除策略:是JS中最常用的垃圾回收策略。 各大浏览器把回收效率进行修改后,进行使用。
整个标记清除算法大致过程就像下面这样:
- 垃圾收集器在运行时会给内存中的所有变量都加上一个标记,假设内存中所有对象都是垃圾,全标记为0
- 然后从各个根对象开始遍历,把不是垃圾的节点改成1
- 清理所有标记为0的垃圾,销毁并回收它们所占用的内存空间
- 最后,把所有内存中对象标记修改为0,等待下一轮垃圾回收 优点:实现非常简单;
缺点:
- 内存碎片化,空闲内存块是不连续的,容易出现很多空闲内存块,还可能会出现分配所需内存过大的对象时找不到合适的块
- 分配速度慢,因为即便是使用分配速度和效率最快的
First-fit策略,其操作仍是一个O(n)的操作,最坏情况是每次都要遍历到最后,同时因为碎片化,大对象的分配效率会更慢
标记整理算法 垃圾回收结束后,会将活着的对象,向内存的一段移动,清除掉边界的内存
:
新生代老年代垃圾回收策略::::TODO
新生代(Young generation)
绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为 minor GC。
新生代 中存在一个Eden区和两个Survivor区。新对象会首先分配在Eden中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden中的对象会被移动到Survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
可以设置新生代和老年代的相对大小。这种方式的优点是新生代大小会随着整个堆大小动态扩展。参数 -XX:NewRatio 设置老年代与新生代的比例。
老年代(Old generation)
对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代要少得多。对象从老年代中消失的过程,可以称之为full GC。
8.2:spring事务传播特性
目前,Spring在TransactionDefinition类中定义了以下7种传播特性,使用@Transactional注解
- PROPAGATION_REQUIRED:如果不存在外层事务,就主动创建事务;否则使用外层事务
- PROPAGATION_SUPPORTS:如果不存在外层事务,就不开启事务;否则使用外层事务
- PROPAGATION_MANDATORY:如果不存在外层事务,就抛出异常;否则使用外层事务
- PROPAGATION_REQUIRES_NEW:总是主动开启事务;如果存在外层事务,就将外层事务挂起
- PROPAGATION_NOT_SUPPORTED:总是不开启事务;如果存在外层事务,就将外层事务挂起
- PROPAGATION_NEVER:总是不开启事务;如果存在外层事务,则抛出异常
- PROPAGATION_NESTED:如果不存在外层事务,就主动创建事务;否则创建嵌套的子事务
8.3:spingbean的作用域范围
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
- global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
8.4:项目周期
根据实际情况填写
8.5:springBoot底层原理
Spring Boot 底层实际上主要的有三个注解:
Configuration , EnableAutoConfiguation ,ComponentScan,
它会读取META-INF下的spring.factories文件信息,通过反射的机制把bean纳入到spring的管理。 约定大于配置
8.6:MySQL索引底层
8.7:Eureka的底层原理
Eureka主要是通过心跳检测去判断的,有一个发送者和客户端,发送者会每隔30秒发送一个心跳到eureka上去,服务端会把eureka上的客户端发送的数据进行一个接受并调用的过程,如果说生产者没有发送心跳到注册中心上,那么就将所有的接口都剔除掉,如果中途eureka发生了宕机,那么也是可以进行调用的,因为将原来的数据放到了一个缓存中去,
Eureka还有自我保护机制,如果在15分钟内检测到有85%的服务都宕机了,那么系统会认为是网络的问题导致的
8.8:Eureka宕机后还可以提供服务吗
可以调通
- Eureka 作为一个服务注册中心启动。
- S1 和 S2 分别作为服务启动,并且注册到 Eureka 上面去,以 S1 为例,S1 注册时会把自己的元数据信息告诉 eureka(例:我叫 S1,我的地址是 xx.xx.xx.xx,我的端口是 xx,我的 xx 是 xx)。同理,S2 也是如此。
- 接下来,S2 要调用 S1 的接口,但是它不知道 S1 的地址是什么,他只知道要调用的服务叫 S1,于是 S2 找到 eureka,从 eureka 上查询出来 S1 的具体地址和端口,这个具体的地址和端口,可能是一个,也可能是多个(集群化部署)。
- S2 获取到 S1 的地址和端口之后,接下来就直接去调用 S1 了。 虽然可以调通,但是有前提:那就是S1的地址不能改变。
8.9:分布式事务如何解决
9.0:为什么使用mq
异步(发送手机验证码),
解耦(借款系统和风控系统),
削峰(秒杀,购买商品,放入消息队列排队)
9.1:spring如何解决循环依赖
使用三级缓存解决循环依赖
9.2:SpringMVC流程
- 客户端(浏览器)发送请求,直接请求到DispatcherServlet。
- DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
- 解析到对应的Handler后,开始由HandlerAdapter适配器处理。
- HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。
- 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。
- ViewResolver会根据逻辑View查找实际的View。
- DispaterServlet把返回的Model传给View。
- 通过View返回给请求者(浏览器)
9.3:hashmap的key存储一个Object对象会怎么
用自定义类作为key,必须重写继承自Object类的equals()和hashCode()方法。
分析hashMap的put方法可知:是把key进行hash,然后存储,如果key是Object,并且没有重写hashcode,那么它将用物理地址进行hash,否则将使用对象值进行hash。
总结:Object作为key时,不重写equals和hashCode函数,会将相同的对象(值相同)存入。在获取时也是先找相同的hash,再找值相同的对象。 如果不重写HashCode,无论equals重写与否,都是在table数组两个不同链表中;重写HashCode,不重写equals,则在table数组的同一个链表中;只有两个函数都重写,才能保证唯一性。
9.4:对称加密、非对称加密
对称加密: 信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密。 常用算法:AES,DES
非对称加密:
加密和解密不是同一个密钥,一般有一个公钥,一个私钥,公钥加密,私钥解密,算法有RSA
9.5:Arraylist和LinkedList区别
Arraylist是基于数组的,在查询效率比较高,插入删除效率比较低
Linkedlist是基于链表的,插入删除效率比较高,查询效率比较低
对于添加和删除的时候,linkedlist优于arraylist,因为arraylist在做数据的添加和删除的时候需要有数据的位置的移动
9.6:线程池底层原理
创建线程池的时候,开始一个线程也没有,随着任务的提交创建线程,当前线程如果小于corePoolSize核心线程数,继续创建线程, 否则(当前线程大于或等于核心线程数)放入LinkedBlockingQueue队列,如果队列没有满,继续放入,如果队列满了, 判断是否小于最大线程数,如果小于继续创建线程,否则拒绝策略(默认拒绝策略抛异常)
9.7:拒绝策略
1、使用线程解决
2、直接拒绝不抛异常
3、直接拒绝抛出异常(默认)
4、将最早的线程舍弃,将最新线程添加
9.8:jvm调优
① 分析 GC 日志及 dump 文件,判断是否需要优化,确定问题根源所在 ② 确定 JVM 调优量化目标和 JVM调优参数(JVM 调优参数根据历史 JVM 参数来调整) ③ 依次调优内存、延迟、吞吐量等指标 ④ 对比观察调优前后的差异,同时需要不断的分析和调整参数,直到找到合适的 JVM 参数配置; ⑤ 找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。
我们把上面的步骤总结一下其实也就是分三步:首先就是分析日志,确认是否需要调优;其次是确认调优目标及调优参数;最后一步就是反复测试并进行追踪。
上面的操作步骤中,某些步骤是需要多次不断迭代完成的(比如调整参数、追踪调优后的幸能)。这里有一点是需要注意的,我们调优的时候一般是从满足程序的内存使用需求开始的,接下来是满足程序的时间延迟的要求,最后才是满足程序吞吐量的要求,我们要遵循这个步骤来不断优化,每一个步骤都是进行下一步的基础,
绝对不能反其道而行之。