面试题总结day01
1.Java中基本数据类型有哪些?
答:整数类型:byte 一字节八位;最大容量255;short 2字节16 容量6w;int 4字节32位;long 8字节64位
浮点数(小数):float一位小数:1.0;double两位小数1.00;
字符类型:char
布尔类型;Boolean
2.Integer 和 int的区别
int是基本数据类型interger是引用数据类型对于int的包装,两者比较是只要数值是相等的就会判断为相等因为引用数据类型会自动 拆箱
3.String和StringBuilder区别
string不可变字符串有final修饰基于char[]数值每次创建对象会开辟新空间
StringBuilder是可变字符串,没有synchronized修饰线程不安全但是效率高
(字符串可变:1.把指定位置的字符串 替换成另一个字符串,2. 删除指定长度的字符串)
4.StringBuilder和StringBuffer区别
StringBuffer同样也是不可变字符串但是StringBuffer有synchronized修饰线程是安全的但是效率相对较低;
5.String a = “A” 和 String a = new String(“A”) 创建字符串的区别
String a = “A”首先回去常量池中找有没有”A”,有的话就直接将对象指向这个值,没有就在栈中创建一个放在常量池中;
而new是会在堆中创建一个string对象赋值a
6.== 和 equals 的区别是什么:
==比较对象比较的是地址,,equals引用数据类型比较,需要重写object中的equals()方法判断对象是否相同
面试题总结day02
7.final 和 finally 和 finalize 的区别
final修饰的类或者变量表示不可修改只能继承或者调用
finally 用于异常处理
finalize ()是一个方法java.long.object中定义的,调用即可 不需要去实现
8.JDK 和 JRE 有什么区别?
jre:提供java的运行环境包含jvm和java的核心内库
jdk:java的开发工具包
9.面向对象四大特性
抽象:对于公共的对象特征的提取,有构造方法以及抽象方法只能继承
封装:对于数据的封装,将隐私封装起来只提供调用方法隐藏信息以及具体代码
继承:子类继承父类,子类中拥有父类的实例变量以及方法,子类还能根据需求进行方法的添加
多态:多态是同一个行为具有不同表现形式或形态的能力,多态性是对象多种表现形式和体现。父类不能直接访问改子类的特点
10.方法覆盖和重载
覆盖:重写方法父类与子类的特性,参数是一样的,是一个方法之间的关系,实际的方法体不同;
重载:方法名相同传入的参数不同(追加参数或者减少参数)是多个方法之间的关系
11.普通类和抽象类
抽象类不能被实例化只能通过实例化其子类抽象类中的抽象方法只需要声明,在子类中实现可以有构造方法而且会被继承,不能有静态方法如果要调用使用default方法,子类必须实现抽象父类中的所有抽象方法
12.接口和抽象类的区别
修饰词不同一个是interface一个是abstract,接口有全局常量,抽象方法,(java8后:静态方法,默认方法),抽象类有构造方法抽象方法普通方法;接口是实现,抽象是继承
13.你知道BIO,NIO,AIO么?
BIO (Blocking I/O):同步阻塞I/O 模式,以流的方式处理数据,数据的读取写入必须阻塞在一个线程内等待其完成。适用于连接数目比较小且固定的架构
NIO (New I/O):同时支持阻塞与非阻塞模式,以块的方式处理数据,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天器
AIO ( Asynchronous I/O):异步非阻塞I/O 模型,适用于连接数目多且连接比较长(重操作)的架构
bio以流模式处理数据,同步阻塞,读写阻塞在一个线程中,nio支持阻塞与非阻塞模式适用于连接多且短的情况;aio异步非阻塞模式,适用于链接数目多且长的情况
14.java 中四大基础流
InputStream : 输入字节流, 也就是说它既属于输入流, 也属于字节流 ,
OutputStream: 输出字节流, 既属于输出流, 也属于字节流
Reader: 输入字符流, 既属于输入流, 又属于字符流
Writer: 输出字符流, 既属于输出流, 又属于字符流
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的
字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容简单来说 字节流不需要关闭资源可以读写,字符流可以理解成需要临时内存区域读写 ,需要关闭资源或者刷新资源
15.读文本用什么流,读图片用什么流
文本使用字符流,图片字节流
16.字符流和字节流有什么区别
字符流适用于读文本,字节流适用于读图片,视频,文件等。
字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
字节流默认不使用缓冲区;字符流使用缓冲区。
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元
17.BufferedInputStream 用到什么设计模式
主要运用了俩个设计模式,适配器和装饰者模式
适配器模式:适配器模式指的是把两个不想连的接口通过某个类连接起来。例子:比如我的小米笔记本电脑没有一个插接网线的接口,这个时候网线和电脑是没法连在一起的,然后我买了一个网线转接口。这时候通过转接口我的网线和电脑就可以连接起来了。
装饰者模式:这种本身是一种类型,保持类型接口不变,又能对该类型的其他子类进行加强的能力,就叫装饰者。就好像,我们在一个对象上面增加一种装饰,使得它更好看了,或者功能更强大了。
18.带缓冲区的流有哪些
BufferedInputStream 带缓冲区的字节输入
BufferedOutputStream 带缓冲区的输出流
BufferedReader : 带缓冲区的字符输入流
BufferedWriter : 带缓冲区的字符输出流
19.说一下Java中的集合体系
Collection接口
List:

  • ArrayList:底层数据结构是数组,查询性能高,增删性能低
  • Vector:底层数据结构是数组,查询性能高,增删性能低
  • LinkedList:底层数据结构是双向链表,查询性能低,增删性能高

Set:

  • HashSet:无序不重复的,使用HashMap的key存储元素,判断重复依据是hashCode()和equals()
  • TreeSet:有序不重复的,底层使用TreeMap的key存储元素,排序方式分为自然排序,比较器排序

Map接口

  • HashMap:key的值没有顺序,线程不安
  • TreeMap:key的值可以自然排序,线程不安全
  • HashTable:它的key和value都不允许为null,线程安全
  • Properties:它的key和value都是String类型的,线程安全

20.HashMap和HashTable的区别
HashMap和HashTable都是实现了Map接口的集合框架,他们的区别

  • HashTable是线程安全的,它的实现方法都加了synchronized关键字,因此它的性能较低
  • HashMap是线程不安全的,它实现方法没有加synchronized,因此它的性能较高
  • HashMap的key和value都允许为null,HashTable中的key和value都不能为null,如果不考虑线程安全,建议使用HashMap,如果需要考虑线程安全的高并发实现,建议使用ConcurrentHashMap

21.ArrayList和LinkedList区别
ArrayList基于数组,查询快,LinkedList基于双向链表操作快速,不支持索引查询效率低
22.一个User的List集合,如何实现根据年龄排序
第一种方式,让User类实现Comparable接口,覆写compareTo方法,方法中自定义根据年龄比较的算法
第二种方式,调用Collections.sort方法,传入一个比较器,覆写compare方法,方法中自定义根据年龄比较的算法
23.HashMap底层用到了那些数据结构?
jdk1.7之前数组和链表,1.7之后加入了红黑树
24.什么是Hash冲突
hash冲突也是指hash碰撞指的是两个不同的值通过计算后取得相同的下标
解决方式:

  • 拉链法,把哈希碰撞的元素指向一个链表
  • 开放寻址法,把产生冲突的哈希值作为值,再进行哈希运算,直到不冲突
  • 再散列法,就是换一种哈希算法重来一次
  • 建立公共溢出区,把哈希表分为基本表和溢出表,将产生哈希冲突的元素移到溢出表

25.HashMap为什么要用到链表结构
在hashmap中添加元素是会根据key进行哈希算法把hash值与数组长度得到一个下标,然后将该元素添加进去,但是会发生哈希碰撞,当发生哈希碰撞时不同的key运算出了相同的hash值就需要将发生哈希碰撞的值挂载到连表中采用拉链法重新运算hash值
26.HashMap为什么要用到红黑树
当HashMap中同一个索引位置出现哈希碰撞的元素多了,链表会变得越来越长,查询效率会变得越来越慢。因此在JDK1.8之后,当链表长度超过8个,会将链表转坏为红黑树来提高查询
27.HashMap链表和红黑树在什么情况下转换的?
当链表的长度大于等于8,同时数组的长度大于64,链表会自动转化为红黑树,当树中的节点数小于等于6,红黑树会自动转化为链表
28.HashMap在什么情况下扩容?HashMap如何扩容的?
HashMap初始容量为16但是负载因子是0.75所以当容量长度达到12时会2倍扩容
扩容方式:
tips:为啥子是0.75:负载因子过小容易浪费空间,过大容易造成更多的哈希碰撞,产生更多的链表和树,因此折衷考虑采用了0.75
为啥子是成倍扩容:需要保证数组的长度是2的整数次幂
为啥数组的长度必须是2的整数次幂:我们在存储元素到数组中的时候,是通过hash值模与数组的长度,计算出下标的。但是由于计算机的运算效率,加减法>乘法>除法>取模,取模的效率是最低的。开发者们为了让你用的开心,也是呕心沥血。将取模运算转化成了与运算,即数组长度减1的值和hash值的与运算,以此来优化性能。但是这个转化有一个前提,就是数组的长度必须为2的整数次幂
29.HashMap是如何Put一个元素的
首先呢将key值进行hash运算获取到在将该值与上该数组长度减少一的值(与:二进制转换运算)计算出索引,然后判断当前索引是否已经有元素,如果没有就放在当前索引;如果当前索引中以及有有元素了就通过额equals比较key和hash值如果相同就是相同的key将原有数据替换掉并返回原有数据,如果不相同判断是连表还是红黑树如果是红黑树就以红黑树的方式写入该数据,如果是连表的话按照连表的方式进行数据写入
连表的写入数据的方式:就依次遍历并比较当前节点的key和新元素的key是否相同,如果相同就覆盖,如果不同就接着往下找,直到找到空节点并把数据封装成新节点挂到链表尾部。然后需要判断,当前链表的长度是否大于转化红黑树的阈值,如果大于就转化红黑树,最后判断数组长度是否需要扩容。
30.HashMap是如何Get一个元素的
方式:
首先通过hash运算获取到数组的索引位置,判断当前索引位置是否有元素如果没有元素则返回null如果有元素则运算比较两个key是否相等,相等则返回当前value;如果第一个元素的key不匹配,判断是红黑树还是链表,如果是红黑树,就就按照红黑树的查询方式查找元素并返回,如果是链表,就遍历并匹配key,让后返回value值
31.你知道HahsMap死循环问题吗
死循环问题发生在 JDK 1.7 版本中,造成这个问题主要是由于 HashMap 自身的运行机制,加上并发操作,从而导致了死循环。
死循环的原因是因为在进行连表扩容是储存的节点顺序是a-b-c-null而进行扩容后新生成的hashmap的节点和原来的节点顺序是相反的而且第二个扩容线程会等待该线程先扩容完再进行操作,轮到第二个线程进行扩容时操作的还是原来的hashmap节点的改变并没有反馈给第二个线程导致扩容挂载的节点有误进入死循环;
解决方案:
HashMap 死循环的常用解决方案有以下 3 个:

  • 使用线程安全容器 ConcurrentHashMap 替代(推荐使用此方案)。
  • 使用线程安全容器 Hashtable 替代(性能低,不建议使用)。
  • 使用 synchronized 或 Lock 加锁 HashMap 之后,再进行操作,相当于多线程排队执行(比较麻烦,也不建议使用)。总结HashMap 死循环发生在 JDK 1.7 版本中,形成死循环的原因是 HashMap 在 JDK 1.7 使用的是头插法,头插法 + 链表 + 多线程并发 + HashMap 扩容,这几个点加在一起就形成了 HashMap 的死循环,解决死锁可以采用线程安全容器 ConcurrentHashMap 替代。

