面试汇总--初中级

1.Java、集合 

反射,静态代理和动态代理,Java动态代理和Cglib动态代理的区别?**** 

反射可以在运行时动态生成对象,获取对象属性,调用对象方法例如:Spring框架中Bean的创建

静态: 代理类是自己手工实现的,自己创建一个Java类,表示代理类。同时你所要代理的目标是确定的。

动态: jdk运行期间,动态创建class字节码并加载到JVM  

JDK动态代理是利用反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。实现InvocationHandler接口  

  • 业务目标对象实现接口 (要求)
  • 实现 InvocationHandler 接口
  • 使用 Proxy.newProxyInstance() 方法生成代理对象

cglib动态代理是利用asm开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。实现MethodInterceptor接口。

如果目标对象生成了接口,默认情况下会采用 JDK 的动态代理实现 AOP,也可强制使用 CGLIB 实现 AOP,如果目标对象没实现接口,必须采用 CGLIB

1.8的时候JDK的效率已高于cglib  

1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,用cglib实现  

深拷贝和浅拷贝 

浅拷贝只复制指向对象的指针,不复制对象本身,新旧对象共享同一块内存。但深拷贝(clone())会另造一个一样的对象,新对象跟原对象不共享内存,修改新对象不改变原对象。

static的应用场景 

修饰成员变量,修饰成员方法,静态代码块,修饰静态内部类,静态导包

String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?(final)

可变性:String使用字符数组保存字符串,private  final  char value[],string对象是不可变的。StringBuilder与StringBuffer继承自AbstractStringBuilder类,AbstractStringBuilder中也是使用字符数组保存字符串 char[] value,所以两种对象都可变的。

2.线程安全性:String中的对象是不可变的,可以理解为常量,所以线程安全;AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁synchronized关键字或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

3.性能:String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer 每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程的不安全。

final和finally,finalize有啥区别?

final修饰的类,不能派生出新子类,不能作为父类被子类继承。一个类不能既被abstract声明,又被final声明。将变量或方法声明final,可以保证在使用的过程中不被修改。被声明final的变量必须在声明时给变量赋初始值,在以后引用中只能读取。被final声明的方法也同样只能使用,不能重写。
finally在异常处理时提供finally块来执行清除操作.不管有没有异常抛出、捕获,finally块都被执行。try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳到catch块执行。finally块则异常是否发生,都会执行finally块的内容,所以代码逻辑中有需要无论发生什么都必须执行的代码,可放在finally块中。

finalize是方法.java允许用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的.它在object类中定义,所有的类都继承它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
 

== 与 equals,hashcode 相同吗?为什么要同时重写equals和hashcode?

 基本数据类型,==比较的是值,没有equals()方法。对于引用数据类型来说,==比较的是对象的内存地址。 没有重写equals方法时,equals等同于==  

  • 如果两个对象通过equals()方法比较,判定相等,那么,调用二者各自的hashCode()方法必须产生同一个hash值。如果两个对象通过equals()方法比较,判定不相等, hashCode()方法可能相等也可能不等。

Object类的常见方法

object 类是根类,所有类直接或间接继承该类;public int hashCode();返回对象的哈希码值。不同对象的,hashCode()一般来说不会相同。同一个对象的hashCode()值肯定相同。public final Class getClass();返回此 Object 的运行时类,可以通过Class类中的一个方法,获取对象的真实类的全名称: public String getName()。 public String toString();返回该对象的字符串表示。一般建议重写该方法。然后调用对象的toString()方法。object类的equals()方法:指示其他某个对象是否与此对象“相等”。默认情况下比较对象的引用是否相同,重写后用于比较成员变量值是否相等

Java 中的异常处理,举几个遇到的异常?

ClassCastException      两个类型间转换不兼容引发的运行异常
ArrayIndexOutOfBoundsException 数组越界
NullPointerException      空指针异常
ArithmeticException   算术异常
NumberFormatException    数字格式异常
InputMismatchException   输入不匹配异常

接口和抽象类的区别是什么

抽象类:构造方法:有构造方法,用于子类实例化使用。
成员变量:可以是变量,也可以是常量。
成员方法:可以是抽象的,也可以是非抽象的。

接口:构造方法:没有构造方法
成员变量:只能是常量。默认修饰符:public static final
成员方法:jdk1.7只能是抽象的。默认修饰符:public abstract (推荐:默认修饰符请自己永远手动给出) jdk1.8可以写以default和static开头的具体方法
 

Arraylist 与 LinkedList ,Vector异同?

相同点:三个类都实现了List接口,存储数据的特点相同:有序,可重复。
不同点:
ArrayList:作为List集合的主要实现类,线程不安全,但执行效率高,底层使用Object数据来存储数据(Object[] elementData;),查询比较快;
Vector:作为List集合的古老实现类,线程安全,但是执行效率低,底层也使用Object数据来存储数据(Object[] elementData;),底层许多方法都用了synchronized关键字,所以线程安全,执行效率低。
LinkedList:底层使用了双向链表来存储数据,频繁的插入和删除操作效率高。
LinkedList核心代码(双向链表):
    private static class Node<E> {
      E item;	//存储数据
      Node<E> next;	//下一个节点
      Node<E> prev;	//上一个节点
 
      Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
      }
    }

使用HashMap时候,用String做Key有啥好处?

final修饰不可变性,线程安全

HashMap的底层实现 (1.7和1.8的区别),说一下put方法?

initailCapacity是初始容量:默认值为16   负载因子:默认值为0.75  

1.根据key通过哈希算法与运算得出数组下标

2.如果数据下标位置元素为空,则将key和value封装为Entry对象(JDK1. 7是Entry对象,JDK1.8是Node对象)并放入该位置

3.如果数组下标位置元素不为空,则要分情况讨论

如果是JDK1.7,则先判断是否需要扩容,如果大于12就进行扩容。每次扩容时,都是直接在其现有容量基础上直接乘2如果不用扩容就生成Entry对象,并使用头插法添加到当前位置的链表中
如果是JDK1.8,则会先判断当前位置上Node的类型,看是红黑树Node,还是链表Node 
如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value 如果此位之上的Node对象是链表节点,则将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会哦判断是否存在当前key,如果存在则更新value,遍历链表后,将新链表Node插入链表中,会看到当前链表的节点个数,如果链表大于等于8并且数组长度超过64,会将该链表转为红黑树   ,将key和value封装为Node插入到链表或者红黑树中后,在判断是否需要进行扩容,如果需要就扩容,如果不需要就结束put方法

HashMap 的长度为什么是2的幂次方

减少Hash碰撞,使Hash算法结果均匀  位运算 会更快

HashMap 多线程操作导致死循环问题

jdk1.8之前,因为多线程同时扩容,向链表添加元素时采用的是头插法,多线程操作链表会发生环化,产生死循环。1.8之后是链表转换树或者对树进行操作(重新平衡红黑树)时出现死循环问题。且 多线程情况使用hashMap 还会有数据丢失的问题(因为各线程之间的 map 是不可见的),所以 多线程情况下 建议使用 concurrentHahsMap。

HashSet 和 HashMap 区别

1.实现的接口不同 HashSet实现的是Set接口,HashMap实现的是Map接口
2.存储内容不同 HashSet存储的是对象,HashMap存储的是键值对
3.添加元素的方法不同 HashSet是通过add()方法添加元素 HashMap是通过put()方法添加元素
4.计算hashCode的方式不同 HashMap是通过Key来计算hashCode值 HashSet是通过成员变量来计算hashCode值,对于两个对象来说,hashCode值可能相同,所以通过equals()来判断对象的相等性,如果两个对象不相等则返回false 
 HashSet的构造方法底层都是调用 HashMap的构造方法 

ConcurrentHashMap 和 Hashtable ,HashMap的区别

1.线程安全不同  HashMap是非线程安全的 ,Hashtable  ConcurrentHashMap是线程安全的,多线程环境下可用;
 2.继承的父类不同 HashMap继承自AbstractMap类。但二者都实现了Map接口。
Hashtable继承自Dictionary类,Dictionary类是一个已经被废弃的类,没用了
3.包含的contains方法不同 HashMap是没有contains方法的,而包括containsValue和containsKey方法; hashtable则保留了contains方法,效果同containsValue,还有containsValue,containsKey。
4.是否允许null值 Hashmap是允许key和value为null值的, HashTable键值对都不能为空,否则包空指针异常。
5.计算hash值方式不同  HashMap有个hash方法重新计算了key的hash值,因为hash冲突变高,所以通过一种方法重算hash值的方法:这里计算hash值,先调用hashCode方法计算出来一个hash值,再将hash与右移16位后相异或,从而得到新的hash值。
Hashtable通过计算key的hashCode()来得到hash值就为最终hash值。
6.解决hash冲突方式不同  HashMap中,当出现冲突时 链表+红黑树
HashTable中, 都是以链表方式存储。

 

ConcurrentHashMap线程安全底层实现(1.7和1.8)

JKD1.8以前ConcurrentHashMap由Segment和HashEntry组成。一个ConcurrentHashMap里包含一个Segment数组,一个Segment又包含一个HashEntry。将数据分为一段段来存储,然后给每一段数据分配一把锁,当一个线程占用锁访问一段数据时,其他数据也能被其他线程访问。
1.8之后采用synchronized和CAS来保证并发安全:数组+链表+红黑树。Synchronized只锁定当前链表或红黑树的首节点,这样只要hash不冲突就不会产生并发。默认 Segment 的个数是 16 个

Java新特性(8,9,10,11...) 

1.8:

  • Lambda 表达式 − 允许把函数作为一个方法的参数(函数作为参数传递到方法中),只能使用一次。只能简化函数式接口的匿名内部类的写法。@FunctionalInterface

  • 方法引用 −  直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,更紧凑简洁,减少冗余代码。

  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • Stream API −新添加的Stream API(java.util.stream) 真正的函数式编程风格引

  • Date Time API − LocalDate/LocalTime 和 LocalDateTime 类增加线程安全

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

java10: var关键字:局部变量类型推断;java11:新增字符串处理的方法,复制判断和空白字符串等。HttpClient替换原有的HttpURLConnection。

2.数据机构与算法(思路)

二分查找法

class Solution {
    public int search(int[] nums, int target) {
        int low = 0, high = nums.length - 1;
        while(low <= high){
          int mid = (high-low)/2 + low;//二分,划为两个区域
          int num = nums[mid];
          // num = target时,要找的下标即为mid;
          if(num == target){ 
              return mid;
          }else if(num<target) { // num<target时,说明要找的target的下标在num的右侧,故而将最低点设为原来的mid+1
              low = mid+1;
          }else{
              high = mid-1; //num>target时,说明要找的target的下标在num的左侧,故而将最高点设为原来的high-1
          }
        }
        return -1;//找不到目标值时,返回-1;
    }    
}

快速排序(优化) 分治

1.从序列中选择一个轴点元素pivot从最后一个元素向前遍历我们的策略是:每次选择第0位置的元素为轴点元素2.利用pivot将数组分割成2个子数组将小于pivot的元素放在pivot的左侧将大于pivot的元素放在pivot的右侧将等于pivot的元素放在pivot的哪侧都可以,本文选择左侧3.对子序列进行步骤1和步骤2操作直到不能再分割(子序列中只剩下一个元素)

冒泡排序(优化),归并排序

选择排序,插入排序,希尔排序

链表反转(迭代和递归)

环形链表(set和快慢指针)

统计素数个数(暴力和埃氏筛选)