32.说一下你对ConcurrentHashMap的理解
理解:
ConcurrentHashMap,它是HashMap的线程安全,支持高并发的版本
在jdk1.7中,它是通过分段锁的方式来实现线程安全的。意思是将哈希表分成许多片段Segment,而Segment本质是一个可重入的互斥锁,所以叫做分段锁。
Segment(分段锁):ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)
在jdk1.8中,它是采用了CAS操作和synchronized来实现的,而且每个Node节点的value和next都用了volatile关键字修饰,保证了可见性

数据库基础

平局值用什么,分组用什么

统计平局值:avg , 分组:group by

两个相同列的结果集求并集用什么

union 并集 , union all(允许重复并集)

完整查询SQL中的关键字的定义顺序

SELECT 列名 FROM 表1 JOIN 表2 ON 条件 WHERE 条件 GROUP BY 列名 HAVING 条件 ORDER BY 列名 LIMIT

完整的多表JOIN查询,SQL中关键字的执行顺序

FROM —> ON —> JOIN —> WHERE —> GROUP BY —> HAVING —> ORDER BY —> LIMIT

员工表employee字段有: id, username, amount ,deptname .

  • 求每个部门总人数怎么做 ,

select 部门名,count(id) from employee group by deptname

  • 求每个部门总工资怎么做?

select 部门名,sum(amount) from employee group by deptname

Spring部分

介绍一下Spring

Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度,面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务如事务,日志进行集中管理
(开源的轻量级控制反转面向切面编程的容器,将创建对象以及销毁的权利交给ioc容器,面向切面指的是将一些相同逻辑抽取出来进行事物管理日志管理等)

说下Spring框架的组成

  1. CoreContain核心容器模块:
    1. spring-core:提供框架的基本组成部分,包括 IoC 和依赖注入功能
    2. spring-beans:提供 BeanFactory,工厂模式
    3. context:提供国际化,事件传播,资源加载等功能
    4. spring-ExpressionLanguage:提供表达式语言(el表达式)
  2. Web模块
    1. Web:提供面向web的基本功能和面向web的应用上下文
    2. Web-MVC:为web应用提供模型视图控制(MVC)
    3. Web-Socket:在 web 应用程序中提供客户端和服务器端之间通信的方式(建立服务器间的通讯)
    4. Web-Portlet:模块提供了用于Portlet环境的MVC实现
  3. 数据/集成模块
    1. JDBC:包含了Spring对JDBC数据访问进行封装的所有类
    2. ORM:为对象-关系映射提供交互层
    3. OXM:提供对Object/XML映射实现的抽象层
    4. JMS:主要包含了一些制造、消费和消息的功能
    5. Transaction:为实现特殊接口类以及所有的 POJO 支持编程式和声明式的事务管理(事务管理)
  4. 其他模块
    1. AOP:提供了面向切面编程相关实现
    2. Aspects:模块提供了与AspectJ的集成,是一个功能强大的AOP框架
    3. Instrumentation:提供了class instrumentation 的支持和类加载器classloader的实现
    4. Messaging:为 STOMP 提供支持
    5. Test:支持使用JUnit和TestNG对Spring组件进行测试

什么是Spirng的IOC

IOC控制反转,把对象的创建,属性设置,初始化,销毁等工作交给Spirng的IOC容器去管理,解放程序员的劳动力。

对象被注册到Spring的IOC容器中,使用的时候从容器中获取即可,非常方便。

它通过依赖注入,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅

你对AOP的理解

AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一

AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。而这些重复的代码,一般统称为横切逻辑代码

使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复

AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等

它是基于动态代理实现的,分为JDK动态代理和CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理

Spring的Bean懒加载和非懒加载有什么区别

懒加载:需要使用对象的时候才创建,节省资源,但不利于提前发现错误

非懒加载,也叫迫切加载,容器启动时就创建对象,消耗资源,但有利于提前发现错误

spring中默认时迫切加载,即在项目启动时,spring会扫描符合条件的所有bean并将其初始化

如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init=”true”

Spring的依赖注入方式有哪些

方式一:setter方式注入,通过反射调用无参构造方法生成对象,再通过对于的setter方法注入配置的值,支持注解和xml两种实现方式

方式二:构造器方式注入,通过反射调用有参构造方法生成对象,支持注解和xml两种实现方式

注解实现方式:@Autowired,它是默认按类型匹配的、@Resource,它是默认按名字匹配的

说一下定义切面相关的注解

@Aspect:定义切面

@Pointcut:定义切点 = cn.xx.service.*

@Before:前置通知,在目标方法运行之前运行

@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)

@AfterReturning:返回通知,在目标方法正常返回之后运行

@AfterThrowing:异常通知,在目标方法出现异常以后运行

@Around:动态代理,手动推进目标方法运行

Bean的四种注册方式

方式一:普通注册方式,直接通过class注册

方式二:简单静态工厂方式注册

方式三:简单实例工厂方式注册

方式四:FactoryBean方式注册

注册Bean的注解有哪些

@Controller/@RestController 一般用于定义控制层的类

@Service 一般用于定义服务层的类

@Repository 一般用于定义持久层类

@Component 定义一般类

@Configuration 定义配置类

IOC的启动流程有了解过吗

当Spring启动时,IOC容器会加载Spring的配置文件,包括XML配置或者注解,然后解析这些Bean并把相关定义信息封装成BeanDefinition对象,通过Bean注册器BeanDefinitionRegistry注册到IOC容器,也就是一个ConcurrentHashMap中

此时会找出所有的单例且非惰性加载的bean,根据其BeanDefinition进行Bean的实例化,它会判断如果bean中有方法覆盖,就使用JDK反射创建Bean,否则使用CGLIB方式生成代理。然后把实例化好的Bean缓存到一个ConcurrentHashMap中

Bean的生命周期讲一下

从宏观的角度来说就是:实例化 ,属性注入,初始化,使用,销毁。更细的生命周期如下

  1. 实例化:如果是单例且迫切加载的bean,在Spring容器启动时就会根据BeanDefinition进行实例化,如果时设置了懒加载或者多例模式的bean,在用的时候才会实例化
  2. 属性赋值:通过BeanDeifinition找到当前Bean所依赖的其他Bean,如果容器中有就直接拿过来,如果没有就根据创建流程区创建依赖的bean,然后通过反射给依赖的字段注入值
  3. 然后会调用BeanPostProcessor的前置处理器,对于@Autowired和@Transcational就是基于BeanPostProcessor来实现的。
  4. 接着会看Bean是否实现InitializingBean ,如果有会触发其afterPropertiesSet方法的调用
  5. 接着是调用我们自定义的bean的init-method方法,此时会调用执行
  6. 然后是调用BeanPostProcessor的后置处理
  7. 容器正常关闭,Bean进行销毁,会先调用实现了DisposableBean的destory方法。
  8. 接着调用我们指定的bean的destroy-method方法,此时会调用执行 | 1.单例多例的区别
    2.Spring的Bean被指定为prototype以及singleton有什么区别
    3.BeanFactory和ApplicationContext有什么区别
    4.BeanFactory和FactoryBean的区别
    5.IOC容器是如何保证Bean的单例的? | | | —- | —- |

    单例和多例模式都属于是对象模式,单例模式只会创建一个对象,而多例模式会创建多个对象,
    spring容器中默认使用的是单例模式,,如果需要使用多例,可以通过修改scope属性:scope=”prototype”如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个map中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。如果是多例(prototype)模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
    (单例和多例的区别在于有没有走map缓存的过程以及对象的创建流程)
    singleton:指定为单例模式,prototype:指定为多例模式
    BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
    ApplicationContext接口是BeanFactory接口的子接口,除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件等服务相关的接口
    BeanFactory是延迟加载,ApplicationContext是迫切加载(BeanFactory是核心接口只提供基本方法而ApplicationContext接口是BeanFactory接口的子接口,继承了BeanFactory中所有功能还进行了国际化,资源事件等补充)
    BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
    FactoryBean是IOC容器创建bean的一种形式,可以通过实现此接口来创建实例化过程比较复杂的bean(FactoryBean用于创建并实例化比较复杂的bean)
    IOC容器会将单例模式的bean放入一个ConcurrentHashMap中,需要这个bean时直接到这个map中获取,如果没有找到才会实例化这个bean。而ConcurrentHashMap本身时线程安全的,也就保证了Bean是单例的(放进ConcurrentHashMap中获取,没有找到才会实例化bean,ConcurrentHashMap线程安全保证单例)

1.Spring如何解决Bean的循环依赖
2.Spring构造器注入允许循环依赖吗为什么
3.说几个Spring的IOC的容器工厂类
4.你知道Spring的AOP主要基于什么设计模式实现吗
5.你知道@Autowaire自动注入的实现原理吗?
  1. 循环依赖分为三种,构造器注入循环依赖 setter方式注入循环依赖,多例模式Bean的循环依赖。而Spring解决了单例beansetter注入循环依赖

setter循环依赖的解决主要使用了三级缓存

  • 一级缓存,用来缓存已经实例化好的bean,即单利Bean缓存池
  • 二级缓存,用来缓存正在创建的bean
  • 三级缓存,用来缓存创建bean的实例工厂ObjectFactory

假设有两个bean,A依赖B,B依赖A

当实例化好A,在属性注入环境,发现A依赖了B,会先将正在创建的A的实例工厂ObjectFactory放入三级缓存,然后去创建B的实例。

走Bean的实例化流程创建B,在B的属注入环节发现,B依赖了A,这个时候就会去三级缓存中,找到A的创建工厂ObjectFactory获取A的实例,并注入到B中。此时B就初始化好了,然后将B实例放入一级缓存。最后将B实例注入到A中,A也就创建好了

在getBean的时候,如果单利Bean缓存池没有Bean,就会走二级缓存尝试获取,如果也没有,就会走三级缓存拿到Bean的ObjectFacory创建Bean,然后把Bean放入二级缓存。

Spring构造器注入能循环依赖吗

构造注入不能解决循环依赖的原因是:如果A的构造其中依赖了B B的构造器中又依赖了A 在getSingleton中三级缓存需要调用getObject()构造器,来构造提早暴露但未设置属性的bean,此时就会产生无限递归创建

多例模式下Bean是不做缓存的,所以就没法暴露ObjectFactory,也就没办法解决循环依赖

说几个Spring的IOC的容器工厂类

BeanFactory:IOC容器顶层接口,提供了Bean获取的基础方法

DefaultListableBeanFactory:是整个 bean 加载的核心部分,Spring 注册及加载Bean 的默认实现

ApplicationContext:除了实现IOC基本功能外,还扩展了国际化支持,资源访问,事件发布

ClasspathXmlApplicationContext:从classpath中获取XML配置

你知道Spring的AOP主要基于什么设计模式实现吗

AOP的实现原理是基于动态代理,动态代理就是在运行时期动态的为原生类生成代理类以达到代码增强的目的,且代理类是持有原生类的,可以在代理类中调用原生类以及做一些增强业务。

动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,原生类可以不实现任何接口

Spring中默认采用JDK动态代理,如果原生类没有实现任何接口,Spring会选择CGLIB代理,或者你可以在配置文件中强制指定使用CGLIB代理

你知道@Autowaire自动注入的实现原理吗?

自动注入是通过BeanPostProcessor 后置处理器AutowiredAnnotationBeanPostProcessor完成的,在Bean实例化过程中,触发了AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法的调用执行,它就会扫描当前类中是否有@Autowired注解,然后得到自动注入依赖的bean的类型,并去容器中得到依赖的bean实例,如果没有就走Bean的实例化流程创建依赖的Ban,然后反射进行字段赋值。