删除数组重复项(双指针)

二叉树遍历(前中后层)

二叉树最小深度(广度和深度)

斐波那契数列第n位的值(暴力递归,双指针)

3.多线程与并发

线程的生命周期? 新建,就绪,运行,阻塞以及死亡

AQS 原理 AQS 对资源的共享方式  AQS底层使用了模板方法模式

抽象队列同步器  

如果被请求的共享资源空闲,则将当前请求资源的线程归为有效工作线程,并且该资源设置为锁定状态。其他线程如果要请求该共享资源,由于该资源被占有,因此无法请求成功,那么就需要一套线程阻塞等待以及唤醒时锁分配的机制。这个机制AQS使用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。在AbstractQueuedSynchronizer类中CLH队列是一个虚拟的双向队列(虚拟的双向对立是不存在队列实例,而是通过Node节点之间的关系关联)


synchronized 关键字的底层原理,JDK1.6 之后synchronized 做了哪些优化

非静态方法锁是锁对象  静态方法锁是锁类 不一样 所以不互斥  

通过monitor对象来完成的: monitorenter monitorexit 

无锁状态--->偏向锁状态--->轻量级锁状态(cas消除同步)--->重量级锁状态 

偏向锁 线程A第一次访问同步块时,先检测对象头Mark Word中的标志位是否为01,依此判断此时对象锁是否处于无所状态或者偏向锁状态(匿名偏向锁)

谈谈 synchronized和ReenTrantLock 的区别

  • ReentrantLock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁
  • ReentrantLock只适用于代码块锁,而synchronized可以修饰类、方法、变量等
  • 锁机制其实也不一样。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word
  • ReentrantLock的state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。其他线程再tryAcquire()时就失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。释放锁之前,A线程是可重复获取此锁(state会累加。但获取多少次就要释放多么次,保证state是能回到零态。
  • synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁 
  • synchronized是不可中断 ,除非加锁的代码中出现异常, ReentrantLock则可以中断。
  • ReentrantLock 和synchronized 都是可重入锁;

说说 synchronized 关键字和 volatile 关键字的区别

1.volatile 是线程同步的轻量级实现 ,性能肯定比synchronized关键字要好 。

2. volatile 只用于变量而 synchronized 可以修饰方法以及代码块 。
3.volatile 能保证数据的可见性,但不保证原子性。synchronized 两者都能保证。
4.volatile 用于解决变量在多个线程之间的可见性,而 synchronized 解决的是多个线程之间访问资源的同步性。

 

线程池参数?如何创建线程池?线程的状态,thread.yield()?线程池五种线程池,四种拒绝策略,三种阻塞队列    线程池大小= 最大线程数 + 阻塞队列大小

1、有任务时会创建核心线程数corePoolSize

2、线程满了(有任务但是线程被使用完)不会立即扩容,而是放到阻塞队列中,当阻塞队列满了之后才会继续创建线程。

3、队列满了,线程数达到最大会执行拒绝策略。

4、当线程数大于核心线程数,超过KeepAliveTime(闲置时间),线程会被回收,最终会保持corePoolSize个数线程。

拒绝策略执行则么办?
1、另外创建一个队列,当拒绝策略执行将任务放入队列。

通过定时任务去每隔一秒去查看线程池队列中是否有任务,没有则添加进去。

缺点: 任务会丢失,线程优雅关闭是指正常情况队列执行晚。会关闭。如果用这种方法创建则会丢失任务。

2、如果 是特别重要的话就 放入DB去持久化,给任务加个状态,通过状态来判断任务的执行情况。

3、其他情况要根据场景考虑 比如又些任务过期淘汰
 

ThreadPoolExecutor(int corePoolSize,// 核心线程数
                   int maximumPoolSize,//最大线程数
                   long keepAliveTime,//空闲线程存活时间
                   TimeUnit unit,//存活时间单位
                   BlockingQueue<Runnable> workQueue,//阻塞队列
                   RejectedExecutionHandler handler)//拒绝策略

ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();
//有缓冲的线程池,线程数 JVM 控制
threadPool = Executors.newFixedThreadPool(3);
//固定大小的线程池
threadPool = Executors.newScheduledThreadPool(2);
threadPool = Executors.newSingleThreadExecutor();
//单线程的线程池,只有一个线程在工作
threadPool = new ThreadPoolExecutor();
//默认线程池,可控制参数比较多  

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();
//默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy();
//队列满了丢任务不抛异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();
//将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();
//如果添加到线程池失败,那么主线程会自己去执行该任务;如果执行程序已关闭(主线程运行结束),则会丢弃该任务

实现Runnable接口和Callable接口的区别

Runnable接口中的唯一抽象方法run()方法没有返回值,Callable接口中的唯一抽象方法call()方法有返回值;

执行execute()方法和submit()方法的区别是什么呢?

execute和submit都属于线程池的方法,execute只能提交Runnable类型的任务,而submit既能提交Runnable类型任务也能提交Callable类型任务。

execute会直接抛出任务执行时的异常,submit会吃掉异常,可通过Future的get方法将任务执行时的异常重新抛出。

execute所属顶层接口是Executor,  submit所属顶层接口是ExecutorService,实现类ThreadPoolExecutor重写了execute方法,抽象类AbstractExecutorService重写了submit方法。
 

高并发结合任务执行时间长短怎么使用线程池?

 CPU 核数 +1 ,减少线程上下文的切换  增加cpu线程

做缓存是第一步,增加服务器是第二步,线程池的设置,使用中间件对任务进行拆分和解耦

JUC 包中的Atomic原子类是哪4类? 介绍一下 AtomicInteger 类的原理和使用?

整形 AtomicInteger 数组类型  AtomicIntegerArray  引用类型 AtomicStampedReference 

属性修改类型AtomicIntegerFieldUpdater

TreadLocal是什么?内存泄露?

每个Thread 维护一个 ThreadLocalMap 映射表,key 是 ThreadLocal实例本身,value 是真正需要存储的 Object。 ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。 ThreadLocalMap 使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。而value还存在着强引用.只有thead退出以后,value的强引用链条才会断掉. 如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄漏。

如何查看线程死锁?    

有三种方法可排查死锁:jps+jstack、jconsole、jvisualvm

在这里插入图片描述

 为什么使用红黑树而不用平衡树?

红黑树不追求"完全平衡",即不像AVL那样要求节点的 |balFact| <= 1,它只要求部分达到平衡,但是提出了为节点增加颜色,红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多

红黑树 : 每个节点要么是红色,要么是黑色,但根节点永远是黑色的;

每个红色节点的两个子节点一定都是黑色;

红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色);

从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;

所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);

在树的结构发生改变时(插入或者删除操作),往往会破坏上述条件 3 或条件 4,需要通过调整使得查找树重新满足红黑树的条件;

如何保证多线程的原子性,可见性,和有序性?

原子性:Atomic包、CAS算法、synchronized、Lock
原子性做了互斥方法,同一个线程只能有一个进行操作
可见性:synchronized、volatile
一个主内存的线程如果进行了修改,可以及时被其他线程观察到,介绍了volatile如何被观察到的
有序性:happens-before原则
happens-before原则,观察结果,如果两个线程不能从happens-before原则观察出来,那么就不能观察他们的有序性,虚拟机可以随意的对他们进行重排序

CompletableFuture你知道吗?

CompletableFuture异步任务执行线程池,默认是把异步任务都放在ForkJoinPool中执行。

  • runAsync() 以Runnable函数式接口类型为参数,没有返回结果,supplyAsync() 以Supplier函数式接口类型为参数,返回结果类型为U;Supplier接口的 get()是有返回值的(会阻塞)

线程a,b,c,d运⾏任务,怎么保证当a,b,c线程执⾏完再执⾏d线程?

Thread中的join方法解决线程顺序问题

倒计时锁CountDownLatch实现让三个线程同时执行

4.JVM


Java 中会存在内存泄漏吗,简述一下?

答:会,长周期持有短周期的引用:全局性的集合,单例模式的使用,类的static变量,删除机制和清除策略

详解JVM内存结构及分区,说一下每个分区放什么?

答:

jvm调优的常用命令和参数是是什么?

答:jps查JVM进程;jstat本地或远程JVM进程类的装载、内存、垃圾收集、JIT编译;jinfo查询JVM的属性和参数值;jmap当前Java堆和永久代;jhat分析jmap生成的dump文件,JDK自带的工具;jstack生成JVM所有线程快照,线程快照是JVM线程正在执行的方法,定位线程长时间停顿的原因。

-Xms ~ -Xmx:指定Java程序最大堆内存,使用java -Xmx5000M -version判断系统分配的最大堆内存。指定最小堆内存,设置成跟最大堆内存一样, 减少GC。

-Xmn:设置新生代大小。整个堆内存 = 新生代内存 + 老年代内存,此值对系统性能影响较大,Sun官方推荐为堆的3/8。

-Xss:指定线程的最大栈空间。该参数决定了java函数调用的深度,值越大调用深度越深,若值太小,容易发生栈溢出错误。

-XX:PermSize :指定方法区(永久区)的初始值,默认是物理内存的1/64,Java8永久区移除变成元数据区,由-XX:MetaspaceSize指定。

-XX:MaxPermSize :指定方法区(永久区)的最大值,默认是物理内存的1/4,Java8永久区移除之后,取而代之的是元数据区,由-XX:MaxMetaspaceSize指定。

-XX:NewRatio=n:老年代和新生代比值,n=2时,说明老年代和新生代的比值为2:1。

-XX:SurvivorRatio=n:Eden区和Survivor区的比值。n=8时,说明Eden和Survivor比值为8:1:1,因为Survivor有两个(from,to)。

描述一下 JVM 加载 Class 文件的原理机制?

答: 1.装载:查找和导入class文件;

2.连接:

      (1)检查:检查载入的class文件数据的正确性;

      (2)准备:为类的静态变量分配存储空间;

      (3)解析:将符号引用转换成直接引用(这一步是可选的)

3.初始化:初始化静态变量,静态代码块。
 解释下Tomcat类加载机制?

有几种类加载器?双亲委派模型机制?双亲委派解决了什么问题?

答: 引导类加载器 bootstrap classloader;扩展类加载器extensions class loader;应用程序类加载器 application classloader;自定义类加载器 java.lang.classloder

一个类加载器收到类加载请求,不会自己先去加载,而是把请求委托给父类的加载器执行,如果父类加载器还存在父类加载器,则进一步向上委托,依次递归,请求最终到达顶层的启动类加载器,如果父类加载器可以完成类加载,就成功返回,若父类加载器无法完成加载,子加载器才会尝试自己去加载.

解决了避免类的重复加载,确保类的全局唯一,保护程序,防止核心 API 随意篡改

垃圾回收常见问题

什么是GC? 为什么要有 GC?

如何判断一个对象是否存活?如何判断对象是否可以被回收?

Java 中垃圾收集的方法有哪些?

答:引用计数算法 ,GC Root Tracing,标记-清除算法,复制算法,标记-整理算法,分代收集算法

常用的性能优化方式有哪些?

减少新生代大小可以缩短新生代GC停顿时间,因为这样被复制到survivor区域或者被提升的数据更少,但是这样一来yangGC的频率就会很高,而且会有更多的垃圾进入到了老年代。如果增加新生代大小又会导致回收的时间和复制的时间变高,所以一般来说需要在这个中间进行折中。

主要的目的是减小GC的频率和Full GC的次数

jmap -heap 21711

JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存

在Java中,有哪些包是直接定义在堆外内存中的?

答:sun.misc.Unsafe提供了方法来进行堆外内存的分配;用NIO包下的ByteBuffer分配直接内存.

Spring里的bean会被GC吗?

答:取决于两点:1.Bean的作用域;2.Spring容器的状态 。容器处于运行中,singleton 类型Bean不会被GC,prototype 类型Bean会被回收,每使用都new一个,用完就回收

垃圾收集器的性能

串行回收器:Serial、Serial old

并行回收器:ParNew、Parallel Scavenge、Parallel old吞吐量优先

并发回收器:CMS、G1在JDK1.8中默认使用的Parallel Scavenge/Parallel 0ld这一对垃圾回收器。

Parallel Scavenge:吞吐量优先。

吞吐量=运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)

5.网络编程与操作系统、Linux

TCP、UDP 协议的区别,TCP/IP协议分层?

1、基于连接与无连接;2、对系统资源的要求(TCP较多,UDP少);3、UDP程序结构较简单;

4、流模式与数据报模式 5、TCP保证数据正确性,UDP可能丢包;6、TCP保证数据顺序,UDP不保证。

UDP协议的最适用场景  对实时性要求较高,对可靠性要求较低的场景,实时聊天(语音、视频聊天),UDP支持广播。 

在浏览器中输入url地址 ->> 显示主页的过程?6个步骤

DNS解析 TCP连接 发送HTTP请求 服务器处理请求并返回HTTP报文 浏览器解析渲染页面

连接结束

HTTP长连接、短连接

  HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点

     在HTTP/1.0中,默认使用的是短连接。浏览器和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。如果客户端浏览器访问的某个HTML或其JavaScript文件、图像文件、CSS文件等;当浏览器每遇到一个Web资源,就建立一个HTTP会话。 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有加入这行代码:

Connection:keep-alive

   在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。

TCP 三次握手和四次挥手?为啥关闭连接4次,建立连接3次?为什么四次挥手有time_wait状态?

①首先 Client 端发送连接请求报文

②Server 段接受连接后回复 ACK 报文,并为这次连接分配资源。

③Client 接收到 ACK 报文后也向 Server 发生 ACK 报文,并分配资源,TCP 连接就建立了。

第一次挥手:Clien发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

第二次挥手:Server收到FIN后,发送一个ACK给Client,Server进入CLOSE_WAIT状态。

第三次挥手: Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,发送ACK给Server,Server进入CLOSED状态,完成四次握手。
因为ACK和SYN(ACK起应答用,而SYN起同步作用)在一个报文里来发送。ACK报文和FIN报文多数情况下都是分开发送的。

一些常见的 Linux 命令了解吗?

解压文件tar -xvzf test.tar.gz  动态查看日志文件tail -f exmaple.log   切换用户su -username   

创建目录mkdir newfolder  当前目录pwd   查看线程 ps –ef|grep tomcat 终止线程kill -9 19979

Cookie和Session的区别?

cookie保存在客户端;session保存在服务器;cookie保存数据类型只是字符串,session可以保存除了资源以外的数据类型;cookie的大小有4K限制,session没有限制;cookie可以被浏览器禁用,session则不能;cookie不安全,可被拷贝进行cookie欺骗,考虑到安全性,重要信息应用session;session在访问量增多时,会增加服务器的负担,从服务器性能方面考虑,非重要信息若需要保留应使用cookie;cookie不设置有效期的话,默认浏览器关闭失效,session服务器默认有效期1440秒,24分钟

cookie禁用,手动通过URL传值、隐藏表单传递Session ID

如果调用方请求你的接口很慢,你怎么排查?

后端接口响应慢分以下2种情况:

某个接口慢: 用SkyWalking。展示每一个与网络有关的耗时,读写数据库、读写Redis、SpringCloud调用、Dubbo调用等。立马定位是哪次操作耗时了。记录SQL语句,可以帮助定位。如果是报错直接用postman请求看报错信息,或盲猜一下代码。 或者查腾讯云ES日志,通过TID查日志。

数据库耗时长: 必要字段加索引,确定是否索引失效了,如果有回表查询,尽量优化为覆盖索引

架构不合理,代码不规范,需要代码评审。

所有访问接口的响应速度很慢。 系统崩溃无响应  压测时查看CPU、内存、load、rt、qps等指标
CPU  定位进程 (命令:top) 找占CPU最高的进程 top命令,记下进程号(PID)。假设最高是:1893
定位线程 (命令:top -Hp 进程号) Java是单进程多线程的  top -Hp 1893。假设最高是:4519
定位代码位置  (命令:jstack)  通过线程定位代码大概位置信息。printf %x 4519。结果为:11a7    jstack 1893 | grep  11a7 -A 30 --color

导致cpu使用飙升的操作  

无限循环的while,经常使用Young GC,频繁的GC
如果访问量很高,可能会导致频繁的GC甚至Full GC。当调用量很大时,内存分配将如此之快以至于GC线程将连续执行,这将导致CPU飙升。锁的不恰当使用。
 

Java--线上问题排查--方法/步骤_IT利刃出鞘的博客-CSDN博客_java线上问题排查

http 1.0 和 http 2.0的区别?

答:http2.x采用二进制格式,http1.x的解析是基于文本;http2.0多路复用,只需一个连接可实现并行http1.x有序并阻塞的;用Header压缩报头,http2.x降低了开销,http2.x让服务器可将响应主动“推送”到客户端缓存

请求类型Get与Post之间的区别?

答:GET在浏览器回退时是无害的,而POST会再次提交请求;

GET产生URL地址可被Bookmark,POST不可以;

GET请求会被浏览器主动cache缓存,而POST不会,除非手动设置;

GET请求只能进行url编码,而POST支持多种编码方式;

GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留;

GET请求在URL中传送的参数有长度限制,而POST没;

对参数的数据类型,GET只接受ASCII字符,而POST没有限制;

GET比POST不安全,因参数暴露在URL上,不能用来传递敏感信息,其实都不安全 HTTP 都可以网络抓包,应该使用 https。

GET参数通过URL传递,POST参数放在Request body中。

什么是restful风格?

每一个URI代表1种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作

请求路径相同,但根据不同的参数、请求方式不同而执行不同的方法,产生的结果也不同。

@GetMapping(path = “/add/{a}/{b}”) 等价于 @RequestMapping(path = “/add/{a}/{b}”,method = RequestMethod.GET)

6.MySQL

sql编写和执行顺序

编写: select  from join where group by having order by limit

执行: from-where-groupby-having-select-orderby-limit

SQL查询语句的执行顺序解析

  1. 首先执行 FROM 子句, 从学生成绩表中组装数据源的数据。
  2. 执行 WHERE 子句, 筛选学生成绩表中所有学生的数学成绩不为 NULL 的数据 。
  3. 执行 GROUP BY 子句, 把学生成绩表按 "班级" 字段进行分组。
  4. 计算 avg 聚合函数, 按找每个班级分组求出 数学平均成绩
  5. 执行 HAVING 子句, 筛选出班级 数学平均成绩大于 75 分的。
  6. 执行SELECT语句,返回数据,但别着急,还需要执行后面几个步骤。
  7. 执行 ORDER BY 子句, 把最后的结果按 "数学平均成绩" 进行排序。
  8. 执行LIMIT ,限制仅返回3条数据。结合ORDER BY 子句,即返回所有班级中数学平均成绩的前三的班级及其数学平均成绩。

有哪些索引?MySQL中聚镞索引和非聚镞索引的不同,性能如何?

1,普通索引:普通索引是最基本的索引,它没有任何限制,值可以为空;仅加速查询。
2,唯一索引: 索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
3,主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。
4,组合索引:多个字段上创建的索引,查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引遵循最左前缀原则。
5,全文索引:全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配

①聚集索引:使用聚集索引的表,记录和索引保持着一致的顺序,这样只要找到索引的值就能直接从叶子节点里面获取到全部列数据   用拼音查 ,快于非聚集索引

②非聚集索引:记录和索引的顺序往往不同,可理解为索引下面的叶子节点存储的还是索引,想要获得真正的列数据,还需要再一次查询;  用部首查

InnoDB是聚集索引,MyISAM 是非聚集索引  

innodb支持事务,myisam不支持事务,innodb有外键,myisam没有外键。 innodb行锁,myisam表锁。

ACID是什么?怎么保证?

  • 原子性是由 undolog 日志来保存历史版本数据,它记录了需要回滚的日志信息。
  • 持久性由 redolog来进行保证,修改数据时MySQL先把这条记录所在的「页」(也就是B+树上的叶子节点)找到,把该页加载到内存,在内存中修改。写一份redo log,redo log记这次在某个页上做了什么修改、执行了什么sql。
  • 隔离性事务之间彼此是不会受相互打扰的,相互隔离的,通过 MVCC 技术
  • 保证原子性、持久化、隔离性都是为了保证一致性

Mysql执行计划怎么看?(type字段)

列名

含义

id

SELECT查询的序列标识符

select_type

SELECT关键字对应的查询类型 

table

用到的表名

partitions

匹配的分区,对于未分区的表,值为 NULL

type

表的访问方法

possible_keys

可能用到的索引

key

实际用到的索引

key_len

所选索引的长度

ref

当使用索引等值查询时,与索引作比较的列或常量

rows

预计要读取的行数

filtered

按表条件过滤后,留存的记录数的百分比

Extra

附加信息

联合索引什么情况下会失效? 什么情况索引会失效?最左匹配原则是什么?

CREATE INDEX index_name ON table_name (column_list)

查询联合索引从必须从最左侧开始,跳过后面的则失效,和sql语句位置没关系。

  • 减少或者避免 select *
  • 在使用+like ‘%abc…’ 开头会导致索引失效,全盘扫描
  • 在索引列上做计算、函数、类型转换会导致索引失效
  • 存储引擎不能使用索引范围右边的列
  • is null not is null 无法使用索引

MySQL有哪几种隔离级别?默认是哪个?

read-uncommitted 读未提交:在该级别,所有的事务都可以看到其他未提交事务的执行结果,本隔离级别很少用于实际应用,因为它的性能不比其他级别好多少。读取未提交的数据,也称之为脏读。

read-committed 读提交内容:这是大多数数据库系统的默认隔离级别(但不是MYSQL默认的),它满足了隔离的简单定义:一个事务只能看见已提交事务所做的改变。也支持所谓的不可重复读。

repeatable-read 可重读:是MYSQL默认的,确保统一事务的多个实例在并发读取数据时,会看到同样的数据行。

serializable 可串行化:这是最高的隔离级别,他通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,他是在每个读的数据行上加上共享锁。在这个级别可能导致大量的超时现象和锁竞争。

低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
 

幻读是什么?RR隔离级别能解决幻读吗?那RR隔离级别是怎么解决幻读的?

在RR级别使用MVCC和next-key临键锁防止幻读 

锁住的是索引本身以及索引之前的间隙,是一个左开右闭的区间。当 SQL 执行按照非唯一索引进行数据的检索时,会给匹配到行上加上临键锁。

InnoDB 实现MVCC,是通过Read View+ Undo Log 实现的,Undo Log 保存了历史快照,Read View可见性规则帮助判断当前版本的数据是否可见。

  1. 获取事务自己的版本号,即事务ID
  2. 获取Read View
  3. 查询得到的数据,然后Read View中的事务版本号进行比较。
  4. 如果不符合Read View的可见性规则, 即就需要Undo log中历史快照;
  5. 最后返回符合规则的数据

 幻读: 第一个事务对一个表数据进行修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表数据,这种修改是向表中插入一行新数据。操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