你知道@Transcational注解的实现原理吗?

分为两个动作把,第一个是解析@Transcational注解,在Sping中有个后置处理器InfrastructureAdvisorAutoProxyCreator,在Bean的初始化过程中,它负责解析标记了@Transcational注解的类,生成代理。还创建了 TransactionAttributeSource ,它是对事务注解的封装,以及 TransactionInterceptor 事务拦截器。

在执行业务方法的时候,代码会进入事务拦截器TransactionInterceptor去执行事务相关的代码,TransactionInterceptor主要是通过调用TranscationManagerment的事务API,而TranscationManagerment又是调用connection的事务API完成事务操作。

常见Http状态码

200 成功返回状态
301 永久重定向,被请求的资源永久移动到新位置
302 临时重定向,被请求的资源临时移动到新的位置,项目中使用了oauth2,对目标资源访问无权限时就会见到,它是会重定向到授权地址
401 无权限访问
403 禁止访问,服务器已经接收到请求,但拒绝执行
404 找不到该资源
500 服务器内部错误 zuul找不到服务名就会见到
503 服务器内部错误 服务器维护或者过载
504 网关超时

Servlet的生命周期

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • Servlet 初始化后调用 init () 方法。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 销毁前调用 destroy() 方法。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

    什么是过滤器?怎么创建一个过滤器

    过滤器:在请求发送之后,处理之前对请求的一次拦截,可以更改请求状态或者参数值等。
    创建过滤器:实现filter接口,重写doFilter方法,最后在web.xml中配置过滤器

    讲一下Session的工作原理

    服务端的session id会自动写入客户端的cookie中,每次请求客户端回自动把cookie带入后台,后台自动根据cookie中的sessionid就能找到session

    Session和cookie有什么区别

    session和cookie都是为了弥补http协议的无状态特性,解决会话问题
    session是以ConcurrentHashMap结构存储在服务器端,同时生成一个sessionid返回客户端并存放到cookie中
    cookie是将数据存储在客户浏览器端
    session占用服务器的性能,但安全性较高,使用cookie减轻服务器的压力,但有被用户篡改风险因此安全性较低
1.说说preparedStatement和Statement的区别
2.statement有sql注入风险,preparedStatement没有sql注入风险
3.请求转发和重定向的区别
4.get和post请求的区别
5.JSP的原理
6.SpringMVC怎么样设定重定向和转发的

说说preparedStatement和Statement的区别

statement的sql语句使用字符串拼接,很容易出错,而preparedStatement使用?作为占位符,不容易出错易于维护
statement不对sql语句作处理,直接交给数据库,而preparedStatement支持预编译,事先将编译好的sql语句放到数据库端,相当于缓存,因此效率更高
statement有sql注入风险,preparedStatement没有sql注入风险

请求转发和重定向的区别

转发是一次请求,可以共享同一组request和response,重定向是多次请求,不能共享同一组request和response
转发地址栏不会发生变化,重定向地址栏会发生变化
转发不能到外部应用,重定向可以到尾部应用
如果我们需要数据共享,使用转发,如果需要访问内部资源(WEB-INF),使用转发,如果需要跨域到外部资源,必须使用重定向

get和post请求的区别

最直观的区别,get把参数包含在url中,post是把参数放到request body中
post相对于get更安全,post发送的数据更大,get有url的长度限制
post更发送更多的数据类型,get只能发送ASCII字符
在restful中,get一般用户查询搜索数据,post一般用户添加或者修改数据

JSP的原理

jsp的本质就是servlet,每个JSP文件都回被编译成一个Serverlet去执行,在该Serverlet会对JSP中的动态内容进行替换,静态部分是标准的html,动态部分是java程序

SpringMVC部分

SpringMVC怎么样设定重定向和转发的

重定向是指将用户从当前请求重新定向到一个视图页面,或者是一个handler处理请求,以前的request域中信息全部失效,同时地址栏会发生变化,它是客户端行为
转发是指将用户从当前请求转发给另一个视图页面或者handler处理请求,以前的request域可以共享,地址栏不会发生变化,它是服务器行为
springmvc默认是使用转发方式跳转的,且会默认经过视图解析器,我们也可以通过指定,转发时在返回值前面加”forward:”,重定向时在返回值前面加”redirect:”,且此时就不会再经过视图解析器了

1.SpringMVC如何对时间格式的参数进行格式化
2.SpringMVC常用的注解有哪些
3.如何定义SpringMVC的拦截器
4.HandlerInterceptor和HandlerInterceptorAdapter的区别
6.SpringMVC的执行原理

SpringMVC如何对时间格式的参数进行格式化

第一种需求,后台接收前台页面返回的string类型时间,要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数(用于接收前端参数)
第二种需求,后台将Date类型数据返回给前台页面,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解(返回数据给前端)

SpringMVC常用的注解有哪些

@Controller:用来标识一个类是控制器类
@RequestMapping:用来映射请求路径和参数
@ResponseBody:将返回值放到responsebody中,通常返回json或者xml格式数据
@RequestBody:将前台请求参数转换成对象
@PathVariable:接收路径参数,通常用在restful接口中
@RestController:@Controller和@ResponseBody的组合注解
@ControllerAdvice:运用aop的思想,对全局做一些处理,比如-结合@ExceptionHandler做全局异常捕获

如何定义SpringMVC的拦截器

SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上
第1步,定义拦截器:可以实现 HandlerInterceptor 接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行
第2步,配置拦截器:在springmvc的配置文件xml中,配置所有拦截路径,以及需要放行的路径

HandlerInterceptor和HandlerInterceptorAdapter的区别

HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true

SpringMVC的执行原理

1.Http请求:客户端请求提交到DispatcherServlet-前端控制器
2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler
3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler
4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView
5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图
6.Http响应:将结果显示到客户端

1.SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决
2.RequestMapping 和 GetMapping有什么区别
3.相比Spring,Spring Boot有哪些优点
4.SpringBoot如何做全局异常处理
5.@SpringBootApplication注解的含义
6.spring-boot-starter-parent的作用

SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决

在spring中,bean默认都是单例的,controller也是交给spring容器管理的一个bean,因此它也是单例的。
单例的好处是减少了创建对象和垃圾回收的时间,节省了内存资源,但同时单例会造成线程不安全的问题,因为当所有请求访问同一个controller实例,controller中的成员变量是所有线程公用的,某个线程如果修改了这个变量,别的请求再来拿这个变量就编程修改后的值了
要解决这个问题,最直接有效的方式就是不要在controller中定义成员变量,如果你非要定义成员变量,两种方式
第一种,可以给controller上加注解@Scope(“prototype”),将controller设置为多例模式,每次请求都重新实例化一个controller
第二种,使用ThreadLocal变量,让每一个线程都有自己独立的变量

RequestMapping 和 GetMapping有什么区别

@Getmapping是一个组合注解,即是@RequestMapping(method = RequestMethod.GET)的缩写,意思是只接收get请求的方法
@Requestmapping如果没有指定请求方式,可以接收get,put等各种类型的请求

SpringBoot部分

相比Spring,Spring Boot有哪些优点

Springboot是一个基于spring的框架,对spring做了大量简化,使开发流程更快,更高效
它大量简化maven依赖,管理了大量的基础依赖
基于注解配置(JavaConfig),无需xml配置
内嵌Tomcat,部署流程简单
打包和部署更加灵活,允许独立运行

SpringBoot如何做全局异常处理

可以使用@ControllerAdvice注解,编写一个全局异常处理类,再自定义一个方法使用@ExceptionHandler来捕获具体的异常并作相应的处理
通常情况下后台向前台返回结果时,会把结果封装成包含有错误码,错误信息以及数据本身的json数据,因此我们可以使用自定义异常类,自定义枚举错误码,在捕获全局异常后,向前台返回一个包含错误码的信息

@SpringBootApplication注解的含义

@SpringBootApplication是SprnigBoot项目的核心注解,目的是开启自动配置,并表示该类为主启动类。它包含三个子标签

  • @ComponentScan注解:开启ioc自动扫描注解,默认扫描当前包及其子包中@Controller,@Service等,并把这些bean加载到ioc器中
  • @EnableAutoConfiguration注解:启用springboot自动配置,自动所有扫描classpath目录下面所有jar中的spring.factories文件实现配置类批量注册
  • @SpringBootConfiguration注解:标志该类为springboot配置类

    spring-boot-starter-parent的作用

    这是SpringBoot的父工程,它的作用是帮我们管理了很多的基础jar包,同时它继承了spring-boot-dependencies,在spring-boot-dependencies项目中通过管理了大量的依赖,同时通过维护了这些依赖的版本号
    但是在项目中,还需要通过 去导入具体的依赖才能使用
1.spring-boot-starter-web的作用
2.SpringBoot中如何读取配置
3.SpringBoot中日志的level有哪些
4.SpringBoot中如何管理事务
5.pringBoot自动配置原理

spring-boot-starter-web的作用

此项目是Springboot和Springmvc整个的jar包,构建了web项目的基本环境,集成了日志,tomcat,springmvc,json支持等等

SpringBoot中如何读取配置

方式一:使用@Value读取配置文件
方式二:使用@ConfigurationProperties读取配置文件

SpringBoot中日志的level有哪些

日志级别从低到高分别为:
TRACE < DEBUG 如果设置为 WARN,则低于 WARN 的信息都不会输出
Spring中默认使用INFO级别输出到控制台

SpringBoot中如何管理事务

事务(transaction)是指业务逻辑上对数据库进行的一系列持久化操作,要么全部成功,要么全部失败。
在Springboot中,可以通过xml配置和注解配置
xml方式通过配置DataSourceTransactionManager和transactionManager实现
注解方式配置通过在主启动类上加上@EnableTransactionManagement开启事务管理器,在具体的实现层service类上加上@Transactional 实现事务

SpringBoot自动配置原理

在启动类上我们会打上: @SpringBootApplication 注解,它是一个组合标签,包括:

  • SpringBootConfuration ,本质是一个 Configuration ,代表Spring的配置类。
  • IOC自动扫描的注解 ,ComponentScan 会去扫描类上是否有:@Component ,@Respository ,@Service @Controller ,如果有,就会把这个类自动注册到Spring容器中。
  • EnableAutoConfiguration :就是启动SpringBoot自动配置的注解

在 @EnableAutoConfiguration 注解中,注册了一个选择器,其中有一个方法会去返回很多的自动配置的的全限定名,这些类会自动注册到Spring容器中,
那它是怎么去找到这些所谓的自动配置类的呢?
他会通过Spring的SPI接口,也就是通过一个SpringFactoryLoader去扫描 classpath中的所有的jar包中的 MET-INF/spring.factories 中的自动配置类,比如: DispatchServlert就对应了DispatchServlertAutoConfiguration自动配置类 , 它通过@Bean+方法的方式注册了一个 DispatchServlert 到Spring容器中了

1.SpringBoot启动流程
2.MyBatis中${}取值和#{}取值的区别
3.MyBatis关联查询中,延迟加载和饥饿加载的区别
4.MyBatis对象关联查询和集合关联查询怎么做
5.MyBatis一级缓存和二级缓存的区别
6.MyBaits的Mapper接口没有实现类为社么可以用@Autowired直接注入


SpringBoot启动流程

1.开启秒表计时
2.starting监听器,
3.处理应用参数
4.加载环境对象
5.打印横幅
6.创建Spring容器对象:AnnotationConfigApplicationContext
7.容器刷新的前置工作
8.刷新容器 ,这里会执行spring的ioc属性容器的refrsh方法,Bean的加载,初始化等都在这个里面,Tomcat的启动也在这个方法里面。
9.刷新容器后置工作
10.秒表停止
11.started事件
12.调用runner
13.running.listeners.

Mybatis部分

MyBatis中${}取值和#{}取值的区别

{}能够防止SQL注入,因为底层使用PreparedStatement对象,预编译,性能较高
${}不能防止SQL注入,因为底层使用Statement对象,不会预编译而是拼接字符串,性能较低
能使用#{}时尽量使用#{},如果需要动态传入表名或者字段名需要用 ${}比如,像 ORDER BY 时只能使用${}

MyBatis关联查询中,延迟加载和饥饿加载的区别

延迟加载,是先从单表查询,需要使用关联数据的时候才发起关联查询,不用的时候不查询关联的数据,又叫懒加载,饥饿加载,是在查询时将关联的数据立即查询出来加载进内存,不管用不用

MyBatis对象关联查询和集合关联查询怎么做

单个关联对象用associate ,适用于多对一的关联查询,使用javaType来定义实体类型,集合用collection,适用于一对多的关联查询,使用ofType来定义集合的泛型类型

MyBatis一级缓存和二级缓存的区别

缓存,是指将从数据库查询出的数据存放在缓存中,下次使用相同查询时不必再从数据库查询,而是直接从缓存中读取,从而减轻数据库查询的压力,提高性能
mybaits中的一级缓存,是SqlSession级别,默认开启,使用同一个SqlSession发送相同的SQL时命中;它的生命周期和SqlSession一致,当调用SqlSession.close()方法时会释放缓存
mybatis中的二级缓存,是namespace级别,默认不开启,执行同一个namespace的相同statement,发送相同的SQL时命中;它的生命周期是程序结束
当SQL中执行了update()、delete()、insert()操作,则缓存中的数据都会清空

MyBaits的Mapper接口没有实现类为社么可以用@Autowired直接注入

动态代理,赋值给mapper接口引用的对象其实是一个代理对象,这个代理对象是由 JDK 动态代理创建的。在解析mapper的时候,mybatis会通过java反射,获取到接口所有的方法
当调用接口中方法时,将通过接口全限定名+方法名对应找到映射文件中namespace和id匹配的sql,然后将执行结果返回

1.在MyBatis如何动态修改SQL
2.MyBatis的动态SQL标签有哪些?
3.Mybatis的mapper如何传递多个参数
4.Mybatis,关联对象查询,使用嵌套子查询和JOIN连表有什么区别
5.为什么要使用连接池
6.讲一下你理解的Redis,为什么Redis很快

在MyBatis如何动态修改SQL

使用Mybatis的拦截器可以做到
(简单来说就是拦截器拦截并重写你的sql)

MyBatis的动态SQL标签有哪些?

if标签:条件判断
choose、when、otherwise标签:选择结构,类似java中的switch
trim标签:对包含的内容加上前缀,后缀
where标签:主要是用来简化SQL语句中where条件判断的,能智能的处理and or,不必担心多余导致语法错误
foreach标签:遍历元素

Mybatis的mapper如何传递多个参数

方式一,可以使用map进行传参,SQL中使用map的key来引用取值
方式二,可以在SQL中使用#{param1},#{param2}…来引用取值,它是根据mapper接口对应方法中形参的顺序进行匹配的,不管接口方法的参数名字叫个啥,SQL都只能使用param1,param2,等来取值
方式三,可以使用@Param注解,给mapper接口方法的参数命名,在SQL中直接使用取的名字来引用

Mybatis,关联对象查询,使用嵌套子查询和JOIN连表有什么区别

嵌套子查询,指的是在查询一个主对象的时候,使用单表查询,在resultmap中额外发送一个子sql查询关联对象,然后映射给主对象
连表join查询,指的是查询一个主对象的时候,使用join连表的方式把主对象和关联对象的数据一次性查出来,用resultmap映射结果
他们的区别,join连表查询只发一条sql就能把数据查询出来,嵌套子查询会有一个n+1的问题,就是说如果主查询出来n条数据,那么会额外发送n条子sql去查询对应的关联对象,加上主查询那1次,也就是n+1次,因此它的性能相对较低的,一般我们会使用join连表查询

为什么要使用连接池

对数据库的操作都需要取得连接,使用完都需要关闭连接,如果每次操作需要打开关闭连接,这样系统性能很低下。连接池就可以动态的管理这些连接的申请,使用和释放,我们操作数据库只需要在连接池里获取连接,使用完放回连接池,这样大大节省了内存,提高效率。
数据库连接池的原理主要分为三部分

  • 第一,连接池的建立,在系统初始化时建立几个连接对象以便使用。
  • 第二,连接池的管理,客户请求连接数据库时,首先查看连接池中是否有空闲连接,如果有直接分配,如果没有就等待,直到超出最大等待时间,抛出异常
  • 第三,连接池的关闭,当系统关闭时,连接池中所有连接关闭

    Redis部分

    讲一下你理解的Redis,为什么Redis很快

    Redis是一种高性能的,开源的,C语言编写的非关系型数据库,可以对关系型数据库起到补充作用,同时支持持久化,可以将数据同步保存到磁盘
    说Redis很快是相对于关系型数据库如mysql来说的,主要有以下因素

  • 第一,数据结构简单,所以速度快

  • 第二,直接在内存中读写数据,所以速度快
  • 第三,采用多路IO复用模型,减少网络IO的时间消耗,避免大量的无用操作,所以速度快
  • 第四,单线程避免了线程切换和上下文切换产生的消耗,所以速度快 | 1.你常用的Redis的数据存储结构有哪些,他们的使用场景分别是什么
    2.你们项目是怎么用Redis的
    3.怎么防止Redis宕机数据丢失问题
    4.Redis持久化是什么?有几种方式
    5.Redis有了AOF持久化为什么还要RDB?
    6.Redis内存不够了怎么办? | | | —- | —- |

你常用的Redis的数据存储结构有哪些,他们的使用场景分别是什么

Redis存储形式是键值对,支持value形式包括String,List,Set,ZSet,Hash。
set无序不可重复,zset有序不可重复,是对于set的增强
String可以用作缓存,计数器,防攻击,验证码、登录过期等,List可以用来做队列,秒杀等,Set可以用来去重

Redis每种存储结构说 4 个命令吧

1.String

  • set key value 设置值
  • get key 取值
  • mset key value key value… 设置多个值
  • mget key key 获取多个值
  • incr key 将key中的值自增1
  • decre key 将key中的值自减1

2.List

  • lpush key value value… 从最左边设置值
  • rpush key value value… 从最右边设置值
  • lrange key start stop 查询key中指定区间的元素
  • lpop key 移出并返回key中最左边的元素
  • rpop key 移出并返回key中最右边的元素

3.Set

  • sadd key value value 添加元素
  • smembers key 返回集合key中的所有元素
  • srem key member 删除集合key中member元素
  • scard key 查询集合key中的元素数量

4.ZSet

  • zadd key score value (score value)… 添加元素
  • zcard key 查询集合key中元素数量
  • zcount key min max 返回有序集合key中score 在min和max之间的元素
  • zrange key start stop 返回有序集合key中索引在start和stop之间的元素

5.Hash

  • hset key field value 添加元素
  • hget key field 获取key集合中field键对应的值
  • hmset key field value (field value)… 添加元素并批量添加子键值对
  • hmget key field field 获取key集合中所有的子键值对

    你们项目是怎么用Redis的

    使用的是Springboot整合的redis,主要用来解决前后端分离后前后端会话问题,以及验证码的问题

    怎么防止Redis宕机数据丢失问题

    通过对Redis持久化,把内存中的数据和命令,保存一份到磁盘中做备份,当Redis发生宕机,重启服务器的时候,会从磁盘重新加载备份的数据,从而解决数据丢失问题
    做持久化,因为redis是在内存上操作,所以将内存中的数据和命令备份在磁盘中,当redis宕机时,重启服务器将这份备份的数据和命令重新加载,恢复数据.

    Redis持久化是什么?有几种方式

    将内存中的数据备份到磁盘的过程,就叫作持久化
    Redis持久化主要有两种方式,RDB和AOF,可以通过修改redis.conf进行配置
    RDB是记录数据快照,而AOF是记录写命令的

    Redis有了AOF持久化为什么还要RDB?

    AOF和RDB各有所长

  • RDB是记录数据快照,它的优点是只产生一个持久化文件,体积相对较小,启动恢复速度快,备份方便,它的缺点是没办法做到数据百分百不丢失,因为它是每隔一定时间保存一次

  • AOF是记录写命令,它的优点是格式清晰,容易理解,数据更安全,采用append模式即使持久化过程中宕机,也不影响已经保存的数据,它的缺点是文件体积较大,恢复速度慢

根据实际需要来选择,通常二者可以结合来使用

Redis内存不够了怎么办?

方式一:增加物理内存
方式二:使用淘汰策略,删掉一些老旧数据
方式三:集群

1.你们Redis用在哪些业务上?用的什么存储结构
2.淘汰策略有哪些?你们用的哪种
3.Redis事务和Mysql事务的区别
4.使用Redis如何实现消息广播
5.为什么要使用Redis做缓存
6.缓存的执行流程

你们Redis用在哪些业务上?用的什么存储结构

主要用做缓存,比如:验证码,分类缓存,数据字典缓存,权限数据缓存,登录信息缓存等。
String类型的存储结构用的比较多,并且使用了Json格式进行序列化。(降低数据库的性能消耗,提高程序稳定性)

淘汰策略有哪些?你们用的哪种

  • volatile-lru :从已设置过期时间的数据集中挑选最近最少使用的数据淘汰(按照使用频率淘汰:已经设置了过期时间的数据)
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰(按照过期时间找到快过期的数据进行删除)
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰(设置了过期时间随机淘汰策略)
  • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰(按照使用频率淘汰)
  • allkeys-random:从数据集中任意选择数据淘汰(真*随机淘汰)
  • no-enviction:不使用淘汰

    Redis事务和Mysql事务的区别

    Mysql的事务是基于日志,记录修改数据前后的状态来实现的,而Redis的事务是基于队列实现的
    Mysql中的事务满足原子性:即一组操作要么同时成功,要么同时失败,
    Redis中的事务不满足原子性,即一组操作中某些命令执行失败了,其他操作不会回滚
    因此对于比较重要的数据,应该存放在mysql中(mysql的事务基于日志满足事务的原子性,只有一组操作同时成功才会成功,而redis是基于队列实现的不满足事务的原子性,及时一组操作中有命令执行失败也不会回滚事务还是会写进内存,所以较为重要的数据还是保存在mysql中)

使用Redis如何实现消息广播

Redis是使用发布订阅来实现广播的
订阅者通过 SUBSCRIBE channel命令订阅某个频道 , 发布者通过 PUBLISH channel message向该频道发布消息,该频道的所有订阅者都可以收到消息

为什么要使用Redis做缓存

一个字,快。
缓存它指的是将数据库的数据同步到内存中,客户端获取数据直接从内存中获取。由于内存读写速度大于磁盘,而使用缓存能减少磁盘读取,大大提高查询性能。
我们一般会将经常查询的,不会经常改变的热点数据,保存到缓存中,提高响应速度(内存操作速度大于磁盘操作redis数据存储在内存中)

缓存的执行流程

1.客户端发起查询请求
2.判断缓存中是否有数据

  • 如果有,直接返回
  • 如果没有,就从数据库查询,再把数据同步到缓存

3.返回数据给客户端

1.你们怎么保证Redis和Mysql的一致性
2.SpringCache常用注解
3.了解缓存击穿,穿透,雪崩吗?怎么处理?
4.Redis的主从有什么优点,和缺点?
5.解释一下Redis的哨兵模式。哨兵的不足?
6.Redis的cluster集群怎么存储数据的?