mysql的锁机制行锁、表锁、间隙锁、意向锁分别是做什么的?

按照粒度划分:行锁、表锁、间隙锁
行锁:每次操作锁住一行或多行记录,锁定粒度最小,发生锁冲突概率最低,并发读最高。
表锁:每次锁住整张表。锁定粒度大,发生冲突的概率最高,并发值最低。
间隙锁:每次锁定相邻的一组记录,锁定粒度结余行锁和表锁之间。

按操作类型可分为:读锁和写锁
读锁(S锁):共享锁,针对同一份数据,多个事务可以对其添加读锁,其他事务无法进行修改数据(其他事务无法添加写锁)。 写法:SELECT … LOCK IN SHARE MODE

写锁(X锁):排他锁,针对同一份数据,在当前上锁事务完成前,会阻塞其他事务的写锁或读锁。(仍旧是可以进行查询操作的,只不过不能加锁)。写法:SELECT … FOR UPDATE

意向锁:不是真正意义上的锁,只是用于辅助判断,提高加锁判断效率。在对表记录添加S或X锁之前,会先对表添加IS或IX锁,方便下一次加锁前条件判断,不必遍历事务判断。
 

如何避免数据库死锁?怎么解决数据库死锁?

 1、资源不共享,需要只由一个进程或线程使用 2、请求且保持,已锁定的资源保持着不释放 3、不剥夺,申请到的资源不能被别人剥夺 4、循环等待 

(1)尽量避免并发地执行涉及到修改数据的语句。 比如有一个修改上百条记录的update语句,我们可以修改成每10条一个update语句,或者干脆就每条记录一个update语句。
(2)要求每个事务一次就将所有要使用的数据全部加锁,否则就不予执行。 
(3)预规定封锁顺序,所有的事务都须按这个顺序对数据执行封锁。如不同的过程在事务内部对对象的更新执行顺序应尽量保持一致。 
(4) 将事务分割成为几个小事务来执行。【比如说把复杂的多表查询分散成多次单表查询】 
(5)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过将大表按行或列分解为若干小表,或按不同的用户群分解两种方法实现。分散“数据热点”,如果数据不是太经常被访问,那么死锁就不会太经常发生。 
(6)将经常更新的数据库和查询数据库分开,主从复制,读写分离。

数据库的查询优化、排查慢sql以及sql优化是怎么进行的?详细点说明下

尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引。

尽量避免在 where 子句中对字段进行 null 值判断和表达式,将导致引擎放弃索引进行全表扫描

一个表的索引数最好不要超过 6 个

InnoDB中哈希索引实现机制是什么?索引为什么用的是b+ tree而不是b-tree、红黑树?

二叉查找树:解决了排序基本问题,但无法保证平衡,会退化为链表;
平衡二叉树:通过旋转解决平衡问题,但旋转效率太低;
红黑树:通过舍弃严格的平衡和引入红黑节点,解决了平衡二叉树旋转效率过低的问题,但在磁盘等场景下,树太高,IO 次数太多;
B 树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;
B+ 树:在 B 树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

 

数据库的乐观锁和悲观锁

乐观锁 : 增加一个版本号的字段version(数字类型),每次更新一行记录,都使得该行版本号加一,开始更新之前先获取version的值,更新提交的时候带上之前获取的version值与当前version值作比较,如果不相等则说明version值发生了变化则检测到了并发冲突,本次操作执行失败,如果相等则操作执行成功。写操作不频繁。DB的读大于写的业务场景

UPDATE TABLE 
SET columnA = 1, VERSION = VERSION + 1  WHERE ID = #{ID}   AND VERSION = #{oldVersion}

悲观锁:每一次行数据的访问都是独占的,只有当正在访问该行数据的请求事务提交以后,其他请求才能依次访问该数据,否则将阻塞等待锁的获取。悲观锁可以严格保证数据访问的安全。所以比较适合用在DB写大于读的情况。SELECT * FROM TABLE WHERE ID = 1 FOR UPDATE  
对于大表的常见优化手段说一下?

1.限定数据的范围  禁止不带任何限制数据范围条件的查询语句

2. 读/写分离  3、分表分库 3.1、垂直分区  3.2、水平分区

水平分表后如何确定数据在哪个表里

 分库分表中间件了,比如Apache ShardingSphere   hash取模 定位表  雪花算法 订单号作为shardingkey

更新一条sql语句过程

  • 首先执行器根据 MySQL 的执行计划来查询数据,先是从缓存池中查询数据,如果没有就会去数据库中查询,如果查询到了就将其放到缓存池中
  • 在数据被缓存到缓存池的同时,会写入 undo log 日志文件
  • 更新的动作是在 BufferPool 中完成的,同时会将更新后的数据添加到 redo log buffer 中
  • 完成之后就可以提交事务了,在提交的同时会做以下三件事:
    (1)将redo log buffer中的数据刷入到 redo log 文件中
    (2)将本次操作记录写入到bin log文件中
    (3)将bin log文件名字和更新内容在bin log中的位置记录到 redo log 中,同时在 redo log 最后添加commit标记。
  • 至此整个更新事务已经完成。

mysql存储过程

mysql> DELIMITER //

mysql> CREATE PROCEDURE ShowStuScore()

    -> BEGIN

    -> SELECT * FROM tb_students_score;

    -> END //

Query OK, 0 rows affected (0.09 sec)

7.Spring和SpringBoot,设计模式

Spring由哪些模块组成?

Spring-Core,Spring-Context,Spring-Aop,Spring-Dao,Spring-Web,Spring Web MVC,
Spring-ORM


 Spring框架有哪些不同类型的事件?

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

6 继承ApplicationEvent自定义事件 ,实现ApplicationListener接口,publishEvent()发布

Spring Bean 的作用域和生命周期?

作用域   singleton:唯一 bean 实例,Spring 中的 bean 默认都是单例的。无状态 

prototype:每次请求都会创建一个新的 bean 实例。(多例)
request:每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
session:每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。

 生命周期: 

实例化 Instantiation 属性赋值 Populate,初始化 Initialization,销毁 Destruction

单例模式了解吗?有哪几种?写个单例模式?Spring和普通单例模式有啥区别? 单例怎么保证线程安全?

饿汉式、懒汉式、内部类懒汉式、枚举型    

  • 设计模式单例,在整个应用中只有一个实例
  • spring单例,在一个IoC容器中只有一个实例

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

BeanFactory和ApplicationContext有啥区别?

BeanFactroy采用的是延迟加载形式注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可发现Spring中存在的配置错误,检查所依赖属性是否注入。ApplicationContext启动后预载入所有的实例Bean,通过预载入单实例bean ,确保需要的时候,不用等待,因为它们已经创建好

Spring 事务中的隔离级别?事务管理类型(编程式和声明式)

   1.DEFAULT:  使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应。
  2.UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
  3.COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
  4.REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
  5.SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

 

  脏读: 当事务正访问数据,并对数据进行了修改,而这种修改还没提交到数据库,这时另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没提交的数据, 那么另外一 个事务读到的数据是脏数据,依据脏数据所做的操作可能是不正确的。
  不可重复读: 在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。 那么,在第一个事务中的两次读数据之间,由于第二个事务修改,第一个事务两次读到的数据是不一样的。就发生了在一个事务内两次读到的数据是不一样的,称为是不可重复读。

 

Spring 事务中的事务传播行为?

支持当前事务
REQUIRED :如果当前存在事务,则加入该事务。如果当前没有事务,则创建一个新的事务

SUPPORTS:如果当前存在事务,则加入该事务 。如果当前没事务, 则以非事务的方式继续运行

MANDATORY :如果当前存在事务,则加入该事务 。如果当前没有事务,则抛出异常

不支持当前事务
REQUIRES_NEW :创建一个新事务,如果当前存在事务,则把当前事务挂起

NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起

NEVER :以非事务方式运行,如果当前存在事务,则抛出异常

其他 NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来执行 。如果当前没有事务,则该取值等价于REQUIRED
 

SpringAOP了解吗?有哪几种实现方式?  ***

 通过实现spring提供的接口实现
 通过自定义方式织入实现
 通过注解的方式实现    使用AspectJ注解技术实现AOP功能

@Aspect注解,使类成为切面类,并被AOP识别和加载。

在类方法头部加@Pointcut注解,使该方法称为一个切入点。@Pointcut注解的execution表达式定义该方法在什么位置切入。

分别是@Before、@After、@Around、@AfterReturning、@AfterThrowing。被@Before注解的方法在被切入方法执行之前执行;被@After注解的方法在被切入方法执行之后执行,不考虑是否执行成功;被@AfterReturning注解的方法在被切入方法执行成功之后执行,当被切入方法发生异常时,该方法不被执行;被@Around注解的方法在被切入方法执行之前和执行之后都执行;被@AfterThrowing注解的方法,只有当被切入方法执行过程发生异常时才会执行。

AOP使用org.aspectj.lang.JoinPoint类型,用于获取被切入点传入的参数,任何切入方法的第一个参数都可以是JoinPoint。

before:前置通知(应用:各种校验):在方法执行前执行,如果通知抛出异常,阻止方法运行
afterReturning:后置通知(应用:常规数据处理):方法正常返回后执行,如果方法中抛出异常,通知无法执行,必须在方法执行后才执行,所以可以获得方法的返回值。
around:环绕通知(应用:十分强大,可以做任何事情):方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法
afterThrowing:抛出异常通知(应用:包装异常信息):方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知(应用:清理现场):方法执行完毕后执行,无论方法中是否出现异常

 

依赖注入IOC是什么?

创建对象权利交给spring容器

Spring 框架中都用到了哪些设计模式?

工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext创建对象;单例模式:Bean默认单例模式;策略模式:Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略;代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题.比如RestTemplate, JmsTemplate, JpaTemplate;适配器模式:Spring AOP的增强或通知(Advice)使用到适配器模式,Spring MVC中也是用到了适配器模式适配Controller;观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用;桥接模式:根据客户的需求动态切换不同的数据源。项目需要连接多数据库,每次访问中根据需要去访问不同数据库.


双检锁

懒汉有线程安全问题