你们怎么保证Redis和Mysql的一致性

我们在代码中控制,如果数据库做是写操作,直接把redis中的对应数据删除,下次查询数据会重新写入缓存。
我们的业务对一致性要求不是很高,因此采用了先操作mysql,后删除redis。在写数据库和删除缓存行代码之间如果有查询请求依然会查询到Redis中的老数据,但是这种情况非常极端,而且我们的业务也能容忍这种短暂的脏数据。(缓存与数据库双写一致性:先删缓存 再更新数据库 再延时 删除缓存)

SpringCache常用注解

@EnableCaching:打在主启动类上,开启缓存功能
@Cacheable:打在方法上,表示该方法会开启缓存,打在类上,表示类中所有的方法都开启缓存,方法的返回值会自动写入缓存。如果缓存中已经有数据,方法将不会被调用,而是拿着缓存数据直接返回给客户端。
@CacheEvict:搭载类或者方法上,会将缓存清除
@CachePut:更新缓存
@Caching:组合操作,要应用于方法的多个缓存操作
@CacheConfig:打在类上,共享的一些常见缓存设置

了解缓存击穿,穿透,雪崩吗?怎么处理?

缓存穿透:
出现的原因:大量的请求使用根本不存在的key进行访问缓存,缓存中没有就会造成大量的请求访问数据库造成数据库压力过大崩盘
解决方式
使用布隆过滤器将不存在的key过滤掉
可以将不存在数据 还是以key为键 null为值 保存到缓存中
缓存击穿:
缓存中没有但是数据库中存在的数据,多数情况是某一时刻,某一个key刚好过期,而此时大量的并发请求访问这个key,那么因为都没能从缓存中获取到数据,因此都会访问数据库,造成数据库压力过大。
解决方式:
设置热点数据永不过期
对访问数据库的操作加锁,开启线程睡眠保证每个线程都有足够的时间操作;
缓存雪崩:
缓存中大量的数据同一时间过期,导致大量的请求到达数据库,造成数据库压力过大。
解决方式:
设置热点数据永不过期
设置随机过期时间

Redis的主从有什么优点,和缺点?

优点是读写分离,分担了读的压力,同时能起到备份作用,防止数据丢失
缺点是不能分担写的压力,主的单点故障没有解决,存储没有得到扩容(主节点宕机无法继续使用,从节点宕机还能继续使用,主写从读)

解释一下Redis的哨兵模式。哨兵的不足?

当主服务器中断服务后,可以将一个从服务器升级为主服务器 ,以便继续提供服务
哨兵就是用来监控主从服务器,实现故障恢复功能的。它会不断的检查主服务器和从服务器的健康状态,当某个服务器出现问题时,可以向管理员发起通知。如果主服务器不可用时,会自动选择一个从服务器作为新的主服务器,并让其他的从服务器从新的主服务器复制数据
哨兵也是主从模式,没有解决写的压力,只减轻了读的压力,而且存储也得不到扩容(优点当主服务器中断服务后实时监听服务器,及时提供主服务器用于防止数据丢失,缺点同样也是主从模式,只是减轻了读的压力没有解决根本问题,存储也没有扩容)

Redis的cluster集群怎么存储数据的?

Redis Cluster集群采用哈希槽 (hash slot)的方式来分配的。它默认分配了16384个槽位,当我们set一个key 时,会用CRC16算法得到所属的槽位,然后将这个key 分到对应区间的节点[我儿豁这记得住锤子太抽象了]
(集群后根据crc16算法得到具体的槽点,放置存储这个key)

1.什么情况下Redis集群不可用?
2.Redis存储结构底层有没有了解?什么是SDS
3.Redis如何模拟队列和栈,用什么命令
4.Redis存储单个对象怎么存,存储对象集合怎么存
5.你们Redis用来做什么?使用的什么结构?
6.统计全国高考前20名用什么?
7.从100个VIP用户中随机抽取5名怎么做?

什么情况下Redis集群不可用?

Redis Cluster有一个容错机制,如果半数以上的主节点与故障节点通信都超时了,就会认为该节点故障了,自动触发故障转移操作,故障节点对应的从节点升级为主节点。
但是如果某个主节点挂了,又没有从节点可以使用,那么整个Redis集群就不可用了、
(主节点挂掉并且没有从节点用于代替主节点的情况下就会导致集群不可用)

Redis存储结构底层有没有了解?什么是SDS

简单动态字符串,是Redis自己封装的字符串结构。它记录了字节数组buf,字节数组中用到的字节数len,以及未使用的字节数free。

  • 为了解决二进制安全问题,定义了len来表示已有字符串长度
  • 为了防止缓冲区溢出,在分配内存的时候做了预留空间free
  • 内存惰性释放,多余的内存加入free做预留,优化了内存频繁分配
  • 针对不同的String长度定制了不同的SDS结构

    Redis如何模拟队列和栈,用什么命令

    list控制同一边进,同一边出就是栈;list控制一边进,另一边出就是队列

    Redis存储单个对象怎么存,存储对象集合怎么存

    单个对象可以使用String,也可以使用hash
    集合对象可以使用hash,以便可以快速的通过field来取值

    你们Redis用来做什么?使用的什么结构?

    登录信息login,使用的是String结构存储
    手机验证码code,使用的是String结构
    课程分类course_type ,使用的是String结构

    统计全国高考前20名用什么?

    Zrevrangebyscore (zset自带排序,默认升序)

    从100个VIP用户中随机抽取5名怎么做?

    Srandmember(spop随机获取并删除该数据)
1.RabbitMQ的使用场景
2.RabbitMQ如何防止消息丢失
3.RabbitMQ的交换机有哪几种
4.消息是如何从发送者到达消费者的(RabbitMQ工作流程)
5.如何防止消息重复消费
6.RabbitMQ消息投递失败,你们怎么处理

RabbitMQ的使用场景

rabbitMQ消息队列可以用来

  • 做任务的异步处理,提高程序的相应时间
  • 提高系统稳定性,通过手动确认机制,当消费者出现故障,只要没有确认签收,请求的数据都不会丢失可以再次处理
  • 服务解耦,生产者通过MQ与消费者交互
  • 消除峰值,通过异步处理,消息传到MQ直接返回,接着等待排队处理,避免了线路拥堵
  • (异步处理,通过手动签收防止信息丢失,服务解耦,避免线路堵塞)

    RabbitMQ如何防止消息丢失

    首先,RabbitMQ的消息确认机制,默认是自动签收,也就是说消息一旦被消费者接收,就自动签收,消息就从队列里清除了。因此对于重要的消息,不容丢失的数据,我们需要设置在消费完成后手动签收
    其次,我们可以将消息持久化,避免消息在消费前MQ宕机,网络问题等造成的消息丢失
    (设置手动签收,因为自动签收模式在遇到异常是还是会将消息消费导致消息丢失)

    RabbitMQ的交换机有哪几种

    Fanout:广播,将消息交给所有绑定到交换机的队列
    Direct:定向,把消息交给符合指定routing key的队列
    Topic:通配符,把消息交给符合routing pattern的队列
    (广播交给所有的交换机队列,定向指定特定的消息队列,通配指定满足条件的队列)

    消息是如何从发送者到达消费者的(RabbitMQ工作流程)

    分为消息发送和消息接收两个步骤

  • 消息发送:生产者和Broker建立TCP连接,创建信道。通过信道将消息发送给Broker,由Exchange将消息进行转发到指定的队列

  • 消息接收:消费者和Broker建立TCP连接 ,创建信道 ,然后监听指定的队列,当有消息到达队列时,Broker默认将消息推送给消费者,消费者就能接收到消息
  • (通过建立与broker容器的tcp连接建立信道,绑定交换机指定队列发送或者监听指定队列实现消息发送接收)

    如何防止消息重复消费

    重复消费,一般时由于消费者消费成功后,在给MQ确认签收的时候出现了网络波动,MQ没有接到确认,就会继续给消费者投递之前的消息,造成消费者接收到了两条一样的消息。
    我们可以通过实现消息的幂等性来避免这种情况,比如说让生产者给每个消息携带一个唯一的id,消费者获取消息后根据这个id去查询数据库,如果不存在就正常消费,如果存在了就证明该消息被消费过,直接丢弃
    (原因:因为网络波动导致mq没有接收到信息消费确认信息,解决方式:通过给每个消息产生唯一的id消费者获取到消息后去查数据库id是否存在,不存在的话正常消费,存在的话就证明这条消息被消费过需要丢弃)

    RabbitMQ消息投递失败,你们怎么处理

    我们可以设置confirm回调和 returned 回调
    比如说,可以在发送消息的时候,把消息详情包括交换机名,路由键,都保存到一个表中,状态设置为发送中,如果在confirm方法中ack为false,代表发送到交换机失败 ,就把这个记录状态修改为发送失败
    然后我们创建一个定时任务定时扫表,去读取发送失败的数据并重新发送,为了优化性能,我们设置重试次数3次,如果3次都失败了,我们可以采取人工干预
1.Lucene创建索引原理
2.ES的keyword和text区别
3.ES的优势
4.Lucene/ES为什么那么快(ES用到什么数据结构)
5.ES的分层结构,index下面是什么
6.讲几个ES中的查询对象:比如TermQuery

Lucene创建索引原理

Lucene是基于倒排索引原理来实现的

  • 首先,将原文档进行分词处理,形成一个个单独的单词,
  • 然后取出标点符号以及停词,形成词元,
  • 再将词元做一些语言相关的处理,比如变成小写,转换时态,单复数形式等等,
  • 将得到的词创建一个字典,按照字母顺序排序,合并相同的词,最终生成一个倒排索引文档

    ES的keyword和text区别

    keyword:不分词,直接建立索引,支持模糊查询,精确查询,聚合查询
    text:分词后建立索引,支持模糊查询,精确查询,不支持聚合查询
    keyword通常用于通常用于存储年龄,性别,邮编,邮箱号码等等,直接将完整数据保存的场景
    text通常存储全文搜索的数据,例如地址,文章内容的保存

    ES的优势

    ES是基于Lucene的开源搜索引擎,它解决了原生Lucene使用的不足,优化了Lucene的调用方式

  • 分布式的实时文件存储,每个字段都被索引并可被搜索

  • 支持实时分析搜索
  • 可以扩展到上百台服务器,处理PB级结构化或非结构化数据
  • 通过简单的 RESTful API、可以跟各种语言的客户端甚至命令行进行交互
  • 上手非常容易,只需很少的学习就可以在生产环境中使用

    Lucene/ES为什么那么快(ES用到什么数据结构)

    传统搜索比如mysql的like关键字查询,它的搜索方式就是全文扫表,查询性能很低
    ES是基于Lucene的全文检索引擎,它采用的是倒排索引结构,在存储时先对文档进行分词,再做一些标点符号去除,大小写时态转换等优化处理,最后按照字母顺序去重排序,形成一个倒排索引文档,我们在检索时,就可以通过二分查找的方式找到目标值

    ES的分层结构,index下面是什么

    Index:索引库,包含有一堆相似结构的文档数据,类比Mysql中的数据库
    Type:类型,它是index中的一个逻辑数据分类,类比Mysql中的表
    Document:文档:是ES中的最小数据单元,通常用json结构标识,类比Mysql中的一行数据
    Field:字段:类比Mysql中的一个列
    从ES7.0开始,Type被干掉了,从此库表合一即一个Index中只有一个默认的Type

    讲几个ES中的查询对象:比如TermQuery

    TermQuery:匹配关键字查询(关键词不分词)
    MatchQuery:匹配关键字查询(关键字分词后)
    BooleanQuery:按条件查询
    matchAllQuery:匹配所有文档查询
    rangeQuery:查询指定范围内的数据
1.你简单描述ES的DSL语法
2.你说一下 match和term的区别?
3.你使用过ES的哪些聚合查询?
4.ES高亮怎么做的?
5.你们ES和数据库的数据一致性怎么做的
6.ES分片机制了解吗

你简单描述一下DSL语法

DSL是一种以json形式标识的,由ES提供的一种查询语言,它由两部分组成,DSL查询和DSL过滤。
DSL过滤类似于模糊查询,DSL查询类似于精确查询

你说一下 match和term的区别?

term:不会对搜索词进行分词处理,而是作为一个整体与目标字段进行匹配,若完全匹配,则可查询到
match:会将搜索词分词,再与目标查询字段进行匹配,若分词中的任意一个词与目标字段匹配上,则可查询到

你使用过ES的哪些聚合查询?

指标聚合,比如求和,求最大值,最小值,平均数
数量统计聚合,计算满足条件数据的总条数,相当于sql中的count
去重聚合,它会计算非重复的数据个数,相当于sql中的distinct
桶聚合,它会将某个field的每个唯一值当成一个桶,并计算每个桶内的文档个数,相当于sql中的group by
最高权值聚合,它会匹配每组前n条数据,相当于sql中的group by后取出前n条

ES高亮怎么做的?

使用HighlightBuilder对关键字作高亮处理,由于我们项目使用的是SpringBoot整合ES的jar包,结果没有进行高亮处理,我们使用ElasticsearchTemplate的queryForPage方法来获取结果,再手动进行分页封装返回前台

你们ES和数据库的数据一致性怎么做的

代码控制的,数据库做了写操作,直接更新ES中的数据,我知道可以通过 Logstash 中数据和ES的数据自动同步。
(数据库一旦执行了写操作就将原有的es数据删除重写获取最新的数据保存在es中)

ES分片机制了解吗

ES的索引库由多个分片 shard组成,shard分为primary shard主shad和replica shard 副本,主shard承担写请求,replica副本的数据从primary复制而来,同时分担读请求,primary shard的数量设定了就不能修改,replica数量可以修改。
(分为主副shard,主shad用于承担写请求,副shard用去复制主shad数据备份并分担写压力,主shard不能修改数量,副shard能修改数量)

1.描述一下ES添加文档的过程
2.数据节点存储数据详细流程:
3.详细描述一下Elasticsearch获取文档的过程
4.详细描述一下Elasticsearch搜索过程
5.详细描述一下Elasticsearch更新和删除文档的过程
6.ES有几种节点类型?他们的作用分别是什么

描述一下ES添加文档的过程

(1) 客户端请求一个协调节点coordinating node
(2) 协调节点根据算法选择一个primary shard: 算法 hash(document_id) % (num_of_primary_shards)
(3) 对应的primary shard 所在节点保存完数据后,将数据同步到replica node。
(4) 协调节点coordinating node 发现 primary node 和所有 replica node 都搞定之后返回结果给客户端

数据节点存储数据详细流程:

(1) 当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh
(2) 当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;
(3)在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;

详细描述一下Elasticsearch获取文档的过程

(1) 客户端请求一个协调节点coordinating node
(2) coordinate node 根据算法hash(document_id) % (num_of_primary_shards),将请求转发到对应的 node,此时会使用 round-robin随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡
(3) 接收到请求的 node 返回 document 给调节点 coordinate node。
(4) coordinate node 返回 document 给客户端。
搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch;

详细描述一下Elasticsearch搜索过程

(1) 在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。
(2) 每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。
(3) 每个分片返回各自优先队列中所有文档的 ID 和排序值给协调节点,协调节点它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
(4) 接下来就是 取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

详细描述一下Elasticsearch更新和删除文档的过程

删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更; 磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

ES有几种节点类型?他们的作用分别是什么

分为主节点,node.master =true , 数据节点node.data =true , 负载均衡节点(node.data =false,node.master=false),
node.master=true,代表该节点有成为主资格 ,主节点的主要职责是和集群操作相关的内容,如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。一般会把主节点和数据节点分开
node.data=true,数据节点主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等,数据节点对CPU,IO,内存要求较高,优化节点的时候需要做状态监控,资源不够时要做节点扩充
当主节点和数据节点配置都设置为false的时候,该节点只能处理路由请求,处理搜索,分发索引操作等,从本质上来说该客户节点表现为智能负载平衡器。配置:mode.master=false,mode.data=false

1.ES集群的三种颜色代表什么
2.你们项目怎么使用ES
3.说一下security中的的filter
4.说一下security的认证原理
5.什么是集群
6.什么是负载均衡

ES集群的三种颜色代表什么

绿色,黄色,红色,绿色代表集群健康,所有的主备分片都得到分配,如果有备分片没有node去分配,集群是黄色,黄色和绿色都是可用状态,如果有主分片的节点down机,集群不可写数据,呈现红色,代表集群不健康。

你们项目怎么使用ES

我们使用的是spring-boot-start-data-elasticsearch这个库来操作ES,用在大数据的搜索场景,比如商品的发布,搜索功能。

SpringSeucity

说一下security中的的filter

SecurityContextPersistenceFilter:请求开始会从SecurityContextRepository中获取SecurityContext对象并设置给SecurityContextHolder,在请求完处理成后将SecurityContextHolder持有的SecurityContext再保存到配置好的SecurityContextRepository中,同时清除SecurityContextHolder中的SecurityContext
UsernamePasswordAuthenticationFilter:默认拦截“/login”登录请求,将请求中的认证信息包括用户名,密码封装成UsernamePasswordAuthenticationToken,然后调用AuthenticationManager的认证方法进行认证
BasicAuthenticationFilter:处理 HTTP 请求的 BASIC 授权标头,如果身份验证成功,就把生成的Authentication对象放入SecurityContextHolder。如果设置了记住我,下次访问就不会走这里来了
RememberAuthenticationFilter:记住我,调用RememberMeServices的autoLogin方法自动登录
AnonymousAuthenticationFilter:匿名filter,检测SecurityContextHolder有没有Authentication对象,如果没有,就会创建一个AnonymousAuthenticationToken并保存到SecurityContextHolder
ExceptionTranslationFilter:处理filter链中的所有AccessDeniedException和AuthenticationException
FilterSecurityInterceptor:继承自AbstractSecurityInterceptor,通过调用AccessDecisionManager.decide方法进行授权

说一下security的认证原理

首先,请求会经过UsernamePasswordAuthenticationFilter拦截,请求的用户名密码会封装成UsernamePasswordAuthenticationToken,过滤器将token提交给认证管理器AuthenticationManager进行认证
然后,认证管理器调用AuthenticationProvider进行认证,AuthenticationProvider再调用UserDetailsService获取到数据库中存储的用户信息UserDetails,然后调用密码编码器对密码进行比较,认证成功后封装Authentication
再后来,请求回到UsernamePasswordAuthenticationFilter,调用SecurityContextHolder将Authentication对象封装成SecurityContext并保存到SecurityContextHolder中
最后,请求回到SecurityContextPersistenceFilter,它会调用SecurityContextRepository将SecurityContext对象存储起来,再清理掉SecurityContextHolder中的信息

什么是集群

集群使将应用复制成多个相同的应用,一起来工作,从而提高工作能力。即将多个应用程序分散在不同的服务器,每个服务器都独立运行相同的代码。可以分散服务器压力解决高并发的问题,同时也能预防单节点故障,即一台服务器故障不影响其他服务器正常运行,但没有解决单体应用代码臃肿,业务复杂,维护性差等等问题

什么是负载均衡

使用了集群后,解决高并发同时有一个新的问题,就是客户端的请求如何分配到多台服务。因此需要通过负载均衡器,比如Nginx,使用负载均衡算法比如轮询、权重、随机等等将请求路由到不同的服务器

1.什么是分布式
2.集群和分布式的区别,分别解决什么问题
3.说一下你理解的微服务
4.什么是CAP理论 , 哪些技术用到AP,哪些用到CP
5.什么是强一致性和最终一致性
6.什么是Base理论

什么是分布式

分布式是将应用按照业务类型拆分成多个子应用,每个子应用部署在不同的服务器上单独运行,子应用之间通过API相互调用。
可以分散服务器压力解决高并发问题,同时可以解决单体应用代码臃肿、业务复杂、维护性差等等问题,但是不能防止单节点故障,比如一个子应用故障,整个应用就能不完整运行

集群和分布式的区别,分别解决什么问题

集群是将一个应用程序复制多份,部署在多台服务器上,每个服务器中的程序都是完整的,可以独立运行
分布式是将一个应用程序拆分成多个子程序,分别部署在多台服务器上,每个服务器中的程序都是不完整的,所有服务器需要相互通信相互协调才能完成最终的业务
集群能解决高并发问题,同时能防止单节点故障,即一台服务器宕机不影响其他服务器的正常运行
分布式也能解决高并发问题,但不能防止单节点故障,即一台服务器宕机了,整体业务就无法完成
集群无法解决项目本身的代码臃肿、业务复杂等等问题,分布式能降低模块之间的耦合
实际应用中,我们可以将分布式和集群相结合,比如分布式某个子程序的负载很高,可以单独对这个子程序做集群

说一下你理解的微服务

微服务也是一个分布式系统,它将单体应用进行细粒度拆分,形成多个微服务,每个服务独立运行,每个服务也都可以有自己的数据库,服务之间使用HTTP通信,互相协调完成整个系统的业务。
它的优点是服务之间解耦合,不同的服务可以有不同的编程语言,技术选型多元化,支持敏捷开发
他的缺点是分布式事务很复杂,部署麻烦,技术成本高,服务间通信对性能也有一定的损耗
(我所理解的微服务就是通过不同的服务类型,划分为微服务,每个服务单独运行且连接各自的数据库)

什么是CAP理论 , 哪些技术用到AP,哪些用到CP

CAP理论指的是,在一个分布式系统中,一致性,可用性,分区容错性,三个要素最多只能同时实现两点。
分区容错性是分布式系统的内在要求,因此我们通常会在一致性和可用性之间做取舍。
满足CP,也就是满足一致性和容错性,舍弃可用性,如果系统允许有段时间失效就可以考虑。常见的如Redis,Nacos,ZooKeeper
满足AP,也就是满足可用性和容错性,舍弃一致性,如果系统允许出现短暂时间的不一致可以考虑。常见的如MySQL,Eureka

什么是强一致性和最终一致性

强一致性是只数据在多个副本中总数实时同步的,如果能容忍数据在多个副本中在一定的延迟时间内同步,则是弱一致性
最终一致性则不要求数据什么时候同步,但是最终会同步即可。通常情况下我们在分布式领域选择会牺牲了强一致性,会采用最终一致性

什么是Base理论

Base指的是基本可用,软状态,最终一致性。它是对CAP中的AP的扩展,意思是说当出现故障部分服务不可用时,要保证核心功能可用,允许在一段时间内数据不一致,但最终要保证一致性。满足Base理论的事务也叫柔性事务

1.讲一下你们公司微服务解决方案
2.说一说Spring Cloud有哪些常用组件
3.Spring Cloud的优缺点?
4.什么是服务注册
5.什么是服务发现
6.什么是服务续约
7.如果服务挂了,注册中心要等到90s后剔除,那么在剔除前的这段时间内,挂掉的服务有可能还是会被调用,怎么处理?

讲一下你们公司微服务解决方案,说一说Spring Cloud有哪些常用组件