public class Singleton {  
    private volatile Singleton instance = null;  
    public Singleton getInstance() {  
        if (instance == null) {  
            synchronized(this) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  

Springboot的Run方法过程,自动配置原理?Starter 的工作原理是什么?

SpringBootConfiguration:包含了Configuration注解,实现配置文件
ComponentScan:指定扫描范围
EnableAutoConfiguration:通过源码可以知道,该注解使用Import引入了AutoConfigurationImportSelector类,而AutoConfigurationImportSelector类通过SpringFactortisLoader加载了所有jar包的MATE-INF文件夹下面的spring.factories文件,spring.factories包含了所有需要装配的XXXConfiguration类的全限定名。XXXConfiguration类包含了实例化该类需要的信息,比如说如果这是个数据源Configuration类,那么就应该有数据库驱动、用户名、密码等等信息。
总结:
1.提供了一个配置类,该配置类定义了我们需要的对象的实例化过程;
2.提供了一个spring.factories文件,包含了配置类的全限定名;
3.将配置类和spring.factories文件打包为一个启动器starter;
4.程序启动时通过加载starter.jar包的spring.factories文件信息,然后通过反射实例化文件里面的类。

Spring的怎么用三级缓存解决循环依赖? 

第一级缓存:singletonObjects,存放经过初始化后的bean。当通过名字获取bean的时候,如果这个名字对应的bean在第一级缓存中,则直接从第一级缓存中获取返回,这样就不会导致多次创建bean了。
第二级缓存:earlySingletonObjects,存放不完整的bean,对象就是最终的对象,但是对象的属性可能不完整。当填充依赖对象的时候,先从一级二级缓存中查找,如果找到了,则直接拿出来赋值。如果没找到,则使用三级缓存创建,然后放入到二级缓存中。
场景:A依赖B和C,B依赖A,C依赖A。A填充B的时候要构造B,然后填充B的时候需要A,所以B就从一级二级缓存中查找A,没找到,就使用三级缓存创建最终的A填充到B的A属性。A填充C的时候要构造C,然后填充C的时候需要A,所以C就从一级二级缓存中查找A,找到了,直接填充。
第三级缓存:singletonFactories,存放bean对应的工厂对象,主要得到根据原始对象进行AOP之后的代理对象。在bean实例化之后就将生成最终对象的ObjectFactory对象放入到三级缓存中,当从二级缓存中获取不到对象的时候,就根据这个ObjectFactory生成最终对象。

 

SpringMVC的处理流程? 

在这里插入图片描述

DispatcherServlet实现frameservlet中的doService()方法,doService()调用doDispatch()方法。

mybatis分页插件的运行原理是什么?如何写一个插件?  MyBatis 分页插件 - PageHelper

分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页。实现一个关键的方法就是intercept,从而实现拦截

 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()
方法,只会拦截那些你指定需要拦截的方法。

 自定义实现: 
public interface Interceptor {
  //拦截的方法
  Object intercept(Invocation invocation) throws Throwable;
  //返回拦截器的代理对象
  Object plugin(Object target);
  //设置一些属性
  void setProperties(Properties properties);

}

#{}和${}区别  

1.#{}是预编译处理,$ {}是字符串替换。

2.mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。

3.使用 #{} 可以有效的防止SQL注入,提高系统安全性。

接口的方法如何和xml绑定

namespace + bean id

mapper.xml文件中常见标签
除了常见的<select>、<insert>、<update>、<delete>标签以外,还有<resultMap>、<parameterMap>(以被弃用)、<sql>、<include>、<selectKey>,加上动态sql的9个标签,trim、where、set、foreach、if、choose、when、otherwise、bind等,其中<sql>为sql片段标签,通过<include>标签引入sql片段,<selectKey>为不支持自增的主键生成策略标签。
 

嵌套查询和嵌套结果区别有哪些

嵌套查询: 

<!-- 一对一:association使用select属性引入另外一条SQL语句 --> <association property="card" column="card_id" javaType="IdCard" select="com.zsj.mapper.IdCardMapper.findCodeById" />

嵌套结果:

<association property="card" javaType="IdCard"> <id property="id" column="card_id" /> <result property="code" column="code" /> </association>

插入一条数据获取这条数据的主键的返回值

使用insert 中的useGeneratedKeys  = true 和 keyProperty= id 两个属性

8.Redis 
为什么用 redis缓存?什么是缓存预热?

答: 高性能、高并发。热点数据提前加载到缓存。

Redis中跳表的原理了解吗?

  1. 从team节点出发,如果当前节点的key与查询的key相等,返回当前节点(如果是修改操作那么一直向下进行修改值即可)。
  2. 如果key不相等,且右侧为null,那么只能向下(结果出现在下右方向),team=team.down
  3. 如果key不相等,且右侧不为null,且右侧节点key小于待查询的key。那么说明同级还可向右,此时team=team.right
  4. (否则的情况)如果key不相等,且右侧不为null,且右侧节点key大于待查询的key 。那么说明如果有结果的话就在这个索引和下个索引之间,此时team=team.down。

redis的同步机制?

Redis的主从同步机制可以确保redis的master和slave之间的数据同步。
Redis在2.8及以上版本使用psync命令完成主从数据同步。
同步方式包括:全量复制和增量复制

为什么要用 redis 而不用 map/guava 做缓存? 

使用自带的map或者guava实现的是本地缓存,特点是轻量以及快,生命周期随jvm的销毁而结束,在多实例的情况下,每个实例都要各自保存一份缓存,缓存不具有一致性

redis 和 memcached 的区别?

memcached多线程,不能持久化,redis单线程,可以持久化。

redis 常见数据结构以及使用场景分析?(String,Hash,List,Set,Sorted Set)

String : key-value 类型 set,get,strlen,exists,dect,incr,setex  ⽤户的访问次数、热点⽂章的点赞转发数量 

127.0.0.1:6379> set key value #设置 key-value 类型的值
OK
127.0.0.1:6379> get key # 根据 key 获得对应的 value
"value"
127.0.0.1:6379> exists key # 判断某个 key 是否存在
(integer) 1
127.0.0.1:6379> strlen key # 返回 key 所储存的字符串值的⻓度。
(integer) 5
127.0.0.1:6379> del key # 删除某个 key 对应的值
(integer) 1
127.0.0.1:6379> get key
(nil)

//批量操作

127.0.0.1:6379> mset key1 value1 key2 value2 # 批量设置 key-value 类型的值
OK
127.0.0.1:6379> mget key1 key2 # 批量获取多个 key 对应的 value
1) "value1"
2) "value2"

//计数器(字符串的内容为整数的时候可以使⽤):
127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> incr number # 将 key 中储存的数字值增⼀
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> decr number # 将 key 中储存的数字值减⼀
(integer) 1
127.0.0.1:6379> get number
"1"

//过期
127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

list: 双向链表     发布与订阅或者说消息队列、慢查询  

//实现队列
127.0.0.1:6379> rpush myList value1 # 向 list 的头部(右边)添加元素
(integer) 1
127.0.0.1:6379> rpush myList value2 value3 # 向list的头部(最右边)添加多个元素
(integer) 3
127.0.0.1:6379> lpop myList # 将 list的尾部(最左边)元素取出
"value1"
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第⼀
1) "value2"
2) "value3"

hash: 类似 JDK1.8 前的 HashMap,数组+链表  可以存储对象

 hset,hmset,hexists,hget,hgetall,hkeys,hvals 

Set :  类似hashset,sadd,spop,smembers,sismember,scard,sinterstore,sunion 共同关注、共同粉丝、共同喜好 

Sorted Set: 和 set 相⽐,sorted set 增加了⼀个权重参数  直播系统中,实时排⾏信息   scorzadd,zcard,zscore,zrange,zrevrange,zrem 

127.0.0.1:6379> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重
(integer) 1
127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 # ⼀次添加多个元素
(integer) 2
127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素数量
(integer) 3
127.0.0.1:6379> zscore myZset value1 # 查看某个 value 的权重
"3"
127.0.0.1:6379> zrange myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元
素
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> zrange myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start 1 为
stop
1) "value3"
2) "value2"
127.0.0.1:6379> zrevrange myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start 1
为 stop
1) "value1"
2) "value2"

redis 设置过期时间

EXPIRE 命令用于将键key的生存时间设置为ttl秒。 EXPIRE key "seconds"
PEXPIRE 命令用于将键key的生存时间设置为ttl毫秒。 PEXPIRE key "milliseconds"
EXPIREAT 命令用于将键key的过期时间设置为 timestamp所指定的秒数时间戳。
PEXPIREAT 命令用于将键key的过期时间设置 为timestamp所指定的毫秒数时间戳。
 EXPIRE、PEXPIRE、EXPIREAT三个命令都是使用PEXPIREAT命令来实现的

//向redis里存入数据和设置缓存时间  
stringRedisTemplate.opsForValue().set("baike", "100", 60 * 10, TimeUnit.SECONDS);
//val做-1操作  
stringRedisTemplate.boundValueOps("baike").increment(-1);
//根据key获取缓存中的val  
stringRedisTemplate.opsForValue().get("baike")
//val +1  
stringRedisTemplate.boundValueOps("baike").increment(1);
//根据key获取过期时间  
stringRedisTemplate.getExpire("baike");
//根据key获取过期时间并换算成指定单位  
stringRedisTemplate.getExpire("baike",TimeUnit.SECONDS);
//根据key删除缓存  
stringRedisTemplate.delete("baike");
//检查key是否存在,返回boolean值  
stringRedisTemplate.hasKey("baike");
//向指定key中存放set集合  
stringRedisTemplate.opsForSet().add("baike", "1","2","3");
//设置过期时间  
stringRedisTemplate.expire("baike",1000 , TimeUnit.MILLISECONDS);
//根据key查看集合中是否存在指定数据  
stringRedisTemplate.opsForSet().isMember("baike", "1");
//根据key获取set集合  
stringRedisTemplate.opsForSet().members("baike");
//验证有效时间
Long expire = redisTemplate.boundHashOps("baike").getExpire();
/**
* 从redis中获取key对应的过期时间;
* 如果该值有过期时间,就返回相应的过期时间;
* 如果该值没有设置过期时间,就返回-1;
* 如果没有该值,就返回-2;
*/
redisTemplate.opsForValue().getOperations().getExpire("key的名称")

redis 过期键的删除策略 

定时删除

在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。

优点:对内存最友好。及时释放键占用内存。缺点:对 CPU 不友好。在过期键多的情况下,删除占用相当一部分 CPU 时间。将 CPU 用在删除和当前任务不想关的过期键上,无疑会对服务器响应时间和吞吐量造成影响。

惰性删除

放任键过期不管,但是每次从键空间中读写键时,都会检查取得的键是否过期。如果过期就删除该删,否则就返回该键。(PS:键空间是一个保存数据库所有键值对的数据结构)

优点:对 CPU 最友好。只有在操作的时候进行过期检查,删除的目标仅限于当前需要处理的键,不会在删除其他无关本次操作的过期键上花费任何 CPU 时间。

缺点:对内存不友好。键过期了,但因为一直没有被访问到,所以一直保留着(除非手动执行 flushdb 清空当前数据库中的 key。),相当于内存泄漏。

定期删除

每隔一段时间,程序就对数据库进行检查,删除里面的过期键。至于要删除多少过期键,以及检查多少数据库,则有算法决定。

redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)?

RDB(Redis database):fork一个子线程,将数据集写入临时文件,写入成功后替换掉原先的文件

保存到dump.rdb。比aof文件小,启动比aof速度快,间隔一段时间写入,所以数据安全性低。

AOF 以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以append模式将修改数据写入到老的磁盘文件中

生产环境更多是二者结合使用的。

缓存击穿,缓存雪崩和缓存穿透问题解决方案?

缓存穿透: 空结果缓存,布隆过滤器原理  缓存雪崩  :设置随机的过期时间    

缓存击穿  加分布式锁

如何解决 Redis 的并发竞争 Key 问题

分布式锁

如何保证缓存与数据库双写时的数据一致性?****

读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。

 延时双删

1 先删除缓存
2 再更新数据库
3 休眠一会(读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据),再次删除缓存。 只做失效 不做更新

redis分布式锁会出现哪些问题?

不可重入 不可重试 超时释放 主从一致   引入Redisson

MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?

可以使用allkeys-lru淘汰策略,该淘汰策略是从 Redis 的数据中挑选最近最少使用的数据删除,这样频繁被访问的数据就可以保留下来了。

Redis内存淘汰机制  设置redis.conf 内存大小 maxmemory  maxmemory-policy allkeys-lru

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
  • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  • allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。

redis集群方案有哪些?它们的工作原理?

主从模式,哨兵模式,

Cluster模式  3 台以上的master节点,最好使用 3 主 3 从六个节点的模式 去中心化

redis cluster中固定的存在16384个hash slot。 

首先定义了编号0 ~ 16383的区间,称为槽,所有的键根据哈希函数映射到0 ~ 16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

redis集群数据存储原理:

    在redis cluster中,如果想要存入一个key-value,

    这个key首先会通过CRC16算法取余(和16384取余),

    结果会对应上0-16383之间的哈希槽(hash slot)

    最后,redis cluster会将key-value放置在对应的哈希槽中。    

redis集群数据获取原理:

    当client向redis cluster中的任意一个节点发送与数据库key有关的命令时,

    接收命令的节点会计算出要处理的key属于哪个哈希槽(hash slot),

    并且先检查这个hash slot是否属于自己(管辖):

        如果key所在的槽正好属于自己(管辖),节点会直接执行这个key相关命令。

        如果key所在的槽不属于自己(管辖),那么节点会给client返回一个MOVED错误,

        指引client转向负责对应槽的节点,并客户端需要再次发送想要执行的和key相关的命令。

9.RabbitMQ 

什么是消息队列?常见的消息队列对比?

通过异步处理提高系统性能和削峰、降低系统耦合性。目前使用较多的消息队列有ActiveMQRabbitMQKafkaRocketMQ
各大消息中间件对比

说一下RabbitMQ架构设计 

在这里插入图片描述

Broker:rabbitmq的服务节点

Queue:队列,是RabbitMQ的内部对象,用于存储消息。RabbitMQ中消息只能存储在队列中。生产者投递消息到队列,消费者从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(轮询)给多个消费者进行消费,而不是每个消费者都收到所有的消息进行消费。(注意:RabbitMQ不支持队列层面的广播消费,如果需要广播消费,可以采用一个交换器通过路由Key绑定多个队列,由多个消费者来订阅这些队列的方式。

Exchange:交换器。生产者将消息发送到Exchange,由交换器将消息路由到一个或多个队列中。如果路由不到,或返回给生产者,或直接丢弃。

RoutingKey:路由Key。生产者将消息发送给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则。这个路由Key需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键固定的情况下,生产者可以在发送消息给交换器时通过指定RoutingKey来决定消息流向哪里。

Binding:通过绑定将交换器和队列关联起来,在绑定的时候一般会指定一个绑定键,这样RabbitMQ就可以指定如何正确的路由到队列了。

交换器和队列实际上是多对多关系。 在投递消息时,通过Exchange和RoutingKey(对应BindingKey)就可以找到相对应的队列。

信道:信道是建立在Connection 之上的虚拟连接。当应用程序与Rabbit Broker建立TCP连接的时候,客户端紧接着可以创建一个AMQP 信道(Channel) ,每个信道都会被指派一个唯一的D。RabbitMQ 处理的每条AMQP 指令都是通过信道完成的。信道就像电缆里的光纤束。一条电缆内含有许多光纤束,允许所有的连接通过多条光线束进行传输和接收。
 

通过异步处理提高系统性能(削峰、减少响应所需时间)

5个步骤: 1。配置RabbitMQ队列、路由器、路由键  的自定义名字。

2。配置RabbitMQ服务器地址

3。创建队列、路由器、及其它们的绑定关系

4。创建发送端

5。创建接收端

Exchange 四种类型?

direct、fanout、topic、headers。

JMS两种消息模型,五种不同的消息正文格式

如何保证消息不被重复消费?

让每个消息携带一个全局的唯一ID,即可保证消息的幂等性

RabbitMQ 弄丢了数据怎么办? 

重试机制 try catch 持久化 


就是 RabbitMQ 自己弄丢了数据,这个你必须开启 RabbitMQ 的持久化,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,可能导致少量数据丢失,但是这个概率较小。
设置持久化有两个步骤:
第一创建 queue 的时候将其设置为持久化
这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的。
第二个是发送消息的时候将消息的 deliveryMode 设置为 2
就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。
必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。
注意,哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。所以,持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 ack,你也是可以自己重发的。
 

如何保证消息的有序性?

你在 mysql 里增删改一条数据,对应出来了增删改 3 条 binlog 日志,接着这三条 binlog 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你楞是换了顺序给执行成删除、修改、增加,不全错了么。
本来这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。
先看看顺序会错乱的俩场景:
RabbitMQ:一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者2先执行完操作,把 data2 存入数据库,然后是 data1/data3。这不明显乱了。
 解决方案:
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

RabbitMQ持久化机制和事务消息机制

  • channel.txSelect用于将当前的信道设置成事务模式
  • channel.txCommit用于提交事务
  • channel.txRollback用于事务回滚。

RabbitMQ如何保证消息的可靠性传输?

生产者弄丢了数据:

消息投递(生产者向MQ发送消息,确保MQ接收到消息)

confirm:  消息从producer——>exchange,会回调confirmCallback

return退回消息从exchange——>queue,当交换机到队列路由失败时会执行returnCallback

消息确认(消费者签收消息)

ack指Acknowledge,确认,表示消费者收到消息后的确认方式。

有3种确认方式:

  • 自动确认:acknowledge=“none”
  • 手动确认:  acknowledge=“manual” 配置文件,手动ack
  • 根据异常情况确认:acknowledge=“auto”  

消费端弄丢了数据

因为你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,RabbitMQ 认为你都消费了,这数据就丢了。这个时候得用 RabbitMQ 提供的 ack 机制,你必须关闭 RabbitMQ 的自动 ack,通过一个 api 来调用,每次代码里确保处理完的时候,再在程序里 ack 一把。如果你还没处理完,不就没有 ack 了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。

消息积压     (紧急扩容,上线更多得消费者,编写离线处理业务补数据)

原因 : 消费者 能力不足  生产的消费信息太多  消费者宕机
场景:几千万条数据在 MQ 里积压了七八个小时
一个消费者一秒是 1000 条,一秒 3 个消费者 3000 条,一分钟是 18 万条。如果你积压了几百万到上千万数据,即使消费者恢复,也需大概 1 小时时间才能恢复过来。
一般这个时候,只能临时紧急扩容了,具体操作步骤和思路:
1先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉。新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。
2然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue。
3接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。

等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。
10 mq 中的消息过期失效了
假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。

这个情况下,就不是说要增加 consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。

假设 1 万个订单积压在 mq 里面,没处理,其中 1000 个订单丢了,只能手动写程序把 1000 个订单查出来,发到 mq 里去再补一次
 mq 都快写满了

如果消息积压在 mq 里,你很长时间都没有处理掉,此时导致 mq 都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。
12.延迟队列—实现未支付订单状态更改
背景:
在系统中,存在下单操作。而下单的支付操作对接的一般也是第三方平台的支付系统。用户下单生成订单,订单状态为待支付,等待第三方支付回调更新状态。在规定时间内:如 15 分钟内,如果订单仍然为 待支付状态,修改订单状态为 取消。

RabbitMQ普通集群模式,镜像集群模式?

普通:它的元数据( Queue 的配置信息)会在所有的 RabbitMQ 实例中进行同步

镜像集群模式(高可用性):创建的queue,元数据和 queue 里的消息都会存在多个实例上,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据。然后每次你写消息到 queue 时,都自动把消息同步到多个实例queue 上。

10.Dubbo

为什么要用 Dubbo?与OpenFeign的不同?

Dubbo的分层?

Dubbo 工作原理?

为什么通过代理对象通信?

什么是负载均衡? Dubbo 提供的负载均衡策略?

说说服务暴露的流程?

说说服务引用的流程?

集群容错方式有哪些?

了解Dubbo SPI机制吗?

RPC原理是什么?如果让你实现一个RPC框架怎么设计?

11.Nginx

正向代理和反向代理?使用反向代理的优点 ?  参考

为啥Nginx性能这么高(基于什么事件机制)?Nginx应用场景?Nginx怎么处理请求的?

Nginx目录结构有哪些?

Nginx配置文件nginx.conf有哪些属性模块?*

worker_processes  1;                					# worker进程的数量
events {                              					# 事件区块开始
    worker_connections  1024;            				# 每个worker进程支持的最大连接数
}                                    					# 事件区块结束
http {                               					# HTTP区块开始
    include       mime.types;            				# Nginx支持的媒体类型库文件
    default_type  application/octet-stream;     		# 默认的媒体类型
    sendfile        on;       							# 开启高效传输模式
    keepalive_timeout  65;       						# 连接超时
    server {            								# 第一个Server区块开始,表示一个独立的虚拟主机站点
        listen       80;      							# 提供服务的端口,默认80
        server_name  localhost;       					# 提供服务的域名主机名
        location / {            						# 第一个location区块开始
            root   html;       						# 站点的根目录,相当于Nginx的安装目录
            index  index.html index.htm;      			# 默认的首页文件,多个用空格分开
        }          										# 第一个location区块结果
        error_page   500502503504  /50x.html;     		# 出现对应的http状态码时,使用50x.html回应客户
        location = /50x.html {          				# location区块开始,访问50x.html
            root   html;      							# 指定对应的站点目录为html
        }
    }  
    ......

Nginx静态资源是什么? 怎么实现动静分离?

如何用Nginx解决前端跨域问题?

Nginx虚拟主机怎么配置?(基于虚拟主机配置域名和基于端口的虚拟主机)

Location的作用是什么?语法能说出来吗?Location正则案例限流怎么做的?正常限制访问频率(正常流量)?突发呢(突发流量)?怎么限制并发连接数? 
 

Nginx负载均衡的算法怎么实现的?策略有哪些? 

轮询(默认),权重weight,ip_hash(IP绑定),fair(第三方插件),url_hash(第三方插件)

least_conn 最小连接  

权重  : weight代表权重默认为1,权重越高被分配的客户端越多 

upstream server_pool 
    server 192.168.5.21:80 weight=2;
    server 192.168.5.22:80;
}

ip_hash:每个请求按访问ip的hash结果分配, 这样每个访客固定访问一个后端服务器,可以解诀session的问题。例如:

upstream server_pool 
    ip_hash
    server 192.168.5.21:80;
    server 192.168.5.22:80;
}

Nginx怎么配置高可用?

Nginx怎么判断别IP不可访问?

怎么限制浏览器访问?

Rewrite全局变量是什么? 

Rewrite主要实现url地址重写,以及重定向,就是把传入web的请求重定向到其他url

12.Maven

什么是Maven的坐标?

添加第三方jar包,将项目拆分为多个工程模块,maven的坐标通过groupId,artifactId,version唯一标志一个构件。groupId通常为公司或组织名字,artifactId通常为项目名称,versionId为版本号。

讲一下maven的生命周期

开始->validate->compile->clean->test->package->verify->deploy

说说你熟悉哪些maven命令?

mvn archetype:generate 创建Maven项目,mvn compile 编译源代码,mvn deploy 发布项目,mvn test-compile 编译测试源代码,mvn test 运行应用程序中的单元测试,mvn site 生成项目相关信息的网站,mvn clean 清除项目目录中的生成结果,mvn package 根据项目生成的jar,mvn install 在本地Repository中安装jar mvn eclipse:eclipse 生成eclipse项目文件,mvnjetty:run 启动jetty务,mvn tomcat:run 启动tomcat服务,mvn clean package -Dmaven.test.skip=true:清除以前的包后重新打包,跳过测试类