我们公司使用到的是第一代微服务方案,使用springcloud Netflix 全家桶
eureka:服务注册中心,用于注册服务,发现以及解决服务间的通讯问题
zuul:服务网关,整个微服务的大门可以实现认证,权限等业务…
ribbon/openfegin:实现服务掉用的负载均衡,解决微服务之前的调用以及请求分发到具体微服务的问题
hystrix:熔断降级,解决单点故障,单点故障产生原因:单个微服务挂掉导致整个项目运行出错
config:分布式配置中心,用于统一管理微服务的配置文件
bus:消息总线,给个个微服务广播消息,实现个个微服务配置自动刷新
sleuth:链路追踪,用于实时监听每个微服务间的调度关系,快速定位单点故障

Spring Cloud的优缺点?

优点:个个服务拥有自己的运行环境,避免了服务间的耦合,便于维护;使用http通讯机制,不同服务支持跨语言开发(不同的服务可以使用不同的语言开发);有极强的拓展性,可以做集群,业务量大的服务还能再拆分微服务;
缺点:成本高,部署麻烦,分布式事务繁琐.微服务之间的调用非常消耗性能

什么是服务注册

Eureka是一个服务组测与发现的组件,翻译成人话就是管理所有微服务的通讯录的组件。它包含注册中心,客户端两部分组成。客户端在启动的时候会向注册中心发送一条自我介绍信息,比如端口,ip等等,在注册中心就会保存一张所有微服务的通讯录。这就叫服务注册

什么是服务发现

微服务会定期的从客户端拉取一份微服务通讯录,到本地缓存起来,默认是30s一次。当一个微服务向另一个微服务发起调用,直接根据本地的通讯录找到对方的服务名,发送HTTP请求。这个就叫服务发现

什么是服务续约

微服务会定时(默认30s)发送心跳请求,告诉注册中心,自己还处于存活状态,那么服务中心就不会将其从清单中删除,否则,当微服务宕机或者网络故障等因素,没有在规定时间(默认90s)内提交心跳请求,注册中心就会将它从通讯录中删除。

如果服务挂了,注册中心要等到90s后剔除,那么在剔除前的这段时间内,挂掉的服务有可能还是会被调用,怎么处理?

第一,可以修改注册中心剔除服务时间,同时加快服务续约心跳请求的频率
第二,可以使用Hystrix的熔断降级机制,当某个服务不可访问,快速失败,并返回托底数据
第三。重试,提供者集群

1.你知道EurekaClient服务发现和服务续约每隔30s做一次请求是用什么技术实现的吗?
2.Ribbon是什么,Ribbon的工作原理讲一下
3.说一下 Ribbon的工作原理
4.Ribbon有哪些负载均衡算法,怎么配置
5.OpenFeign和Ribbon的区别
6.OpengFiegn的工作流程

你知道EurekaClient服务发现和服务续约每隔30s做一次请求是用什么技术实现的吗?

使用到了线程池定时任务来实现敷服务发现(scheduledthreadpoolexcutor),服务发现功能默认开启,然后获取定时任务的时间间隔(默认30s),初始化服务发现的定时任务然后去发现服务;
服务续约需要保证服务注册功能是开启的(默认开启),获取定时任务,然后根据心跳请求实现服务续约
(心跳请求:服务每隔30秒向服务注册中心发送心跳请求,告诉服务注册中心自己还活着,如果服务30s没有向注册中心发送心跳请求,那么注册中心会任务该服务已经挂掉并且不再续约,将该服务从注册中心列表中剔除)

Ribbon是什么,Ribbon的工作原理讲一下

ribbon是一个客户端负均衡器载,用于实现微服务调用时的负载均衡(负载均衡策略:轮询,权重,随机),在我看来该组件就是用于减轻集群服务的压力,防止过多请求访问一个服务造成服务宕机

说一下 Ribbon的工作原理

Ribobn内部通过LoadBalancerInterceptor拦截RestTemplate发起的请求,然后交给RibbonLoadBalancerClient负载均衡客户端做负载均衡,RibbonLoadBalancerClient把选择服务的工作交给ILoadBalancer负载均衡器 ,ILoadBalancer会调用 IRule负载均衡算法类来选择服务。之后RibbonLoadBalancerClient把选择好的服务交给LoadBalancerRequest去发请求。
(ribbon通过内部的loadbalancerinterceptor拦截resttemplate发送的请求然后交给负载均衡客户端做负载均衡处理[ribbonloadbalancerclient],负载均衡器客户端将服务选择的工作交给负载均衡器[iloadbalancer],负载均衡器使用irule负载均衡算法来选择服务,然后负载均衡客户端将选择好的服务交给LoadBalancerRequest发送请求调用指定服务)

Ribbon有哪些负载均衡算法,怎么配置

RoundRobinRule:简单轮询,ribbon默认规则
AvailabilityFilteringRule:忽略短路状态和并发过高的服务器
WeightedResponseTimeRule:根据服务器响应时间作为权重,响应时间越长权重越小
ZoneAvoidanceRule:根据区域选择
BestAvailableRule:忽略短路的服务器,选择并发较低的服务器
RandomRule:随机选择一个可用服务器
Retry:重试机制的选择逻辑
配置:自定义配置类,通过创建对象的方式实现不同的负载均衡算法再将这个配置类交给spring管理,通过@RibbonClient(name = “springboot-eureka-clent”, configuration = MyConfiguration.class)注解将自定义的负载均衡策略配置给eureka实现自定义配置负载均衡算法以策略

OpengFiegn的工作流程

首先,当程序启动时,@EnableFeignClient会扫描@FeignClient注解的接口,并交给Spring容器管理。
当发起请求时,会使用jdk动态代理,并为每个方法都生成相应的RequestTemplate,同时封装http信息,包括url和请求参数等,
最后把RestTemplate交个HttpClient发送请求,使用ribbon的负载均衡发起调用
程序启动,@EnableFeignClien注解的开启fegin会去找到具体所有的@FeignClient的接口,交给spring
管理然后请求发起时会使用jdk动态代理,每个方法上都生成相应的requesttemp,封装好请求信息(url以及参数),最后通过HttpClient发起请求由ribbon实现负载均衡

我在项目中使用Fiegn的方式是创建一个包,将需要调用的接口方法拷贝到自定义的feginclient接口中,再实现fallbackfactory实现托底数据,方式其他接口不能使用,在通过注入feginclient的方式执行对应服务的接口,当然fegin的基础配置不能少(依赖,启动类的注解)

1.为什么要使用Eureka 为什么要使用Ribbon 为什么要使用config配置中心
2.什么Feign的客户端接口没有写实现类也可以直接被依赖注入
3.介绍一下Hystrix
4.什么是熔断,什么是降级
5.什么是资源隔离?
6.资源隔离:信号量和线程池的区别

为什么要使用Eureka 为什么要使用Ribbon 为什么要使用config配置中心

在微服务系统中,各个服务之间是需要进行网络通信的,那么他们相互调用就得知道对方的通信地址。eureka就是专门来做做服务注册与发现,解决服务之间通信问题的
当一个微服务做了集群,也就是同一个服务名会对应多个地址,那么我们在调用的时候,应该调用哪一个就成了问题,Ribbon是一个负债均衡器,它可以按照负债均衡算法,向多个服务发起调用。当一个微服务有多个集群时,就可以使用它做请求的分发
在微服务系统中,服务数量很多,而每个服务都有自己的配置文件,管理起来很麻烦。用了配置中心就可以帮我们集中管理配置文件,它支持本地配置文件,也支持将配置文件放到远程仓库如git集中管理
Ribbon :解决具体调用集群中哪一个服务的问题;
config配置中心:实现个个服务的配置文件集中管理

为什么Feign的客户端接口没有写实现类也可以直接被依赖注入

自动注入的实例其实是一个jdk动态代理对象,Feign会为每个方法生成相应的requestTemplate,它根据服务名找到对应的服务,根据返回值类型、形参列表匹配相应的接口,然后封装url、请求参数,最后生成request请求,使用Ribbon负载均衡发起调用
实质上通过依赖注入产生的是jdk动态代理的对象,Feign会为每个方法生成requesttemp,先根据服务名称找到具体服务,然后根据方法名称,url,参数….去指定具体的接口,生成具体的requset请求,再由ribbon实现负载均衡

介绍一下Hystrix

Hystrix意为熔断器,它可以将出现故障的服务,通过熔断、降级等手段隔离开,这样不影响整个系统的主业务。它可以防止由单节点异常导致整个微服务故障,如果遇到故障时,快速失败,熔断的同时可以返回兜底数据达到服务降级的目的
Hystrix是springcloud组件,是熔断器,目的是为了将出现了故障的单个服务导致整个项目的业务功能运行异常,具体实现方式:,通过熔断、降级等手段隔离开故障服务,保证整个项目顺利运行

什么是熔断,什么是降级

熔断:就是服务的一种保护机制,当链路上的服务不可访问,服务触发降级,当同步失败率(服务访问不成功)
达到一定阈值时标记该服务为短路,直接熔断禁用该服务,然后

什么是资源隔离?

指的是限制某一个分布式服务的资源使用,可以理解为限流,也就是限制某个服务的请求数量。它包括线程池隔离和信号量隔离
线程池隔离,是指用一个线程池来存储当前请求,可以通过设置线程池最大线程数和最大排队队列数来限制请求数量
信号量隔离:是指用一个计数器来记录当前有多少个线程在运行,请求进来计数器就增加1,超过最大信号量,就直接返回

资源隔离:信号量和线程池的区别

线程池方式是异步处理,它与调用线程不是同一个线程
信号量方式是同步处理,与调用线程是同一个线程
线程池方式由于需要排队,调度,线程切换,因此开销较大,信号量方式无需切换线程,开销较小

1.对于CAP理论,Eureka选择的是AP还是CP?它保证了一致性还是可用性?
2.说一下Eureka的自我保护
3.你们项目是如何做服务降级的?
4.Zuul有哪几类Filter,他们的执行顺序是怎么样的?
5.在Zuul中做登录检查如何实现?
6.在Zuul中如何做限流?

对于CAP理论,Eureka选择的是AP还是CP?它保证了一致性还是可用性?

CAP理论指的是,一个分布式系统中,一致性,可用性,分区容错性,三个要素只能同时实现两点。Eureka选择的是AP,它是弱一致性的,保证了可用性和分区容错性,放弃了数据一致性。也就是说当多个Eureka之间不可通信时,需要保证服务可用,正常提供服务注册发现功能,但是网络恢复后最终还是会同步的。

说一下Eureka的自我保护

为了防止服务被误删除,Eureka不会立即删除过时的服务数据。这种机制可能会导致客户端从注册中心获取到已经下线的服务并发起调用而导致错误,因此在开发阶段我们可以关闭自我保护机制。在生产环境中,我们需要打开自我保护,因为它可以防止因为网络波动,服务没有及时续约而造成的服务误删除问题。

你们项目是如何做服务降级的?

比如在秒杀业务中,需要实时从redis中查询库存,通过设置hystrix的最大信号量,以此来防止redis雪崩。当并发过高,请求数超过最大信号量,触发降级,直接向客户端返回兜底数据:”活动太火爆啦,请骚后重试“

Zuul有哪几类Filter,他们的执行顺序是怎么样的?

zuul按照执行顺序,分为pre前置过滤,route路由过滤,post后置过滤,error异常后过滤
正常流程是请求先经过前置过滤器,到达路由过滤器进行路由,路由到各种微服务执行请求,返回结果后经过后置过滤,返回用户
异常流程,如果再整个过程中出现异常,都会进入error异常过滤器,处理完毕后经过post过滤器返回用户,如果error自己出现异常,最终也会通过post过滤器返回用户,如果post过滤器出现异常,也会跳转到error过滤器,然后直接返回用户

在Zuul中做登录检查如何实现?