如何解决依赖传递引起的版本冲突?

可通过dependency的exclusion元素排除掉依赖

说说maven的依赖原则

  最短路径原则(依赖传递的路径越短越优先),pom文件申明顺序优先(路径长度一样,则先申明的优先),覆写原则(当前pom文件里申明的直接覆盖父工程传过来的)

说说依赖的解析机制?

当依赖范围是 system 的时候,Maven 直接从本地文件系统中解析构件。

根据依赖坐标计算仓库路径,尝试直接从本地仓库寻找构件,如果发现对应构件,就解析成功。 如果在本地仓库不存在相应的构件,就遍历所有远程仓库,发现下载并解析使用。

如果依赖的版本是 RELEASE 或 LATEST,SNAPSHOT,就基于更新策略读取所有远程仓库的元数据文件(groupId/artifactId/maven-metadata.xml),将其与本地仓库的对应元合并后,计算出RELEASE 或者 LATEST 真实的值,最新快照版本的值,然后基于该值检查本地仓库,或从远程仓库下载。如果最后解析得到的构件版本包含有时间戳,先将该文件下载下来,再将文件名中时间戳信息删除,剩下 SNAPSHOT 并使用(以非时间戳的形式使用)。

说说插件的解析机制 ( spring-boot-maven-plugin插件爆红原因)

用到插件时先从本地仓库查插件,如果本地仓库没有则从远程仓库查并下载到本地仓库,当Maven需要的插件在本地仓库不存在时不会去以前给普通依赖配置的远程仓库查找插件,而是需要有专门的插件远程仓库。

13.Docker

虚拟化和容器化有什么区别?什么是 Docker 容器?Docker 运行在哪些平台上?Docker Swarm?

答:在 Linux 发行版上都可运行,Docker Swarm 是容器编排工具,允许我们跨不同主机管理多容器.用 Swarm,我们可将多个 Docker 主机变成单个主机,便于监控和管理.

什么是 DockerFile?

如何从 Docker 镜像创建 Docker 容器?(还有命令)

 答:docker run -it -d <image_name>

Docker Compose 可以使用 JSON 代替 YAML 吗?(可以,说出方法)

如何启动、停止和终止容器?

答:docker images 列出本地主机的镜像,docker pull [OPTIONS] 拉取镜像仓库的镜像,docker search [OPTIONS] 搜索仓库的镜像,docker rmi [OPTIONS] 删除本地镜像,docker rm [OPTIONS] 删除容器,docker run [OPTIONS] 启动镜像,先检查本地是否有镜像,不存在则远程仓库下载,docker build [OPTIONS] 构建镜像,docker ps [OPTIONS] 运行中的镜像容器,docker cp [OPTIONS] 宿主机与容器之间的文件复制

Docker 还可以通过以下云服务在生产中使用?

解释 Docker 三个架构组件?

答:Docker 客户端:构建和运行操作与 Docker 主机通信。Docker 主机:包含 Docker 守护程序、Docker 镜像和 Docker 容器。守护进程建立与Docker Registry 连接。Docker Registry:该组件存储 Docker 镜像。它可以是公共注册表,例如 Docker Hub 或 Docker Cloud,也可以是私有注册表。

如何构建Dockerfile?

使用什么命令将新镜像推送到 Docker Registry?

什么是Docker引擎?

如何访问正在运行的容器?如何列出所有正在运行的容器?

答: docker exec -it <container_id> bash ;

描述 Docker 容器的生命周期?

答:创建容器,运行容器,暂停容器(可选),取消暂停容器(可选),启动容器,停止容器,重启容器,杀死容器,销毁容器。

什么是Docker对象标签?

使用Docker Compose时如何保证容器1先于容器2运行?

答:docker-compose.yml用depends_on;docker-compose up命令按照我们指定的依赖顺序启动和运行服务。

docker create命令有什么作用?

答:创建容器而不启动

14.Git

请描述什么是工作区、暂存区和本地仓库?
工作区:个人克隆项目到本地后,项目所在的文件夹目录
暂存区:用于储存工作区中的变更(增删改等改动)的文件的地方.操作时使用git add会将本地所有的变更提交到暂存区中
本地仓库:用于储存工作区和暂存区中提交上来的文件,使用git commit -m ‘提交内容的描述’

在这里插入图片描述

列举工作中常用的git命令
1.新增文件:git add file或者git add   2.提交文件:git commit –m或git commit –a

3.查看工作区状况:git status –s   4.拉取合并远程分支的操作:git fetch/git merge或者git pull

5.查看提交记录:git reflog  6.创建仓库:git init  7.查看仓库的状态:git status

8.这次相较上次修改了哪些内容:git diff   9.将添加的文件放到栈存区中:git add

10.将栈存区内容提交到代码区中:git commit  11.将远程仓库的代码克隆到本地:git clone git地址

12.查看当前分支:git branch  13.切换分支:git checkout

提交时发生冲突,你能解释冲突是如何产生的吗?你是如何解决的?
在合并分支的时候,master分支和dev分支恰好有人都修改了同一个文件,GIT不知道应该以哪一个人的文件为准,所以就产生了冲突了。手动解决。

如果本次提交误操作,如何撤销?


可以先用git reflog查看历史提交记录

1.软撤销

​ 本地代码不会变化,只是 git 转改会恢复为 commit 之前的状态

​ 不删除工作空间改动代码,撤销 commit,不撤销 git add .

​ git reset --soft HEAD~1 //表示撤销最后一次的 commit ,1 可以换成其他更早的数字

2.硬撤销

​ 本地代码会直接变更为指定的提交版本,慎用

​ 删除工作空间改动代码,撤销 commit,撤销 git add .

​ git reset --hard HEAD~1 //注意完成这个操作后,就恢复到了上一次的commit状态

​ commit 的消息内容填错了

​ 输入git commit --amend,​ 进入 vim 模式,对 message 进行更改

​ 还有一个 --mixed

​ git reset --mixed HEAD~1

​ 意思是:不删除工作空间改动代码,撤销commit,并且撤销git add . 操作

​ 这个为默认参数,git reset --mixed HEAD~1 和 git reset HEAD~1 效果是一样的。

git跟其他版本控制器有啥区别

Git以外的版本控制系统使用增量存储方式来保存不同版本。概念上说,所有系统都是以文件为基础来保存信息。

Git并不这样看待数据。Git将数据视为mini filesystem的快照。每次commit,或在Git中保存项目的状态,将文件在的状态做一个快照,保存对那个快照的引用。如果文件本身没有做变更,git并不会重新保存一份,而仅仅重新引用这个已经保存过的文件快照。

在本地工程常会修改一些配置文件,不需要被提交,又不想每次执行git status时都让这些文件显示出来,该如何操作?
首先利用命令touch .gitignore新建文件:touch .gitignore,然后往文件中添加需要忽略哪些文件夹下的什么类型的文件:

/target/class
.settings
.imp
*.ini

注意:忽略/target/class文件夹下所有后缀名为.settings,.imp的文件,忽略所有后缀名为.ini的文件。

如何把本地仓库的内容推向一个空的远程仓库?

1.首先确保本地仓库与远程之间是连同的。如果提交失败,则需要进行下面的命令进行连通:

git remote add origin XXXX 注意:XXXX是你的远程仓库地址。

2.如果是第一次推送,则进行下面命令:

git push -u origin master 注意:-u 是指定origin为默认主分支

3.之后的提交,只需要下面的命令: ​ git push origin master

如果分支是否已合并为master,你可以通过什么手段知道?
git branch –merged 它列出了已合并到当前分支的分支。

git branch –no-merged 它列出了尚未合并的分支。

如何在Git中创建存储库
要创建存储库,先为项目创建一个目录然后运行命令 git init。将在项目的目录中创建 .git 目录

请写出查看分支、创建分支、删除分支、切换分支、合并分支的命令以及写出解决冲突的思路?
git branch 查看分支 git barnch -a查看所有的分支 git branch -r查看远程所有的分支
git checkout -b创建分支 git branch -d 删除分支  git checkout master 切换分支
git merge 合并分支

在主分支上进行了修改并提交 在次分支上进行了修改并提交 然后在合并的话会发生冲突
解决办法是:
在开发工具vscode中手动选择合并方式 一般都是双方都保留更改
冲突只发生在共同的地方

请写出将工作区文件推送到远程仓库的思路?
方案一:
用命令行将本地仓库推送到远程仓库。
首先先创建一个远程仓库待用,然后在本地建立git版本管理,在项目目录下进行以下操作
git init 项目目录下会多一个.git文件  git add . 添加所有文件
git commit -m“first commit” 提交文件
git remote add origin //添加到远程仓库
git push -u origin master //第一次推送到远程仓库需要 -u,将本地master推送到远程master,以后就直接Git push 就好了

方案二:
用命令行创建一个新的仓库
如果已经拉取了git仓库,那我们只需要在该仓库下进行项目的新建等操作即可
首先先创建一个本地文件
然后git init 初始化git
git add 文件 添加本地文件到暂存区
git commit -m“first commit” 提交文件
git remote add origin //添加到远程仓库
git push -u origin master //第一次推送到远程仓库需要 -u,将本地master推送到远程master,以后就直接Git push 就好了

请写出配置ssh的思
首先通过win+r打开命令提示窗口输入
git config –global user.name ‘xxxxx’  git config –global user.email ‘xxxxx’
来设置git的user name和email 然后开始生成密钥 ssh-keygen -t rsa -C ‘之前生成的邮箱’
然后执行cd ~/.ssh看是否存在id_rsa和id_rsa.pub,如果存在说明已经有ssh key
然后在文件中找到.ssh,找到公钥id_rsa.pub在记事本中打开,复制里面的内容,在github上添加ssh Key,点击用户头像,选择setting,新建一个ssh key,取个名字,把之前复制的密钥粘贴进去。

参与的多人协同开发,项目都有哪些分支,分支名是什么,每个分支代表什么,以及分支是由谁合并。

master 主分支用来发布 dev 日常开发用的分支  test 测试用的分支  分支最后由小组长合并

在github上创建项目,为小组成员添加权限,小组长搭好框架,把本地项目上传到远程项目 ,新建一个开发分支,所有成员都切换开发分支,在开发分支中开发,最后小组长将开发分支合并master分支上,并解决一些冲突。
首先需要在其他团队里申请到访问权限,然后在其他的团队那里克隆一下文件,然后在本地运行这个文件,创建一个分支,在这个分支里做进一步的操作,最后合并分支解决冲突提交到远程仓库。

15.ElasticSearch

elasticsearch 了解多少,你们公司 es 的集群架构,索引数据大小,分片有多少? 参考1 参考2

调优手段有哪些:设计阶段调优,写入调优,查询调优,其他调优

elasticsearch 的倒排索引是什么?说一下es的索引原理?怎么根据一个词找到对应倒排索引?

elasticsearch 索引数据多了怎么办,如何调优,部署:动态索引层面,存储层面,部署层面

Elasticsearch 是如何实现 master 选举的**

discovery.zen.minimum_master_nodes = 2 防止发生脑裂  有两个master 

PUT /_cluster/settings
{
   "persistent":{
      "discovery.zen.minimum_master_nodes":2
   }
}

描述一下 Elasticsearch 索引文档的过程,更新和删除文档的过程。

描述一下 Elasticsearch 查询数据的过程?

Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法

lucence 内部结构是什么?

如何监控 Elasticsearch 集群状态?

Marvel 让你可以很简单的通过 Kibana 监控 Elasticsearch

Elasticsearch 中的节点(比如共 20 个),其中的 10 个

客户端在和集群连接时,如何选择特定的节点执行请求的?

Elasticsearch 是一个分布式的 RESTful 风格的搜索和数据分析引擎。

Elasticsearch是高度可伸缩的开源全文搜索和分析引擎,它允许您快速和接近实时地存储、搜索和分析大量数据。介绍一下使用Elasticsearch的用例

Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法?

对于 GC 方面,在使用 Elasticsearch 时要注意什么?

Elasticsearch 对于大数据量(上亿量级)的聚合如何实现?

在并发情况下,Elasticsearch 如何保证读写一致?

1、可以通过版本号使用乐观并发控制,

2、另外对于写操作,一致性级别支持 quorum/one/all,默认为 quorum,即只 

有当大多数分片可用时才允许写操作。 

3、对于读操作,可以设置 replication 为 sync(默认),这使得操作在主分片和副 

本分片都完成后才会返回; 

介绍一下你们的个性化搜索方案?

是否了解字典树?

拼写纠错是如何实现的?

假如es数据量越来越多,有些分页查询的场景变慢怎么解决?如何提高查询效率啊?

mysql到es的数据同步怎么做的?通过binlog的方式有什么优缺点?还有别的方式吗?***

16.Nacos

Nacos架构,注册中心的原理?

整个注册中心的注册和发现流程有三个方面来完成:服务的提供方(以下简称server)、服务的消费者(以下简称client)、注册中心(nacos)。

server与nacos的交互过程: 

server向nacos发起注册任务请求,并维持一个心跳检测的定时任务,naocs会通过阻塞队列异步地处理这些请求,并实时的通过UDP推送到client,为防止UDP数据丢失,client也会通过定时任务每隔10s向nacos发送拉取请求,当服务列表改变,nacos再返回。

  1. 使用OpenAPI发起服务注册。
  2. 建立心跳机制,检测服务器状态。
  3. 查询服务器提供方的实例列表
  4. 定时任务,10S拉取一次数据。
  5. 检测服务提供失败异常,基于UDP协议推送更新。

配置中心一般会配置什么内容?什么信息一般不会写到配置中心?

--配置中心一般配置什么?  (会经常变化的配置信息,例如连接池,日志,线程池,限流熔断规则)

--什么配置信息不会写到配置中心? (服务器端口,服务名,服务的注册地址,配置中心 )

项目中为什么要定义bootstrap.yml文件?

配置中心(优先级)-> ①bootstrap.yml -> ②bootstrap.properties -> ③application.yml -> ④application.properties

bootstrap.yml文件优先级高于application.yml文件。

用于指定nacos的位置,(IP和端口),例如项目名称,nacos中配置文件的文件类型,如yml,text等等

在Controller类上加入@RefreshScope注解,启动自动刷新nacos的配置 

Nacos配置中心宕机,我们还可以读取到配置信息吗?

可以从内存中读到,客户端获取了配置中心的配置信息后,会将配置信息在本地内存中存一份。

客服端如何获取配置中心的信息?如何感知配置中心的数据变化?

基于长轮询机制从nacos获取配置信息,所谓的长轮询就是没有配置更新时,会在nacos服务端进行等待

服务启动后没有在配置中心获取我们的数据是什么原因?

  1.    依赖
  2.    配置文件的名字bootstrap.yml
  3.    配置中心的Data Id名字是否正确,缩进关系是否正确
  4.    假如是动态发布,类上是否有@RefreshScope注解

项目中使用的日志规范?项目的日志级别有哪些?

  SLF4J         trace,debug,info,warn,error

Nacos配置管理模型?(Namespace,Group,Service/DataId

在这里插入图片描述

nacos集群选举  raft算法 

Raft算法是通过一切以领导者为准的方式,实现一系列值的共识和各节点日志的一致

17.openFeign

Springcloud Feign 与 openFeign区别?

1)Feign是Spring Cloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用接口,就可以调用服务注册中心的服务。

2)OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中。

openFeign负载均衡策略 

随机(Random)算法:在实例列表中随机选择某个实例
轮询(RoundRobin)算法:按顺序选择实例
最少连接数(Least Connections)算法:每次取连接数最少的实例
一致性哈希(Consistent Hashing)算法:基于一致性哈希算法总是将相同参数的请求落在同一个实例上
权重随机(Weigthd Random):随机取一个数,根据这个数属于哪个范围选择对应的实例

 

Rest和RPC区别? 都是远程调用 

Rest一种架构风格,约束条件和原则。满足约束条件和原则的应用程序或设计就是 RESTful

HTTP协议实现,GET、POST、PUT和DELETE,支持json和xml。易读、灵活、低耦合

RPC 可以基于TCP/UDP,也可以基于HTTP协议进行传输 ,性能高一些。

18.Sentinel

什么是服务熔断?什么是服务降级?

超时、不可用、限流

服务直接的调用,比如在高并发情况下出现进程阻塞,导致当前线程不可用,慢慢的全部线程阻塞,导致服务器雪崩。
服务熔断:相当于保险丝,出现某个异常,直接熔断整个服务,而不是一直等到服务超时。通过维护一个自己的线程池,当线程到达阈值的时候就启动服务降级,如果其他请求继续访问就直接返回fallback的默认值。

19.Seata  

分布式事务方案?

刚性事务 遵循 acid 强一致性

柔性事务 遵循 base 最终一致性

1. 2PC模式 也叫xa协议 是 二阶段提交协议, 三阶段协议,引入超时机制,性能差不满足高并发

2.TCC事务补偿  prepare conmmit rollback 

3.最大努力通知型  通过mq发送请求 ,比如微信支付宝支付成功回调。

4.可靠消息+最终一致性 

20.分布式

分布式和微服务的区别

单体应用:过往的系统只需要启动一个服务完成所有的事情;
微服务:就是把应用拆封成多个(每个完成所有事情的其中一件或几件),达到协同的效果

我们可以去想什么是分布式数据库,比如elasticSearch:
他的节点是可以部署到不同机器上面的,每个节点可以单独保存数据,也可以做副本相互备份;
我们去查询数据的时候,数据可能在A节点也可能在B节点,但是我们不用关心去哪里查,es整个服务会返回给我们(达到多个节点完成一件事情的需求)
 

微服务只是对服务拆分,分布式是对服务部署方面的考量,微服务是可以包含分布式的,但是分布式不一定是微服务;

讲讲你理解的性能评价及测试指标?

TPS: Transaction Per Second 每秒钟系统能够处理的交易或事务的数量

QPS :  每秒查询率(QPS,Queries-per-second)
并发用户数: 系统同时处理的request/事务的用户数量。
响应时间(Response Time,RT): 可以理解为服务器处理响应的耗时,一般取平均响应时间。

如果是对一个接口(单场景)压测,且这个接口内部不会再请求其它接口,TPS=QPS。

例如:访问一个 Index 页面请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这个页面就产生一个“TPS”,产生三个“QPS”。对于衡量单个接口服务的处理能力,一般采用QPS比较多,一般如果衡量事务业务场景的处理能力一般则采用TPS。注:Jmeter聚合报告中,Throughput是用来衡量吞吐量,通常是由TPS来表示。

一般情况下,大型系统(业务量大、机器多)做压力测试看TPS,10000~50000个用户并发,中小型系统做压力测试,5000个用户并发比较常见。

CAP和BASE和AP理论,有哪些组件或者中间件满足AP/CP?

CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性)Nacos 不仅支持 CP 也支持 AP。一致性(Consistency) : 所有节点访问同一份最新的数据副本
可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应)。分区容错性(Partition tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务。CAP 理论中分区容错性 P 是一定要满足的,在此基础上,只能满足可用性 A 或者一致性 C

 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性):分布式方案解决最终一致性。

Session的分布式方案

spring.session.store-type=  redis

如何解决瞬时大流量高并发?  

缓存: 浏览器缓存Java 代码中通过设置响应头,告诉前端浏览器进行缓存:

Nginx 缓存:  expires 指令来实现缓存控制;动静分离;调整配置文件参数

location /static {
root /opt/static/; expires 1d;//全天
}

CDN 的全称是 Content Delivery Network,即内容分发网络,CDN 主要缓存静态资源。

应用拆分,数据库拆分数据库拆分分为:垂直拆分和水平拆分 (分库分表),读写分离。

采用队列是解决高并发大流量,异步处理/流量削峰/系统解耦

池化 对象 数据库 线程池

采用数据搜索引擎  

前端优化 

雪花算法原理?分布式唯一ID(雪花算法和UUID)

使用一个 64 bit 的 long 型的数字作为全局唯一 id

分布式寻址算法?

hash算法 一致性hash算法 hash slot

高并发限流算法漏桶算法* 和 令牌桶算法*, 计数器算法, 滑动时间窗口?

突发流量会进入一个漏桶,漏桶按我们定义的速率依次处理请求,如果水流过大也就是突发流量过大会直接溢出,多余的请求会被拒绝。所以漏桶算法能控制数据的传输速率。

令牌桶算法的机制:一个大小固定的令牌桶,以恒定的速率不断产生令牌。如果令牌消耗速率小于生产令牌的速度,令牌就会一直产生直至装满整个令牌桶。

如何保证接口幂等性  

1.token+redis 机制
比如订单支付场景:
        该支付分为两个步骤:
                1.1 获取全局唯一token
                       接口处理生成唯一标识(token) 存储到redis中,并返回给调用客户端。
                1.2 发起支付操作并附带token
                    接口处理:
                    1.2.1 获得分布式锁(处理并发情况)
                    1.2.2 判断redis中是否存在token
                    1.2.3 存在 执行支付业务逻辑,否则返回该订单已经支付
                    1.2.4 释放分布式锁

思考:为什么要加分布式锁?
原因1:在高并发请求中 ,token判断是否存在是非线程安全的,所以要加分布式锁来保证 该条件的判断为线程安全
        注释:也可redis用删除操作来判断token,删除成功代表token校验通过 这个删除是原子操作的

原因2:在支付业务中,判断支付订单是否已经存在,存在说明该订单已经支付过了,不存在就执行扣款操作,如果相同操作并发两个请求来到判断条件可能两个请求都能判断支付订单不存在,造成重复扣款。 所以也要加分布式锁保证线程的安全。
 

防重表

1.利用数据库建一张防重表(加唯一索引)

     比如订单支付,
        反正重复支付:订单号插入防重表 成功 执行支付业务逻辑,失败说明已经支付过。

防重表支付成功是否要删除:
1.可定期清除数据
2.也可结合 订单状态 ,在支付前查询订单状态为待支付 执行支付操作 ,操作后删除订单号 若 第二个请求插入防重表成功,但是这个时候查询订单状态失败。
(实际这个防重表就是实现了分布式锁)

缓存队列

将请求放入队列,后续使用异步任务处理队列中的数据,过滤掉重复的消息。 和防止重复消费道理是一样。
 此外 cas和 乐观锁也可以。

21.开发实操

开发文档编写 

功能概述--数据库设计--接口URL--请求参数--流程和逻辑--关键数据结构 

Jenkins 

Jenkins是开源CI&CD软件领导者, 1000个插件来支持构建、部署、自动化, 满足任何项目的需要。Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。
CI(Continuous integration持续集成)持续集成强调开发人员提交了新代码之后,立刻进行构建、(单元)测试。
CD(Continuous Delivery持续交付) 是在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境(类生产环境)中 。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值