可以通过继承ZuulFilter抽象类,自定义pre类型的过滤器,shouldFilter方法中可以定义需要放行的资源,run方法中检查请求头中的token信息,如果没有token,就响应到客户端未登录的信息,并组织filter继续往后执行

在Zuul中如何做限流?

方式一:可以通过继承ZuulFilter抽象类自定义pre过滤器,加上限流算法,来实现
方式二:可以通过hystrix的资源隔离模式,设置线程池最大连接数或者最大信号量来实现
方式三:常用,Ratelimit,使用令牌桶算法。。。

1.配置中心解决什么问题?
2.EureakServer的搭建流程
3.Ribbon的整合流程
4.Feign的整合流程
5.Hystrix的整合流程
6.Zuul的整合流程
7.你们ZUUL做了写什么

配置中心解决什么问题?

在分布式系统中,服务数量很多,而每个服务都有自己的配置文件,管理起来很麻烦。配置中心是个好东西,可以帮我们集中管理配置文件,它支持本地配置文件,也支持将配置文件放到远程仓库如git集中管理。

EureakServer的搭建流程

第一步,导入eureka-server依赖,以及springboot的web环境依赖。
第二布,主启动类上打注解,@EnableEurekaServer,开启eureka服务端功能
第三步,yml配置文件中,配置注册中心的端口号,主机名,注册中心地址

Ribbon的整合流程

第一步,导入ribbon依赖
第二部,给RestTemplate的Bean定义方法上,加上注解@LoadBalanced,让这个restTemplate有负载均衡的功能
第三步,修改restTemplate调用服务的url,将目标主机名换成目标服务名

Feign的整合流程

第一步,导入openfeign依赖
第二部,主配置类加注解,@EnableFeignClients,开启feign支持
第三步,定义feign客户端接口,并加上注释@FeignClient(“目标服务名”),接口中定义方法,该方法与目标服务的对应方法的方法名,返回值类型,形参列表,url路径要一致

Hystrix的整合流程

  • 第一步,导入hystrix依赖
  • 第二部,主启动类加注解,@EnableCircuitBreaker,开启熔断功能
  • 第三步,在需要开启熔断功能的方法上,加注解@HystrixCommand(fallbackMethod=”xxx”),xxx是降级方法
  • 第四步,定义降级方法,方法名需要和fallbackMethod的值一致,形参列表和返回值类型需要和目标方法一致

feign整合Hystrix:

  • 第一步,yml中配置,feign.hystrix.enable=true,开启hystrix功能
  • 第二部,@FeignClient标签中,定义fallback或者fallbackFactory,指定降级类
  • 第三步,

如果是fallback,就实现feign接口,并覆写接口中的方法作为降级方法
如果是fallbackFactory,就实现FallbackFactory接口,同时指定泛型为feign接口,覆写create方法,返回一个feign接口的匿名内部类,类中写降级方法

Zuul的整合流程

第一步,导入zuul依赖
第二步,主启动类上加注解@EnableZuulProxy,开启zuul功能
第三步,yml中配置,统一访问前缀prefix,禁用通过服务名方式访问服务ignoredServices,配置路由routes指定某个服务使用某个路径来访问
你们ZUUL做了写什么

1.ConfigServer的整合流程
2.你们微服务项目的技术栈描述一下
3.浏览器发起一个请求,在你的微服务项目中的怎么去执行的?
4.说下Ribbon和Feign的区别呢
5.Spring,SpringBoot和SpringCloud的关系以及区别
6.什么是分布式事务,

ConfigServer的整合流程

配置中心服务端配置:
第一步,导入config-server依赖
第二步,主启动类加注解,@EnableConfigServer,开启配置中心
第三步,配置文件中,配置远程仓库地址,仓库账号密码
客户端配置:
第一步,导入config-client依赖
第二步,创建bootstrap.yml配置文件,配置中心地址config.uri,要拉取的配置文件名name,环境名profile

你们微服务项目的技术栈描述一下

  1. 前端门户系统:HTML + JQuery + CSS<br /> 前端管理系统:VUE + ElementUI<br /> 后端系统:基于SpringCloud微服务框架(Eureka+OpenFeign+Hystrix+Zuul+Config)<br />+MyBatisPlus+SpringMVC+Redis+ElasticSearch+RabbitMQ+AlicloudOSS

浏览器发起一个请求,在你的微服务项目中的怎么去执行的?

浏览器发起的所有请求首先通过Nginx,通过负载均衡算法,路由给zuul集群,然后通过zuul前置过滤,作登录校验后,它会从配置中心拉取的通讯地址中,根据url匹配到对应的服务,然后使用ribbon发起restful调用。微服务间也可以通过feign相互调用,最终执行完任务,返回浏览器

说下Ribbon和Feign的区别呢

Ribbon和Feign都是SpringCloud Netflix中实现负载均衡的组件,不同点在于
Ribbon是需要我们手动构建http请求,根据目标服务名通过负载均衡算法直接调用目标服务,
Feign是采用接口的方式,将需要调用的目标服务方法定义成抽象方法,路径,服务名,形参列表,返回值类型需要保持一致。我们只需要调用接口中的方法就可以了。它会自动帮我们生成jdk动态代理,为每个方法生成RequestTemplate并封装url和请求参数,使用负载均衡算法发起调用
Ribbon的实现方式,一般配合RestTemplate发起http请求,我们需要在注册RestTemplate的Bean的方法上加@LoadBalanced,使它具有负载均衡的能力
Feign的实现方式,是在主启动类上加@EnableFeignClients,在客户端接口上加注解@FeignClient

Spring,SpringBoot和SpringCloud的关系以及区别

Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度,面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务如事务,日志进行集中管理。
Springboot是一个基于spring的框架,对spring做了大量简化,使开发流程更快,更高效。比如它大量简化maven依赖,基于注解配置(JavaConfig)无需XML,内嵌Tomcat,部署流程简单,打包和部署更加灵活,允许独立运行
SpringCloud是基于SpringBoot实现的,用于微服务架构中管理和协调服务的,它是一系列框架的有序集合,它为开发者提供了一系列工具,例如服务发现与注册,配置中心,网关,负载均衡,熔断器,链路追踪等等,让微服务架构落地变得更简单

分布式事务

什么是分布式事务,

分布式事务,指的是在分布式环境中,一个请求可能涉及到对多个数据库的写操作,要保证多数据库的一致性就需要用到分布式事务

1.分布式事务你知道哪些解决方案? 这些方案如何选型
2.什么是2pc
3.Seata相比传统XA方案2PC有什么区别,以及优点?
4.Seata的TC,TM,RM的含义,以及作用?
5.解释一下Seata的工作原理
6.你能简单描述一下你在项目中是如何集成Seata的吗

分布式事务你知道哪些解决方案? 这些方案如何选型

常见的分布式事务解决方案,2PC,TCC,可靠消息最终一致性,最大努力通知
2PC,它将整个事务流程分为两个阶段,P指的是准备阶段,C指的是提交阶段。它是一个阻塞协议,不适用于并发较高,事务生命周期长的分布式事务。
TCC,它是基于补偿性事务的AP系统的一种实现,补偿也就是说先按照预定方案执行,如果失败了就走补偿方案。它可以自己定义数据操作的粒度,但是对应用的侵入性强,可以用在登录送积分,送优惠券等等场景
可靠消息最终一致性,指的是当事务发起方执行完本地事务后,就发出一条消息通知其他参与方,并且他们一定能接收到消息并处理事务。适合执行周期长,并且实时性要求不高的场景
最大努力通知,是在不影响主业务的情况下,尽可能的保证数据的一致性,它适用于一些最终一致性敏感度低的业务,比如支付结果通知

什么是2pc

2PC,是将整个事务流程分为两个阶段,P指的是准备阶段,C指的是提交阶段。它常见的标准有XA,JTA,Seata
由DTP模型定义事务管理器TM和资源管理器RM之间通讯的接口规范叫做XA,它规定的交互方式是酱紫的:应用程序AP通过TM提交和回滚事务,TM通过XA接口来通知RM数据库事务的开始,结束,提交,回滚
2PC能保证分布式事务的原子性,但是也有很多缺陷
比如,在第一阶段,如果参与者迟迟不回复协调者,就会造成事务的阻塞,性能不好
比如,在第二阶段,如果事务协调者发出提交事务指令后宕机,一部分参与者收到消息提交了事务,另一部分没有收到消息没有提交事务,这就会导致数据不一致
再比如,在第二阶段,如果事务协调者发出提交事务指令后宕机,收到指令的参与者也宕机了,我们就不能确定事务的执行结果,究竟有没有提交

Seata相比传统2PC有什么区别,以及优点?

Seata是由阿里中间件团队发起的开源项目Fescar更名而来,是一个开源的分布式事务框架,它通过对本地关系数据库的分支事务协调,来驱动完成全局事务
Seata的主要优点是性能好,不会长时间占用链接资源,对业务零入侵
与传统的2PC的区别主要两方面
在架构层次方面,传统的2PC方案的RM本质就是数据库自身,而Seata的RM是以jar包形式作为中间件层部署在应用程序上
在两阶段提交上方面,传统2PC方案是在第二阶段完成才释放资源,而Seata是在第一阶段就将本地事务提交,提高了效率

Seata的TC,TM,RM的含义,以及作用?

TC:事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各各分支事务的提交或回滚
TM:事务管理器,TM需要嵌入应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令
RM:控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分支事务的提交和回滚

你知道TCC吗,它有什么样的优缺点?

TCC是基于补偿型事务的AP系统的一种实现。补偿指的先按照事先预定的方案去执行,如果失败了就走补偿方案
它的优点是异步执行效率高,它能对分布式事务中的各个资源分别锁定,分别提交与释放
它的缺点是对应用的侵入性强,改动成本高,实现难度大
####

解释一下Seata的工作原理

Seata有三个角色:

  • TM任务管理器,负责开启,提交,回滚事务的发起,
  • TC事务协调器 ,接收TM的指令通知RM提交或者回滚事务
  • RM资源管理器,控制着分支事务的提交和回滚

假设有服务A需要调用服务B,且两个服务都需要修改各自的数据库,A服务作为程序入口充当TM和RM,B服务控制着分支事务充当RM。

  • A服务的TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID
  • A服务的RM向TC注册分支事务,并将其纳入XID对应全局事务的管辖
  • A服务执行分支事务,写undolog日志,向TC上报事务状态
  • 当调用B服务时,B服务的RM向TC注册分支事务,该分支事务执行,然后写undolog,向TC上报事务状态
  • 服务执行完毕A服务的TM向TC发送commit或者rollback指令
  • TC接收到指令,向参与事务的RM发送指令
  • 事务参与者RM受到commit指令,删除undolog日志。 如果是rollback指令就根据undolog回滚

    你能简单描述一下你在项目中是如何集成Seata的吗

    事务协调器:安装并启动Seata客户端
    主业务端:

  • 第一步,导入Seata依赖

  • 第二步,yml中配置事务组名,同时需要添加配置文件file.conf,registry.conf,需要注意yml中事务组名与file.comf中的事务组名一致
  • 第三步,配置DataSource,需要适用Seata对DataSource进行代理
  • 第四步,数据库中添加undo log日志表
  • 第五步,业务方法上加注解@GlobalTransactional(rollbackFor = Exception.class)注解

事务参与者:

  • 前四步与主业务端相同,第五步不需要了 | 1.你说一下什么是分布式锁
    2.分布式锁有哪些解决方案
    3.Redis如何实现分布式锁,用什么命令
    4.你项目中怎么使用分布式锁的
    5.了解Redission的看门狗原理吗?
    6.你在项目中如果使用ZK实现分布式锁的? | | | —- | —- |