diff --git a/JavaIO.md b/JavaIO.md new file mode 100644 index 0000000..d9d8cbe --- /dev/null +++ b/JavaIO.md @@ -0,0 +1,154 @@ +## Java IO + + +* [1.Java 中有几种类型的流?](#1java-中有几种类型的流) +* [2.什么是 java序列化?](#2什么是-java序列化) +* [3.如何实现 java 序列化?](#3如何实现-java-序列化) +* [4.字节流和字符流的区别?](#4字节流和字符流的区别) +* [5.PrintStream、BufferedWriter、PrintWriter的比较?](#5printstreambufferedwriterprintwriter的比较) +* [6.什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征?](#6什么是节点流什么是处理流它们各有什么用处处理流的创建有什么特征) +* [7.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?](#7流一般需要不需要关闭如果关闭的话在用什么方法一般要在那个代码块里面关闭比较好处理流是怎么关闭的如果有多个流互相调用传入是怎么关闭的) +* [8.什么是BIO](#8什么是bio) +* [9.什么是NIO](#9什么是nio) +* [10.什么是AIO](#10什么是aio) +* [11.同步与异步](#11同步与异步) +* [12.阻塞与非阻塞](#12阻塞与非阻塞) +* [13.同步、异步、阻塞、非堵塞](#13同步异步阻塞非堵塞) +* [14.通道是个什么意思?](#14通道是个什么意思) +* [15.缓冲区是什么意思?](#15缓冲区是什么意思) +* [16.IO多路复用的底层原理](#16io多路复用的底层原理) +* [参考链接](#参考链接) + + + + +#### 1.Java 中有几种类型的流? + +(1)按照流的方向:输入流(inputStream)和输出流(outputStream);(2)按照实现功能分:节点流(可以从或向一个特定的地方(节点)读写数据。如 FileReader)和处理流(是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接);(3)按照处理数据的单位: 字节流和字符流。字节流继承于 InputStream 和 OutputStrea,字符流继承于InputStreamReader 和 OutputStreamWriter 。 + +#### 2.什么是 java序列化? + +序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题 + +#### 3.如何实现 java 序列化? + +序列化的实现,将需要被序列化的类实现Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:File Output Stream)来构造一个 Object Output Stream(对象流)对象,接着,使用 Object Output Stream 对象的 write Object(Object obj)方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。 + +#### 4.字节流和字符流的区别? + +字节流读取的时候,读到一个字节就返回一个字节;字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在 UTF-8 码表中是 3 个字节)时。先去查指定的编码表,将查到的字符返回。字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。字节流主要是操作 byte 类型数据,以 byte 数组为准,主要操作类就是 OutputStream、InputStream字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。所以字符流是由 Java 虚拟机将字节转化为 2 个字节的 Unicode 字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。在程序中一个字符等于两个字节,java 提供了 Reader、Writer 两个专门操作字符流的类。 + +#### 5.PrintStream、BufferedWriter、PrintWriter的比较? + +1. PrintStream类的输出功能非常强大,通常如果需要输出文本内容,都应该将输出流包装成PrintStream后进行输出。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError 方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream +2. BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符串的高效写入。通过write()方法可以将获取到的字符输出,然后通过newLine()进行换行操作。BufferedWriter中的字符流必须通过调用flush方法才能将其刷出去。并且BufferedWriter只能对字符流进行操作。如果要对字节流操作,则使用BufferedInputStream +3. PrintWriter的println方法自动添加换行,不会抛异常,若关心异常,需要调用checkError方法看是否有异常发生,PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush) + +#### 6.什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征? + +1. 节点流 直接与数据源相连,用于输入或者输出 +2. 处理流:在节点流的基础上对之进行加工,进行一些功能的扩展 +3. 处理流的构造器必须要 传入节点流的子类 + +#### 7.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的? + +1. 流一旦打开就必须关闭,使用close方法 +2. 放入finally语句块中(finally 语句一定会执行) +3. 调用的处理流就关闭处理流 +4. 多个流互相调用只关闭最外层的流 + +#### 8.什么是BIO + +BIO 就是传统的 [java.io](http://java.io/) 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。 + +#### 9.什么是NIO + +是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。 + +#### 10.什么是AIO + +AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作 + +#### 11.同步与异步 + +同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。而异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。我们可以用打电话和发短信来很好的比喻同步与异步操作。 + +#### 12.阻塞与非阻塞 + +阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在这个慢的操作在执行时 CPU 去干其它别的事,等这个慢的操作完成时,CPU 再接着完成后续的操作。虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估。 + +#### 13.同步、异步、阻塞、非堵塞 + +同/异、阻/非堵塞的组合,有四种类型,如下表: + +| 组合方式 | 性能分析 | +| ---------- | ------------------------------------------------------------ | +| 同步阻塞 | 最常用的一种用法,使用也是最简单的,但是 I/O 性能一般很差,CPU 大部分在空闲状态。 | +| 同步非阻塞 | 提升 I/O 性能的常用手段,就是将 I/O 的阻塞改成非阻塞方式,尤其在网络 I/O 是长连接,同时传输数据也不是很多的情况下,提升性能非常有效。 这种方式通常能提升 I/O 性能,但是会增加CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗,也就是系统的瓶颈是在 I/O 还是在 CPU 上。 | +| 异步阻塞 | 这种方式在分布式数据库中经常用到,例如在网一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其它机器上,这些备份记录通常都是采用异步阻塞的方式写 I/O。异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况。 | +| 异步非阻塞 | 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之间的消息同步机制一般用这种 I/O 组合方式。如 Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。它适合同时要传多份相同的数据到集群中不同的机器,同时数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高。 | + +#### 14.通道是个什么意思? + +- 通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象(通道)。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。 +- 正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。 + +#### 15.缓冲区是什么意思? + +- Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中 +- 在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。 +- 缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程 + +ByteBuffer +CharBuffer +ShortBuffer +IntBuffer +LongBuffer +FloatBuffer +DoubleBuffer + +#### 16.IO多路复用的底层原理 + +IO多路复用使用两个系统调用(select/poll/epoll和recvfrom),blocking IO只调用了recvfrom;select/poll/epoll 核心是可以同时处理多个connection,而不是更快,所以连接数不高的话,性能不一定比多线程+阻塞IO好,多路复用模型中,每一个socket,设置为non-blocking,阻塞是被select这个函数block,而不是被socket阻塞的。 + +1)select机制 + +客户端操作服务器时就会产生这三种文件描述符(简称fd):writefds(写)、readfds(读)、和exceptfds(异常)。select会阻塞住监视3类文件描述符,等有数据、可读、可写、出异常 或超时、就会返回;返回后通过遍历fdset整个数组来找到就绪的描述符fd,然后进行对应的IO操作。 + +优点: +  几乎在所有的平台上支持,跨平台支持性好 +缺点: +  由于是采用轮询方式全盘扫描,会随着文件描述符FD数量增多而性能下降。 +  每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间) +  默认单个进程打开的FD有限制是1024个,可修改宏定义,但是效率仍然慢。 + +2)poll机制 + +基本原理与select一致,只是没有最大文件描述符限制,因为采用的是链表存储fd。 + +3)epoll机制 + +epoll之所以高性能是得益于它的三个函数 +  1)epoll_create()系统启动时,在Linux内核里面申请一个B+树结构文件系统,返回epoll对象,也是一个fd +  2)epoll_ctl() 每新建一个连接,都通过该函数操作epoll对象,在这个对象里面修改添加删除对应的链接fd, 绑定一个callback函数。 +  3)epoll_wait() 轮训所有的callback集合,并完成对应的IO操作 + +优点: +  没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个句柄 +  效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降 +  内核和用户空间mmap同一块内存实现 + + +### 参考链接 + +https://zhuanlan.zhihu.com/p/124230020 + +https://blog.csdn.net/chengyuqiang/article/details/79183748 + +https://www.wkcto.com/article/detail/259 + +https://www.cnblogs.com/lanqingzhou/p/13609317.html + +https://www.imooc.com/article/265871 + +https://blog.csdn.net/chenwiehuang/article/details/105296691 diff --git "a/Java\345\237\272\347\241\200.md" "b/Java\345\237\272\347\241\200.md" new file mode 100644 index 0000000..94766a8 --- /dev/null +++ "b/Java\345\237\272\347\241\200.md" @@ -0,0 +1,552 @@ +## Java基础 + + +* [1.说下面向对象四大特性](#1说下面向对象四大特性) +* [2.Java语言有哪些特点](#2java语言有哪些特点) +* [3.什么是Java程序的主类?应用程序和小程序的主类有何不同?](#3什么是java程序的主类应用程序和小程序的主类有何不同) +* [4.访问修饰符public,private,protected,以及不写(默认)时的区别?](#4访问修饰符publicprivateprotected以及不写默认时的区别) +* [5.float f=3.4;是否正确?](#5float-f34是否正确) +* [6.Java有没有goto?](#6java有没有goto) +* [7.&和&&的区别?](#7和的区别) +* [8.Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?](#8mathround115-等于多少mathround-115等于多少) +* [9.用最有效率的方法计算2乘以8?](#9用最有效率的方法计算2乘以8) +* [10.什么是Java注释](#10什么是java注释) +* [11.Java有哪些数据类型](#11java有哪些数据类型) +* [12.final 有什么用?](#12final-有什么用) +* [13.final finally finalize的区别](#13final-finally-finalize的区别) +* [14.String str = "i" 和String str = new String("1")一样吗?](#14string-str--i-和string-str--new-string1一样吗) +* [15.Java 中操作字符串都有哪些类?它们之间有什么区别?](#15java-中操作字符串都有哪些类它们之间有什么区别) +* [16.Java中为什么要用 clone?](#16java中为什么要用-clone) +* [17.深克隆和浅克隆?](#17深克隆和浅克隆) +* [18.new一个对象的过程和clone一个对象的区别?](#18new一个对象的过程和clone一个对象的区别) +* [19.Java中实现多态的机制是什么?](#19java中实现多态的机制是什么) +* [20.谈谈你对多态的理解?](#20谈谈你对多态的理解) +* [21.构造器(constructor)是否可被重写(override)?](#21构造器constructor是否可被重写override) +* [22.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?](#22两个对象值相同xequalsy--true但却可有不同的hash-code这句话对不对) +* [23.是否可以继承String类?](#23是否可以继承string类) +* [24.String类的常用方法有哪些?](#24string类的常用方法有哪些) +* [25.char型变量中能否能不能存储一个中文汉字,为什么?](#25char型变量中能否能不能存储一个中文汉字为什么) +* [26.this关键字的用法](#26this关键字的用法) +* [27.super关键字的用法](#27super关键字的用法) +* [28.this与super的区别](#28this与super的区别) +* [29.static存在的主要意义](#29static存在的主要意义) +* [30.static的独特之处](#30static的独特之处) +* [31.static应用场景](#31static应用场景) +* [32.static注意事项](#32static注意事项) +* [33.break ,continue ,return 的区别及作用](#33break-continue-return-的区别及作用) +* [34.在Java中定义一个不做事且没有参数的构造方法的作用](#34在java中定义一个不做事且没有参数的构造方法的作用) +* [35.构造方法有哪些特性?](#35构造方法有哪些特性) +* [36.静态变量和实例变量区别](#36静态变量和实例变量区别) +* [37.静态方法和实例方法有何不同?](#37静态方法和实例方法有何不同) +* [38.什么是方法的返回值?返回值的作用是什么?](#38什么是方法的返回值返回值的作用是什么) +* [39.什么是内部类?](#39什么是内部类) +* [40.内部类的分类有哪些](#40内部类的分类有哪些) +* [41.Java中异常分为哪些种类?](#41java中异常分为哪些种类) +* [42.hashCode 与 equals (重要)](#42hashcode-与-equals-重要) +* [43.hashCode()介绍](#43hashcode介绍) +* [44.为什么要有 hashCode](#44为什么要有-hashcode) +* [45.抽象类和接口(Java7)的区别](#45抽象类和接口java7的区别) +* [46.Java 8的接口新增了哪些特性?](#46java-8的接口新增了哪些特性) +* [47.重写和重载的区别](#47重写和重载的区别) +* [48.ArrayList和LinkedList有什么区别?](#48arraylist和linkedlist有什么区别) +* [49.HashMap是怎么实现的?](#49hashmap是怎么实现的) +* [50.HashMap在Java7和Java8中的实现有什么不同?](#50hashmap在java7和java8中的实现有什么不同) +* [51.HashMap有时候会死循环,你知道是什么原因吗?](#51hashmap有时候会死循环你知道是什么原因吗) +* [52.ConcurrentHashMap是怎么实现的?](#52concurrenthashmap是怎么实现的) +* [53.静态代理和动态代理的区别](#53静态代理和动态代理的区别) +* [54.JDK动态代理和CGLIB动态代理的区别](#54jdk动态代理和cglib动态代理的区别) +* [参考链接](#参考链接) + + + + + +## Java基础 + +#### 1.说下面向对象四大特性 + +封装、继承、多态、抽象。 + +#### 2.Java语言有哪些特点 + +简单易学(Java语言的语法与C语言和C++语言很接近) + +面向对象(封装,继承,多态) + +平台无关性(Java虚拟机实现平台无关性) + +支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的) + +支持多线程(多线程机制使应用程序在同一时间并行执行多项任) + +健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等) + +安全性 + +#### 3.什么是Java程序的主类?应用程序和小程序的主类有何不同? + +一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。而在Java小程序中,这个主类是一个继承自系统类JApplet或Applet的子类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。 + +#### 4.访问修饰符public,private,protected,以及不写(默认)时的区别? + +| 修饰符 | 当前类 | 同 包 | 子 类 | 其他包 | +| --------- | ------ | ----- | ----- | ------ | +| public | √ | √ | √ | √ | +| protected | √ | √ | √ | × | +| default | √ | √ | × | × | +| private | √ | × | × | × | + +类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。 + +#### 5.float f=3.4;是否正确? + +不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。 + +#### 6.Java有没有goto? + +goto 是Java中的保留字,在目前版本的Java中没有使用。(根据James Gosling(Java之父)编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字) + +#### 7.&和&&的区别? + +&运算符有两种用法:(1)按位与;(2)逻辑与。 + +&&运算符是短路与运算。 + +逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。 + +#### 8.Math.round(11.5) 等于多少?Math.round(-11.5)等于多少? + +Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。 + +#### 9.用最有效率的方法计算2乘以8? + +2 << 3 + +#### 10.什么是Java注释 + +定义:用于解释说明程序的文字 + +Java注释的分类 +- 单行注释 + 格式: // 注释文字 +- 多行注释 + 格式: /* 注释文字 */ +- 文档注释 + 格式:/** 注释文字 */ + +Java注释的作用 +在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。 + +注意事项:多行和文档注释都不能嵌套使用。 + +#### 11.Java有哪些数据类型 + +定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。 + +- 基本数据类型 + - 数值型 + - 整数类型(byte,short,int,long) + - 浮点类型(float,double) + - 字符型(char) + - 布尔型(boolean) +- 引用数据类型 + - 类(class) + - 接口(interface) + - 数组([]) + +#### 12.final 有什么用? + +用于修饰类、属性和方法; + +- 被final修饰的类不可以被继承 +- 被final修饰的方法不可以被重写 +- 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的 + +#### 13.final finally finalize的区别 + +- final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表 + 示该变量是一个常量不能被重新赋值。 +- finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块 + 中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。 +- finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调 + 用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的 + 最后判断。 + +#### 14.String str = "i" 和String str = new String("1")一样吗? + +不一样,因为内存的分配方式不一样。String str = "i"的方式JVM会将其分配到常量池中,而String str = new String("i")JVM会将其分配到堆内存中。 + +#### 15.Java 中操作字符串都有哪些类?它们之间有什么区别? + +操作字符串的类有:String、StringBuffer、StringBuilder。 +String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,再将指针指向新的 String 对象,而 StringBuffer 、 StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。 +StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。 + +#### 16.Java中为什么要用 clone? + +在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说,A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但clone()方法是其中最简单,也是最高效的手段。 + +#### 17.深克隆和浅克隆? + +浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 + + +深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。 + +#### 18.new一个对象的过程和clone一个对象的区别? + +new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。 + +clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。 + +#### 19.Java中实现多态的机制是什么? + +Java中的多态靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。 + +#### 20.谈谈你对多态的理解? + +多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源代码,就可以让引用变量绑定到各种不同的对象上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。 + +#### 21.构造器(constructor)是否可被重写(override)? + +答:构造器不能被继承,因此不能被重写,但可以被重载。 + +#### 22.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? + +不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。 + +Java对于eqauls方法和hashCode方法是这样规定的: + +(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同; + +(2)如果两个对象的hashCode相同,它们并不一定相同。 + +当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。 + +#### 23.是否可以继承String类? + +String 类是final类,不可以被继承。 + +补充:继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。 + +#### 24.String类的常用方法有哪些? + +•indexof();返回指定字符的的索引。 + +•charAt();返回指定索引处的字符。 + +•replace();字符串替换。 + +•trim();去除字符串两端空格。 + +•splt();字符串分割,返回分割后的字符串数组。 + +•getBytes();返回字符串byte类型数组。 + +•length();返回字符串长度。 + +•toLowerCase();将字符串转换为小写字母。 + +•toUpperCase();将字符串转换为大写字母。 + +•substring();字符串截取。 + +•equals();比较字符串是否相等。 + +#### 25.char型变量中能否能不能存储一个中文汉字,为什么? + +char可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char 类型占2个字节(16 比特),所以放一个中文是没问题的。 + +#### 26.this关键字的用法 + +this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。 + +this的用法在java中大体可以分为3种: + +1.普通的直接引用,this相当于是指向当前对象本身。 + +2.形参与成员名字重名,用this来区分: + +```java +public Person(String name, int age) { + this.name = name; + this.age = age; +} +``` + +3.引用本类的构造函数 + +```java +class Person{ + private String name; + private int age; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + public Person(String name, int age) { + this(name); + this.age = age; + } +} +``` + +#### 27.super关键字的用法 + +super可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。 + +super也有三种用法: + +1.普通的直接引用 + +与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。 + +2.子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分 + +```java +class Person{ + protected String name; + + public Person(String name) { + this.name = name; + } + +} + +class Student extends Person{ + private String name; + + public Student(String name, String name1) { + super(name); + this.name = name1; + } + + public void getInfo(){ + System.out.println(this.name); //Child + System.out.println(super.name); //Father + } + +} + +public class Test { + public static void main(String[] args) { + Student s1 = new Student("Father","Child"); + s1.getInfo(); + + } +} +``` + +3.引用父类构造函数 + +3、引用父类构造函数 + +- super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。 +- this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。 + +#### 28.this与super的区别 + +- super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参) +- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名) +- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。 +- super()和this()均需放在构造方法内第一行。 +- 尽管可以用this调用一个构造器,但却不能调用两个。 +- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。 +- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。 +- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。 + +#### 29.static存在的主要意义 + +static的主要意义是在于创建独立于具体对象的域变量或者方法。 以致于即使没有创建对象,也能使用属性和调用方法 ! + +static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能 。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。 + +为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。 + +#### 30.static的独特之处 + +1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法 不属于任何一个实例对象,而是被类的实例对象所共享 。 + +怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】…我觉得我已经讲的很通俗了,你明白了咩? + +2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。 + +3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的! + +4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。 + +#### 31.static应用场景 + +因为static是被类的实例对象所共享,因此如果 某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量 。 + +因此比较常见的static应用场景有: + +1、修饰成员变量 2、修饰成员方法 3、静态代码块 4、修饰类【只能修饰内部类也就是静态内部类】 5、静态导包 + +#### 32.static注意事项 + +1、静态只能访问静态。 2、非静态既可以访问非静态的,也可以访问静态的。 + +#### 33.break ,continue ,return 的区别及作用 + +break 跳出总上一层循环,不再执行循环(结束当前的循环体) + +continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) + +return 程序返回,不再执行下面的代码(结束当前的方法 直接返回) + +#### 34.在Java中定义一个不做事且没有参数的构造方法的作用 + +Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。 + +#### 35.构造方法有哪些特性? + +名字与类名相同; + +没有返回值,但不能用void声明构造函数; + +生成类的对象时自动执行,无需调用。 + +#### 36.静态变量和实例变量区别 + +静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。 + +实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。 + +#### 37.静态方法和实例方法有何不同? + +静态方法和实例方法的区别主要体现在两个方面: + +1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。 +2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制 + +#### 38.什么是方法的返回值?返回值的作用是什么? + +方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作! + +#### 39.什么是内部类? + +在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是 内部类 。内部类本身就是类的一个属性,与其他属性定义方式一致。 + +#### 40.内部类的分类有哪些 + +内部类可以分为四种: 成员内部类、局部内部类、匿名内部类和静态内部类 。 + + #### 41.Java中异常分为哪些种类? + +按照异常需要处理的时机分为编译时异常(也叫受控异常)也叫 CheckedException 和运行时异常(也叫非受控异常)也叫 UnCheckedException。Java认为Checked异常都是可以被处理的异常,所以Java程序必须显式处理Checked异常。如果程序没有处理Checked 异常,该程序在编译时就会发生错误无法编译。这体现了Java 的设计哲学:没有完善错误处理的代码根本没有机会被执行。对Checked异常处理方法有两种: + +● 第一种:当前方法知道如何处理该异常,则用try...catch块来处理该异常。 + +● 第二种:当前方法不知道如何处理,则在定义该方法时声明抛出该异常。 + +运行时异常只有当代码在运行时才发行的异常,编译的时候不需要try…catch。Runtime如除数是0和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。 + +#### 42.hashCode 与 equals (重要) + +HashSet如何检查重复 + +两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗? + +hashCode和equals方法的关系 + +面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?” + +#### 43.hashCode()介绍 + +hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。 + +散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象) + +#### 44.为什么要有 hashCode + +##### 我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode: + +当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。 + +##### hashCode()与equals()的相关规定 + +如果两个对象相等,则hashcode一定也是相同的 + +两个对象相等,对两个对象分别调用equals方法都返回true + +两个对象有相同的hashcode值,它们也不一定是相等的 + +##### 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖 + +hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据) + +##### 对象的相等与指向他们的引用相等,两者有什么不同? + +对象的相等 比的是内存中存放的内容是否相等而 引用相等 比较的是他们指向的内存地址是否相等。 + +#### 45.抽象类和接口(Java7)的区别 + +1. 抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法; +2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的; +3. 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法; +4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。 + +#### 46.Java 8的接口新增了哪些特性? + +增加了default方法和static方法,这2种方法可以有方法体。 + +#### 47.重写和重载的区别 + +重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写! +重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。 +重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。 + +重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。 +每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。 + + +#### 48.ArrayList和LinkedList有什么区别? + +1. ArrayList和LinkedList的差别主要来自于Array和LinkedList数据结构的不同。ArrayList是基于数组实现的,LinkedList是基于双链表实现的。另外LinkedList类不仅是List接口的实现类,可以根据索引来随机访问集合中的元素,除此之外,LinkedList还实现了Deque接口,Deque接口是Queue接口的子接口,它代表一个双向队列,因此LinkedList可以作为双向队列 ,栈(可以参见Deque提供的接口方法)和List集合使用,功能强大。 + +2. 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。Array获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大的,因为这需要移动数组中插入位置之后的的所有元素。 + +3. 相对于ArrayList,LinkedList的随机访问集合元素时性能较差,因为需要在双向列表中找到要index的位置,再返回;但在插入,删除操作是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。 + +4. LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。 + +#### 49.HashMap是怎么实现的? + +详见:https://blog.csdn.net/woshimaxiao1/article/details/83661464 + +#### 50.HashMap在Java7和Java8中的实现有什么不同? + +详见:https://blog.csdn.net/qq_36520235/article/details/82417949 + +#### 51.HashMap有时候会死循环,你知道是什么原因吗? + +详见:https://www.cnblogs.com/williamjie/p/11089522.html + +#### 52.ConcurrentHashMap是怎么实现的? + +详见:https://www.infoq.cn/article/ConcurrentHashMap/ + +#### 53.静态代理和动态代理的区别 + +静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委托类都需要修改。 + +#### 54.JDK动态代理和CGLIB动态代理的区别 + +JDK动态代理只能对实现了接口的类生成代理,而不能针对类。 +CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final。 + + + +#### 参考链接 + +https://blog.csdn.net/jackfrued/article/details/44921941 + +https://www.cnblogs.com/Java-JJ/p/12625888.html + +http://www.mamicode.com/info-detail-3005972.html + + diff --git "a/Java\350\231\232\346\213\237\346\234\272.md" "b/Java\350\231\232\346\213\237\346\234\272.md" new file mode 100644 index 0000000..6c55fac --- /dev/null +++ "b/Java\350\231\232\346\213\237\346\234\272.md" @@ -0,0 +1,449 @@ +## Java虚拟机 + + +* [1.说一下JVM的内存结构?](#1说一下jvm的内存结构) +* [2.栈帧里面包含哪些东西?](#2栈帧里面包含哪些东西) +* [3.程序计数器有什么作用?](#3程序计数器有什么作用) +* [4.字符串常量存放在哪个区域?](#4字符串常量存放在哪个区域) +* [5.你熟悉哪些垃圾收集算法?](#5你熟悉哪些垃圾收集算法) +* [6.Java里有哪些引用类型?](#6java里有哪些引用类型) +* [7.JVM怎么判断一个对象是不是要回收?](#7jvm怎么判断一个对象是不是要回收) +* [8.GC Roots 有哪些?](#8gc-roots-有哪些) +* [9.你知道哪些GC类型?](#9你知道哪些gc类型) +* [10.对象都是优先分配在年轻代上的吗?](#10对象都是优先分配在年轻代上的吗) +* [11.你了解过哪些垃圾收集器?](#11你了解过哪些垃圾收集器) +* [12.说说CMS垃圾收集器的工作原理](#12说说cms垃圾收集器的工作原理) +* [13.说说G1垃圾收集器的工作原理](#13说说g1垃圾收集器的工作原理) +* [14.说说ZGC垃圾收集器的工作原理](#14说说zgc垃圾收集器的工作原理) +* [15.ZGC收集器中的染色指针有什么用?](#15zgc收集器中的染色指针有什么用) +* [16.说说类加载的过程](#16说说类加载的过程) +* [17.说下有哪些类加载器?](#17说下有哪些类加载器) +* [18.什么是双亲委派机制?](#18什么是双亲委派机制) +* [19.双亲委派机制可以被违背吗?请举例说明。](#19双亲委派机制可以被违背吗请举例说明) +* [20.Tomcat是怎么打破双亲委派机制的呢?](#20tomcat是怎么打破双亲委派机制的呢) +* [21.Java对象的布局了解过吗?](#21java对象的布局了解过吗) +* [22.什么情况下会发生栈内存溢出?](#22什么情况下会发生栈内存溢出) +* [23.JVM新生代中为什么要分为Eden和Survivor?](#23jvm新生代中为什么要分为eden和survivor) +* [24.JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代?](#24jvm中一次完整的gc流程是怎样的对象如何晋升到老年代) +* [25.什么是指令重排序?](#25什么是指令重排序) +* [26.什么是内存屏障?](#26什么是内存屏障) +* [27.什么是happen-before原则?](#27什么是happen-before原则) +* [28.说说你知道的几种主要的JVM参数](#28说说你知道的几种主要的jvm参数) +* [29.怎么打出线程栈信息?](#29怎么打出线程栈信息) +* [30.为什么需要双亲委派模式?](#30为什么需要双亲委派模式) +* [31.怎么打破双亲委派模型?](#31怎么打破双亲委派模型) +* [32.说一下堆和栈的区别](#32说一下堆和栈的区别) +* [33.Java 8 为什么要将永久代(PermGen)替换为元空间(MetaSpace)呢?](#33java-8-为什么要将永久代permgen替换为元空间metaspace呢) +* [34.说一下Java对象的创建过程](#34说一下java对象的创建过程) +* [35.对象的访问定位有哪几种方式?](#35对象的访问定位有哪几种方式) +* [36.说一下堆内存中对象的分配的基本策略](#36说一下堆内存中对象的分配的基本策略) +* [37.Minor Gc和Full GC 有什么不同呢?](#37minor-gc和full-gc-有什么不同呢) +* [38.Java会存在内存泄漏吗?请简单描述。](#38java会存在内存泄漏吗请简单描述) +* [39.如何判断一个类是无用的类?](#39如何判断一个类是无用的类) +* [40.介绍一下类文件结构吧!](#40介绍一下类文件结构吧) +* [41.说一下 JVM 调优的工具?](#41说一下-jvm-调优的工具) +* [42.JVM调优命令有哪些?](#42jvm调优命令有哪些) +* [43.JRE、JDK、JVM 及 JIT 之间有什么不同?](#43jrejdkjvm-及-jit-之间有什么不同) +* [程序计数器为什么是私有的?](#程序计数器为什么是私有的) +* [如何判断一个常量是废弃常量 ?](#如何判断一个常量是废弃常量-) +* [参考资料](#参考资料) + + + +#### 1.说一下JVM的内存结构? + +详见:https://blog.csdn.net/rongtaoup/article/details/89142396 + +#### 2.栈帧里面包含哪些东西? + +局部变量表、操作数栈、动态连接、返回地址等 + +#### 3.程序计数器有什么作用? + +程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。这里面存的,就是当前线程执行的进度。 +程序计数器还存储了当前正在运行的流程,包括正在执行的指令、跳转、分支、循环、异常处理等。 + +#### 4.字符串常量存放在哪个区域? + +1. 字符串常量池,已经移动到堆上(jdk8之前是perm区),也就是执行intern方法后存的地方。 +2. 类文件常量池,constant_pool,是每个类每个接口所拥有的,这部分数据在方法区,也就是元数据区。而运行时常量池是在类加载后的一个内存区域,它们都在元空间。 + +#### 5.你熟悉哪些垃圾收集算法? + +标记清除(缺点是碎片化) +复制算法(缺点是浪费空间) +标记整理算法(效率比前两者差) +分代收集算法(老年代一般使用“标记-清除”、“标记-整理”算法,年轻代一般用复制算法) + +#### 6.Java里有哪些引用类型? + +强引用 +这种引用属于最普通最强硬的一种存在,只有在和 GC Roots 断绝关系时,才会被消灭掉。 + +软引用 +软引用用于维护一些可有可无的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。 +可以看到,这种特性非常适合用在缓存技术上。比如网页缓存、图片缓存等。 +软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。 + +弱引用 +弱引用对象相比较软引用,要更加无用一些,它拥有更短的生命周期。当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。弱引用拥有更短的生命周期,在 Java 中,用 java.lang.ref.WeakReference 类来表示。它的应用场景和软引用类似,可以在一些对内存更加敏感的系统里采用。 + +虚引用 +这是一种形同虚设的引用,在现实场景中用的不是很多。虚引用必须和引用队列(ReferenceQueue)联合使用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。实际上,虚引用的 get,总是返回 null。 + +#### 7.JVM怎么判断一个对象是不是要回收? + +引用计数法(缺点是对于相互引用的对象,无法进行清除) +可达性分析 + +#### 8.GC Roots 有哪些? + +GC Roots 是一组必须活跃的引用。用通俗的话来说,就是程序接下来通过直接引用或者间接引用,能够访问到的潜在被使用的对象。 + +GC Roots 包括: +Java 线程中,当前所有正在被调用的方法的引用类型参数、局部变量、临时值等。也就是与我们栈帧相关的各种引用。 +所有当前被加载的 Java 类。 +Java 类的引用类型静态变量。 +运行时常量池里的引用类型常量(String 或 Class 类型)。 +JVM 内部数据结构的一些引用,比如 sun.jvm.hotspot.memory.Universe 类。 +用于同步的监控对象,比如调用了对象的 wait() 方法。 +JNI handles,包括 global handles 和 local handles。 + +这些 GC Roots 大体可以分为三大类,下面这种说法更加好记一些: +活动线程相关的各种引用。 +类的静态变量的引用。 +JNI 引用。 + +有两个注意点: +我们这里说的是活跃的引用,而不是对象,对象是不能作为 GC Roots 的。 +GC 过程是找出所有活对象,并把其余空间认定为“无用”;而不是找出所有死掉的对象,并回收它们占用的空间。所以,哪怕 JVM 的堆非常的大,基于 tracing 的 GC 方式,回收速度也会非常快。 + +#### 9.你知道哪些GC类型? + +Minor GC:发生在年轻代的 GC。 +Major GC:发生在老年代的 GC。 +Full GC:全堆垃圾回收。比如 Metaspace 区引起年轻代和老年代的回收。 + +#### 10.对象都是优先分配在年轻代上的吗? + +不是。当新生代内存不够时,老年代分配担保。而大对象则是直接在老年代分配。 + +#### 11.你了解过哪些垃圾收集器? + +年轻代 +Serial 垃圾收集器(单线程,通常用在客户端应用上。因为客户端应用不会频繁创建很多对象,用户也不会感觉出明显的卡顿。相反,它使用的资源更少,也更轻量级。) +ParNew 垃圾收集器(多线程,追求降低用户停顿时间,适合交互式应用。) +Parallel Scavenge 垃圾收集器(追求 CPU 吞吐量,能够在较短时间内完成指定任务,适合没有交互的后台计算。) + +老年代 +Serial Old 垃圾收集器 +Parallel Old垃圾收集器 +CMS 垃圾收集器(以获取最短 GC 停顿时间为目标的收集器,它在垃圾收集时使得用户线程和 GC 线程能够并发执行,因此在垃圾收集过程中用户也不会感到明显的卡顿。) + +#### 12.说说CMS垃圾收集器的工作原理 + +Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾回收停顿时间, 和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。 +最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。 +CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段: +1)初始标记 +只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。 +2)并发标记 +进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。 +3)重新标记 +为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。 +4)并发清除 +清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户线程一起并发工作, 所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。 + +#### 13.说说G1垃圾收集器的工作原理 + +优点:指定最大停顿时间、分Region的内存布局、按收益动态确定回收集 + +G1开创的基于Region的堆内存布局是它能够实现这个目标的关键。虽然G1也仍是遵循分代收集理论设计的,但其堆内存的布局与其他收集器有非常明显的差异:G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。 + +虽然G1仍然保留新生代和老年代的概念,但新生代和老年代不再是固定的了,它们都是一系列区域(不需要连续)的动态集合。G1收集器之所以能建立可预测的停顿时间模型,是因为它将Region作为单次回收的最小单元,即每次收集到的内存空间都是Region大小的整数倍,这样可以有计划地避免在整个Java堆中进行全区域的垃圾收集。更具体的处理思路是让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所获得的空间大小以及回收所需时间的经验值,然后在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值收益最大的那些Region,这也就是“Garbage First”名字的由来。这种使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限的时间内获取尽可能高的收集效率。 + +G1收集器的运作过程大致可划分为以下四个步骤: +·初始标记 (Initial Marking):仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。 +·并发标记 (Concurrent Marking):从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。 +·最终标记 (Final Marking):对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。 +·筛选回收 (Live Data Counting and Evacuation):负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。 +从上述阶段的描述可以看出,G1收集器除了并发标记外,其余阶段也是要完全暂停用户线程的 。 + +#### 14.说说ZGC垃圾收集器的工作原理 + +1)内存布局 +·小型Region(Small Region):容量固定为2MB,用于放置小于256KB的小对象。 +·中型Region(Medium Region):容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。 +·大型Region(Large Region):容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置4MB或以上的大对象。每个大型Region中只会存放一个大对象,这也预示着虽然名字叫作“大型Region”,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB。大型Region在ZGC的实现中是不会被重分配(重分配是ZGC的一种处理动作,用于复制对象的收集器阶段,稍后会介绍到)的,因为复制一个大对象的代价非常高昂。 + +2)染色指针 +染色指针是一种直接将少量额外的信息存储在指针上的技术,可是为什么指针本身也可以存储额外信息呢?在64位系统中,理论可以访问的内存高达16EB(2的64次幂)字节 [3] 。实际上,基于需求(用不到那么多内存)、性能(地址越宽在做地址转换时需要的页表级数越多)和成本(消耗更多晶体管)的考虑,在AMD64架构 [4] 中只支持到52位(4PB)的地址总线和48位(256TB)的虚拟地址空间,所以目前64位的硬件实际能够支持的最大内存只有256TB。此外,操作系统一侧也还会施加自己的约束,64位的Linux则分别支持47位(128TB)的进程虚拟地址空间和46位(64TB)的物理地址空间,64位的Windows系统甚至只支持44位(16TB)的物理地址空间。 +尽管Linux下64位指针的高18位不能用来寻址,但剩余的46位指针所能支持的64TB内存在今天仍然能够充分满足大型服务器的需要。鉴于此,ZGC的染色指针技术继续盯上了这剩下的46位指针宽度,将其高4位提取出来存储四个标志信息。通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入了重分配集(即被移动过)、是否只能通过finalize()方法才能被访问到。当然,由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次幂) 。 + +3)收集过程 +·并发标记 (Concurrent Mark):与G1、Shenandoah一样,并发标记是遍历对象图做可达性分析的阶段,前后也要经过类似于G1、Shenandoah的初始标记、最终标记(尽管ZGC中的名字不叫这些)的短暂停顿,而且这些停顿阶段所做的事情在目标上也是相类似的。与G1、Shenandoah不同的是,ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新染色指针中的Marked 0、Marked 1标志位。 +·并发预备重分配 (Concurrent Prepare for Relocate):这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set)。重分配集与G1收集器的回收集(Collection Set)还是有区别的,ZGC划分Region的目的并非为了像G1那样做收益优先的增量回收。相反,ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本换取省去G1中记忆集的维护成本。因此,ZGC的重分配集只是决定了里面的存活对象会被重新复制到其他的Region中,里面的Region会被释放,而并不能说回收行为就只是针对这个集合里面的Region进行,因为标记过程是针对全堆的。此外,在JDK 12的ZGC中开始支持的类卸载以及弱引用的处理,也是在这个阶段中完成的。 +·并发重分配 (Concurrent Relocate):重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。得益于染色指针的支持,ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障所截获,然后立即根据Region上的转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为称为指针的“自愈”(Self-Healing)能力。这样做的好处是只有第一次访问旧对象会陷入转发,也就是只慢一次,对比Shenandoah的Brooks转发指针,那是每次对象访问都必须付出的固定开销,简单地说就是每次都慢,因此ZGC对用户程序的运行时负载要比Shenandoah来得更低一些。还有另外一个直接的好处是由于染色指针的存在,一旦重分配集中某个Region的存活对象都复制完毕后,这个Region就可以立即释放用于新对象的分配(但是转发表还得留着不能释放掉),哪怕堆中还有很多指向这个对象的未更新指针也没有关系,这些旧指针一旦被使用,它们都是可以自愈的。 +·并发重映射 (Concurrent Remap):重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用,这一点从目标角度看是与Shenandoah并发引用更新阶段一样的,但是ZGC的并发重映射并不是一个必须要“迫切”去完成的任务,因为前面说过,即使是旧引用,它也是可以自愈的,最多只是第一次 +使用时多一次转发和修正操作。重映射清理这些旧引用的主要目的是为了不变慢(还有清理结束后可以释放转发表这样的附带收益),所以说这并不是很“迫切”。因此,ZGC很巧妙地把并发重映射阶段要做的工作,合并到了下一次垃圾收集循环中的并发标记阶段里去完成,反正它们都是要遍历所有对象的,这样合并就节省了一次遍历对象图 [9] 的开销。一旦所有指针都被修正之后,原来记录新旧对象关系的转发表就可以释放掉了。 + +#### 15.ZGC收集器中的染色指针有什么用? + +染色指针是一种直接将少量额外的信息存储在指针上的技术,可是为什么指针本身也可以存储额外信息呢?在64位系统中,理论可以访问的内存高达16EB(2的64次幂)字节 [3] 。实际上,基于需求(用不到那么多内存)、性能(地址越宽在做地址转换时需要的页表级数越多)和成本(消耗更多晶体管)的考虑,在AMD64架构 [4] 中只支持到52位(4PB)的地址总线和48位(256TB)的虚拟地址空间,所以目前64位的硬件实际能够支持的最大内存只有256TB。此外,操作系统一侧也还会施加自己的约束,64位的Linux则分别支持47位(128TB)的进程虚拟地址空间和46位(64TB)的物理地址空间,64位的Windows系统甚至只支持44位(16TB)的物理地址空间。 +尽管Linux下64位指针的高18位不能用来寻址,但剩余的46位指针所能支持的64TB内存在今天仍然能够充分满足大型服务器的需要。鉴于此,ZGC的染色指针技术继续盯上了这剩下的46位指针宽度,将其高4位提取出来存储四个标志信息。通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入了重分配集(即被移动过)、是否只能通过finalize()方法才能被访问到。当然,由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次幂) 。 + +#### 16.说说类加载的过程 + +加载 +验证 +准备(为一些类变量分配内存,并将其初始化为默认值) +解析(将符号引用替换为直接引用。类和接口、类方法、接口方法、字段等解析) +初始化 + +#### 17.说下有哪些类加载器? + +Bootstrap ClassLoader(启动类加载器) +Extention ClassLoader(扩展类加载器) +App ClassLoader(应用类加载器) + +#### 18.什么是双亲委派机制? + +双亲委派机制的意思是除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。 + +#### 19.双亲委派机制可以被违背吗?请举例说明。 + +可以被违背。 +打破双亲委派的例子:Tomcat + +对于一些需要加载的非基础类,会由一个叫作WebAppClassLoader的类加载器优先加载。等它加载不到的时候,再交给上层的ClassLoader进行加载。 +这个加载器用来隔绝不同应用的 .class 文件,比如你的两个应用,可能会依赖同一个第三方的不同版本,它们是相互没有影响的。 + +#### 20.Tomcat是怎么打破双亲委派机制的呢? + +是通过重写ClassLoader#loadClass和ClassLoader#findClass 实现的。可以看图中的WebAppClassLoader,它加载自己目录下的.class文件,并不会传递给父类的加载器。但是,它却可以使用 SharedClassLoader 所加载的类,实现了共享和分离的功能。 + +#### 21.Java对象的布局了解过吗? + +对象头区域此处存储的信息包括两部分: +1、对象自身的运行时数据( MarkWord ),占8字节 +存储 hashCode、GC 分代年龄、锁类型标记、偏向锁线程 ID 、 CAS 锁指向线程 LockRecord 的指针等, synconized 锁的机制与这个部分( markwork )密切相关,用 markword 中最低的三位代表锁的状态,其中一位是偏向锁位,另外两位是普通锁位。 +2、对象类型指针( Class Pointer ),占4字节 +对象指向它的类元数据的指针、 JVM 就是通过它来确定是哪个 Class 的实例。 + +实例数据区域 +此处存储的是对象真正有效的信息,比如对象中所有字段的内容 + +对齐填充区域 +JVM 的实现 HostSpot 规定对象的起始地址必须是 8 字节的整数倍,换句话来说,现在 64 位的 OS 往外读取数据的时候一次性读取 64bit 整数倍的数据,也就是 8 个字节,所以 HotSpot 为了高效读取对象,就做了"对齐",如果一个对象实际占的内存大小不是 8byte 的整数倍时,就"补位"到 8byte 的整数倍。所以对齐填充区域的大小不是固定的。 + +#### 22.什么情况下会发生栈内存溢出? + +栈是线程私有的,他的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表又包含基本数据类型,对象引用类型。 +如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,方法递归调用产生这种结果。 +如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。(线程启动过多)。 + +#### 23.JVM新生代中为什么要分为Eden和Survivor? + +如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC.老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。 +Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。 +设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生) + +#### 24.JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代? + +当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。 +大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态; +如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。 +老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代。 +Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。 + +#### 25.什么是指令重排序? + +在实际运行时,代码指令可能并不是严格按照代码语句顺序执行的。大多数现代微处理器都会采用将指令乱序执行(out-of-order execution,简称OoOE或OOE)的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待。通过乱序执行的技术,处理器可以大大提高执行效率。而这就是指令重排。 + +#### 26.什么是内存屏障? + +内存屏障,也叫内存栅栏,是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。 +LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 +StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 +LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。 +StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。 在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。 + +#### 27.什么是happen-before原则? + +单线程happen-before原则:在同一个线程中,书写在前面的操作happen-before后面的操作。 锁的happen-before原则:同一个锁的unlock操作happen-before此锁的lock操作。 +volatile的happen-before原则:对一个volatile变量的写操作happen-before对此变量的任意操作(当然也包括写操作了)。 +happen-before的传递性原则:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。 +线程启动的happen-before原则:同一个线程的start方法happen-before此线程的其它方法。 +线程中断的happen-before原则 :对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。 +线程终结的happen-before原则: 线程中的所有操作都happen-before线程的终止检测。 +对象创建的happen-before原则: 一个对象的初始化完成先于他的finalize方法调用。 + +#### 28.说说你知道的几种主要的JVM参数 +1)堆栈配置相关 +-Xmx3550m: 最大堆大小为3550m。 +-Xms3550m: 设置初始堆大小为3550m。 +-Xmn2g: 设置年轻代大小为2g。 +-Xss128k: 每个线程的堆栈大小为128k。 +-XX:MaxPermSize: 设置持久代大小为16m +-XX:NewRatio=4: 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。 +-XX:SurvivorRatio=4: 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6 +-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。 + +2)垃圾收集器相关 +-XX:+UseParallelGC: 选择垃圾收集器为并行收集器。 +-XX:ParallelGCThreads=20: 配置并行收集器的线程数 +-XX:+UseConcMarkSweepGC: 设置年老代为并发收集。 +-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 +-XX:+UseCMSCompactAtFullCollection: 打开对年老代的压缩。可能会影响性能,但是可以消除碎片 + +3)辅助信息相关 +-XX:+PrintGC 输出形式: +[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs] + +-XX:+PrintGCDetails 输出形式: +[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs + +#### 29.怎么打出线程栈信息? + +输入jps,获得进程号。 +top -Hp pid 获取本进程中所有线程的CPU耗时性能 +jstack pid命令查看当前java进程的堆栈状态 +或者 jstack -l > /tmp/output.txt 把堆栈信息打到一个txt文件。 +可以使用fastthread 堆栈定位(fastthread.io) + +#### 30.为什么需要双亲委派模式? + +在这里,先想一下,如果没有双亲委派,那么用户是不是可以自己定义一个java.lang.Object的同名类,java.lang.String的同名类,并把它放到ClassPath中,那么类之间的比较结果及类的唯一性将无法保证,因此,为什么需要双亲委派模型?防止内存中出现多份同样的字节码。 + +#### 31.怎么打破双亲委派模型? + +打破双亲委派机制则不仅要继承ClassLoader类,还要重写loadClass和findClass方法。 + +#### 32.说一下堆和栈的区别 + +1)物理地址 +堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩) +栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。 + +2)内存分别 +堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。 +栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。 + +3)存放的内容 +堆存放的是对象的实例和数组。因此该区更关注的是数据的存储 +栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 + +4)程序的可见度 +堆对于整个应用程序都是共享、可见的。 +栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。 + +#### 33.Java 8 为什么要将永久代(PermGen)替换为元空间(MetaSpace)呢? + +整个永久代有一个 JVM 本身设置固定大小上线,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会出现java.lang.OutOfMemoryError。你可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。 + +#### 34.说一下Java对象的创建过程 + +1)类加载检查: 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。 +2)分配内存: 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。 +选择以上2种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的。 + +在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全: +CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。 +TLAB: 为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中的剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配 + +3)初始化零值: 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。 + +4)设置对象头: 初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。 + +5)执行 init 方法: 在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,init 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 init 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。 + +#### 35.对象的访问定位有哪几种方式? + +建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针2种: + +句柄: 如果使用句柄的话,那么Java堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。 + +直接指针: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象的地址。 + +这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。 + +#### 36.说一下堆内存中对象的分配的基本策略 + +eden区、s0区、s1区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden区->Survivor 区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。 +另外,大对象和长期存活的对象会直接进入老年代。 + +#### 37.Minor Gc和Full GC 有什么不同呢? + +大多数情况下,对象在新生代中 eden 区分配。当 eden 区没有足够空间进行分配时,虚拟机将发起一次Minor GC。 +新生代GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。 +老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC经常会伴随至少一次的Minor GC(并非绝对),Major GC的速度一般会比Minor GC的慢10倍以上。 + +#### 38.Java会存在内存泄漏吗?请简单描述。 + +内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除 + +但是,即使这样,Java也还是存在着内存泄漏的情况,java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。 + + +#### 39.如何判断一个类是无用的类? + +方法区主要回收的是无用的类,判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是 “无用的类” : +该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。 +加载该类的 ClassLoader 已经被回收。 +该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。 + +虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。 + +#### 40.介绍一下类文件结构吧! + +魔数: 确定这个文件是否为一个能被虚拟机接收的 Class 文件。 +Class 文件版本 :Class 文件的版本号,保证编译正常执行。 +常量池 :常量池主要存放两大常量:字面量和符号引用。 +访问标志 :标志用于识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。 +当前类索引,父类索引 :类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,由于 Java 语言的单继承,所以父类索引只有一个,除了 java.lang.Object 之外,所有的 java 类都有父类,因此除了 java.lang.Object 外,所有 Java 类的父类索引都不为 0。 +接口索引集合 :接口索引集合用来描述这个类实现了那些接口,这些被实现的接口将按implents(如果这个类本身是接口的话则是extends) 后的接口顺序从左到右排列在接口索引集合中。 +字段表集合 :描述接口或类中声明的变量。字段包括类级变量以及实例变量,但不包括在方法内部声明的局部变量。 +方法表集合 :类中的方法。 +属性表集合 : 在 Class 文件,字段表,方法表中都可以携带自己的属性表集合。 + +#### 41.说一下 JVM 调优的工具? + +常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory AnalyzerTool)、GChisto。 + +jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存, 线程和类等的监控。 +jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。 +MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Javaheap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。 +GChisto,一款专业分析gc日志的工具。 + +#### 42.JVM调优命令有哪些? + +jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。 +jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 +jmap,JVM Memory Map命令用于生成heap dump文件 +jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看 +jstack,用于生成java虚拟机当前时刻的线程快照。 +jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。 + +#### 43.JRE、JDK、JVM 及 JIT 之间有什么不同? + +JRE 代表 Java 运行时(Java run-time),是运行 Java 引用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开发工具,如 Java编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。 + +#### 44.程序计数器为什么是私有的? + +程序计数器主要有下面两个作用: + +字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 +在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 +需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。 + +所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。 + +#### 45.如何判断一个常量是废弃常量 ? + +运行时常量池主要回收的是废弃的常量。假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。 + + +### 参考资料 +https://blog.csdn.net/qq_41701956/article/details/100074023 +https://blog.csdn.net/cunily/article/details/106915944 +https://www.cnblogs.com/chengxuyuanxiaoyang/p/13692997.html diff --git a/Kafka.md b/Kafka.md new file mode 100644 index 0000000..da96928 --- /dev/null +++ b/Kafka.md @@ -0,0 +1,326 @@ +## Kafka + +* [1.为什么要使用 kafka?为什么要使用消息队列?](#1为什么要使用-kafka为什么要使用消息队列) +* [2.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么?](#2kafka中的israr又代表什么isr的伸缩又指什么) +* [3.kafka中的broker 是干什么的?](#3kafka中的broker-是干什么的) +* [4.kafka中的 zookeeper 起到什么作用?可以不用zookeeper么?](#4kafka中的-zookeeper-起到什么作用可以不用zookeeper么) +* [5.kafka follower如何与leader同步数据?](#5kafka-follower如何与leader同步数据) +* [6.什么情况下一个 broker 会从 ISR 中被踢出去?](#6什么情况下一个-broker-会从-isr-中被踢出去) +* [7.kafka 为什么那么快?](#7kafka-为什么那么快) +* [8.kafka producer如何优化打入速度?](#8kafka-producer如何优化打入速度) +* [9.kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit 了](#9kafka-producer-打数据ack--为-0-1--1-的时候代表啥-设置--1-的时候什么情况下leader-会认为一条消息-commit-了) +* [10.kafka unclean 配置代表啥?会对 spark streaming 消费有什么影响?](#10kafka--unclean-配置代表啥会对-spark-streaming-消费有什么影响) +* [11.如果leader crash时,ISR为空怎么办?](#11如果leader-crash时isr为空怎么办) +* [12.kafka的message格式是什么样的?](#12kafka的message格式是什么样的) +* [13.kafka中consumer group 是什么概念?](#13kafka中consumer-group-是什么概念) +* [14.Kafka中的消息是否会丢失和重复消费?](#14kafka中的消息是否会丢失和重复消费) +* [15.为什么Kafka不支持读写分离?](#15为什么kafka不支持读写分离) +* [16.Kafka中是怎么体现消息顺序性的?](#16kafka中是怎么体现消息顺序性的) +* [17.kafka如何实现延迟队列?](#17kafka如何实现延迟队列) +* [18.什么是消费者组?](#18什么是消费者组) +* [19.解释下 Kafka 中位移(offset)的作用。](#19解释下-kafka-中位移offset的作用) +* [20.阐述下 Kafka 中的领导者副本(Leader Replica)和追随者副本 (Follower Replica)的区别。](#20阐述下-kafka-中的领导者副本leader-replica和追随者副本-follower-replica的区别) +* [21.如何设置 Kafka 能接收的最大消息的大小?](#21如何设置-kafka-能接收的最大消息的大小) +* [22.监控 Kafka 的框架都有哪些?](#22监控-kafka-的框架都有哪些) +* [23.Broker 的 Heap Size 如何设置?](#23broker-的-heap-size-如何设置) +* [24.如何估算 Kafka 集群的机器数量?](#24如何估算-kafka-集群的机器数量) +* [25.Leader 总是 -1,怎么破?](#25leader-总是--1怎么破) +* [26.LEO、LSO、AR、ISR、HW 都表示什么含义?](#26leolsoarisrhw-都表示什么含义) +* [27.Kafka 能手动删除消息吗?](#27kafka-能手动删除消息吗) +* [28.consumer_offsets 是做什么用的?](#28consumer_offsets-是做什么用的) +* [29.分区 Leader 选举策略有几种?](#29分区-leader-选举策略有几种) +* [30.Kafka 的哪些场景中使用了零拷贝(Zero Copy)?](#30kafka-的哪些场景中使用了零拷贝zero-copy) +* [31.如何调优 Kafka?](#31如何调优-kafka) +* [32.Controller 发生网络分区(Network Partitioning)时,Kafka 会怎么样?](#32controller-发生网络分区network-partitioning时kafka-会怎么样) +* [33.Java Consumer 为什么采用单线程来获取消息?](#33java-consumer-为什么采用单线程来获取消息) +* [34.简述 Follower 副本消息同步的完整流程。](#34简述-follower-副本消息同步的完整流程) +* [参考资料](#参考资料) + + +#### 1.为什么要使用 kafka?为什么要使用消息队列? + +缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。 +解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。 +冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。 +健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。 +异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。 + +#### 2.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么? + +ISR:In-Sync Replicas 副本同步队列 +AR:Assigned Replicas 所有副本 +ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。 + +#### 3.kafka中的broker 是干什么的? + +broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。 + +#### 4.kafka中的 zookeeper 起到什么作用?可以不用zookeeper么? + +zookeeper 是一个分布式的协调组件,早期版本的kafka用zk做meta信息存储,consumer的消费状态,group的管理以及 offset的值。考虑到zk本身的一些因素以及整个架构较大概率存在单点问题,新版本中逐渐弱化了zookeeper的作用。新的consumer使用了kafka内部的group coordination协议,也减少了对zookeeper的依赖,但是broker依然依赖于ZK,zookeeper 在kafka中还用来选举controller 和 检测broker是否存活等等。 + +#### 5.kafka follower如何与leader同步数据? + +Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。 + +#### 6.什么情况下一个 broker 会从 ISR 中被踢出去? + +leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。 + +#### 7.kafka 为什么那么快? + +Cache Filesystem Cache PageCache缓存 +顺序。 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。 +Zero-copy。零拷技术减少拷贝次数 +Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。 +Pull 拉模式。使用拉模式进行消息的获取消费,与消费端处理能力相符。 + +#### 8.kafka producer如何优化打入速度? + +增加线程 +提高 batch.size +增加更多 producer 实例 +增加 partition 数 +设置 acks=-1 时,如果延迟增大:可以增大 num.replica.fetchers(follower 同步数据的线程数)来调解; +跨数据中心的传输:增加 socket 缓冲区设置以及 OS tcp 缓冲区设置。 + +#### 9.kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit 了 + +1(默认):数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。 +0:生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。 +-1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。 + +#### 10.kafka unclean 配置代表啥?会对 spark streaming 消费有什么影响? + +unclean.leader.election.enable 为true的话,意味着非ISR集合的broker 也可以参与选举,这样有可能就会丢数据,spark streaming在消费过程中拿到的 end offset 会突然变小,导致 spark streaming job挂掉。如果unclean.leader.election.enable参数设置为true,就有可能发生数据丢失和数据不一致的情况,Kafka的可靠性就会降低;而如果unclean.leader.election.enable参数设置为false,Kafka的可用性就会降低。 + +#### 11.如果leader crash时,ISR为空怎么办? + +kafka在Broker端提供了一个配置参数:unclean.leader.election,这个参数有两个值: +true(默认):允许不同步副本成为leader,由于不同步副本的消息较为滞后,此时成为leader,可能会出现消息不一致的情况。 +false:不允许不同步副本成为leader,此时如果发生ISR列表为空,会一直等待旧leader恢复,降低了可用性。 + +#### 12.kafka的message格式是什么样的? + +一个Kafka的Message由一个固定长度的header和一个变长的消息体body组成。 +header部分由一个字节的magic(文件格式)和四个字节的CRC32(用于判断body消息体是否正常)构成。 +当magic的值为1的时候,会在magic和crc32之间多一个字节的数据:attributes(保存一些相关属性,比如是否压缩、压缩格式等等);如果magic的值为0,那么不存在attributes属性。 +body是由N个字节构成的一个消息体,包含了具体的key/value消息。 + +#### 13.kafka中consumer group 是什么概念? + +同样是逻辑上的概念,是Kafka实现单播和广播两种消息模型的手段。同一个topic的数据,会广播给不同的group;同一个group中的worker,只有一个worker能拿到这个数据。换句话说,对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个worker消费。group内的worker可以使用多线程或多进程来实现,也可以将进程分散在多台机器上,worker的数量通常不超过partition的数量,且二者最好保持整数倍关系,因为Kafka在设计时假定了一个partition只能被一个worker消费(同一group内)。 + +#### 14.Kafka中的消息是否会丢失和重复消费? + +要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费。 + +1)消息发送 +Kafka消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可通过producer.type属性进行配置。Kafka通过配置request.required.acks属性来确认消息的生产: + +0---表示不进行消息接收是否成功的确认; +1---表示当Leader接收成功时确认; +-1---表示Leader和Follower都接收成功时确认; +综上所述,有6种消息生产的情况,下面分情况来分析消息丢失的场景: + +(1)acks=0,不和Kafka集群进行消息接收确认,则当网络异常、缓冲区满了等情况时,消息可能丢失; + +(2)acks=1、同步模式下,只有Leader确认接收成功后但挂掉了,副本没有同步,数据可能丢失; + +2)消息消费 + +Kafka消息消费有两个consumer接口,Low-level API和High-level API: + +Low-level API:消费者自己维护offset等值,可以实现对Kafka的完全控制; + +High-level API:封装了对parition和offset的管理,使用简单; + +如果使用高级接口High-level API,可能存在一个问题就是当消息消费者从集群中把消息取出来、并提交了新的消息offset值后,还没来得及消费就挂掉了,那么下次再消费时之前没消费成功的消息就“诡异”的消失了; + +3)解决办法 + +针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态; +针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。 + + +#### 15.为什么Kafka不支持读写分离? + +在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。 + +Kafka 并不支持主写从读,因为主写从读有 2 个很明 显的缺点: + +(1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。 + +(2)延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。 + +#### 16.Kafka中是怎么体现消息顺序性的? + +kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。 +整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1。 + +#### 17.kafka如何实现延迟队列? + +Kafka并没有使用JDK自带的Timer或者DelayQueue来实现延迟的功能,而是基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer)。JDK的Timer和DelayQueue插入和删除操作的平均时间复杂度为O(nlog(n)),并不能满足Kafka的高性能要求,而基于时间轮可以将插入和删除操作的时间复杂度都降为O(1)。时间轮的应用并非Kafka独有,其应用场景还有很多,在Netty、Akka、Quartz、Zookeeper等组件中都存在时间轮的踪影。 + +底层使用数组实现,数组中的每个元素可以存放一个TimerTaskList对象。TimerTaskList是一个环形双向链表,在其中的链表项TimerTaskEntry中封装了真正的定时任务TimerTask. + +Kafka中到底是怎么推进时间的呢?Kafka中的定时器借助了JDK中的DelayQueue来协助推进时间轮。具体做法是对于每个使用到的TimerTaskList都会加入到DelayQueue中。Kafka中的TimingWheel专门用来执行插入和删除TimerTaskEntry的操作,而DelayQueue专门负责时间推进的任务。再试想一下,DelayQueue中的第一个超时任务列表的expiration为200ms,第二个超时任务为840ms,这里获取DelayQueue的队头只需要O(1)的时间复杂度。如果采用每秒定时推进,那么获取到第一个超时的任务列表时执行的200次推进中有199次属于“空推进”,而获取到第二个超时任务时有需要执行639次“空推进”,这样会无故空耗机器的性能资源,这里采用DelayQueue来辅助以少量空间换时间,从而做到了“精准推进”。Kafka中的定时器真可谓是“知人善用”,用TimingWheel做最擅长的任务添加和删除操作,而用DelayQueue做最擅长的时间推进工作,相辅相成。 + +#### 18.什么是消费者组? + +消费者组是 Kafka 独有的概念,如果面试官问这 个,就说明他对此是有一定了解的。我先给出标准答案: +1、定义:即消费者组是 Kafka 提供的可扩展且具有容错性的消费者机制。 +2、原理:在 Kafka 中,消费者组是一个由多个消费者实例 构成的组。多个实例共同订阅若干个主题,实现共同消费。同一个组下的每个实例都配置有 相同的组 ID,被分配不同的订阅分区。当某个实例挂掉的时候,其他实例会自动地承担起 它负责消费的分区。 + +此时,又有一个小技巧给到你:消费者组的题目,能够帮你在某种程度上掌控下面的面试方 +向。 + +如果你擅长位移值原理,就不妨再提一下消费者组的位移提交机制; +如果你擅长 Kafka Broker,可以提一下消费者组与 Broker 之间的交互; +如果你擅长与消费者组完全不相关的 Producer,那么就可以这么说:“消费者组要消 费的数据完全来自于 Producer 端生产的消息,我对 Producer 还是比较熟悉的。” + +#### 19.解释下 Kafka 中位移(offset)的作用。 + +在 Kafka 中,每个 主题分区下的每条消息都被赋予了一个唯一的 ID 数值,用于标识它在分区中的位置。这个 ID 数值,就被称为位移,或者叫偏移量。一旦消息被写入到分区日志,它的位移值将不能 被修改。 +答完这些之后,你还可以把整个面试方向转移到你希望的地方。常见方法有以下 3 种: +如果你深谙 Broker 底层日志写入的逻辑,可以强调下消息在日志中的存放格式; +如果你明白位移值一旦被确定不能修改,可以强调下“Log Cleaner 组件都不能影响位 移值”这件事情; +如果你对消费者的概念还算熟悉,可以再详细说说位移值和消费者位移值之间的区别。 + +#### 20.阐述下 Kafka 中的领导者副本(Leader Replica)和追随者副本 (Follower Replica)的区别。 + +这道题表面上是考核你对 Leader 和 Follower 区别的理解,但很容易引申到 Kafka 的同步 机制上。因此,我建议你主动出击,一次性地把隐含的考点也答出来,也许能够暂时把面试 官“唬住”,并体现你的专业性。 + +你可以这么回答:Kafka 副本当前分为领导者副本和追随者副本。只有 Leader 副本才能 对外提供读写服务,响应 Clients 端的请求。Follower 副本只是采用拉(PULL)的方 式,被动地同步 Leader 副本中的数据,并且在 Leader 副本所在的 Broker 宕机后,随时 准备应聘 Leader 副本。 + +通常来说,回答到这个程度,其实才只说了 60%,因此,我建议你再回答两个额外的加分 项。 + +强调 Follower 副本也能对外提供读服务。自 Kafka 2.4 版本开始,社区通过引入新的 Broker 端参数,允许 Follower 副本有限度地提供读服务。 +强调 Leader 和 Follower 的消息序列在实际场景中不一致。很多原因都可能造成 Leader 和 Follower 保存的消息序列不一致,比如程序 Bug、网络问题等。这是很严重 的错误,必须要完全规避。你可以补充下,之前确保一致性的主要手段是高水位机制, 但高水位值无法保证 Leader 连续变更场景下的数据一致性,因此,社区引入了 Leader Epoch 机制,来修复高水位值的弊端。关于“Leader Epoch 机制”,国内的资料不是 很多,它的普及度远不如高水位,不妨大胆地把这个概念秀出来,力求惊艳一把。 + +#### 21.如何设置 Kafka 能接收的最大消息的大小? + +这道题除了要回答消费者端的参数设置之外,一定要加上 Broker 端的设置,这样才算完整。毕竟,如果 Producer 都不能向 Broker 端发送数据很大的消息,又何来消费一说呢? 因此,你需要同时设置 Broker 端参数和 Consumer 端参数。 + +Broker 端参数:message.max.bytes、max.message.bytes(主题级别)和 replica.fetch.max.bytes。 +Consumer 端参数:fetch.message.max.bytes。 +Broker 端的最后一个参数比较容易遗漏。我们必须调整 Follower 副本能够接收的最大消 息的大小,否则,副本同步就会失败。因此,把这个答出来的话,就是一个加分项。 + +#### 22.监控 Kafka 的框架都有哪些? + +面试官其实是在 考察你对监控框架的了解广度,或者说,你是否知道很多能监控 Kafka 的框架或方法。下面这些就是 Kafka 发展历程上比较有名气的监控系统。 + +Kafka Manager:应该算是最有名的专属 Kafka 监控框架了,是独立的监控系统。 +Kafka Monitor:LinkedIn 开源的免费框架,支持对集群进行系统测试,并实时监控测 +试结果。 +CruiseControl:也是 LinkedIn 公司开源的监控框架,用于实时监测资源使用率,以及 提供常用运维操作等。无 UI 界面,只提供 REST API。 +JMX 监控:由于 Kafka 提供的监控指标都是基于 JMX 的,因此,市面上任何能够集成 JMX 的框架都可以使用,比如 Zabbix 和 Prometheus。 +已有大数据平台自己的监控体系:像 Cloudera 提供的 CDH 这类大数据平台,天然就提 供 Kafka 监控方案。 +JMXTool:社区提供的命令行工具,能够实时监控 JMX 指标。答上这一条,属于绝对 的加分项,因为知道的人很少,而且会给人一种你对 Kafka 工具非常熟悉的感觉。如果 你暂时不了解它的用法,可以在命令行以无参数方式执行一下kafka-run-class.sh kafka.tools.JmxTool,学习下它的用法。 + +#### 23.Broker 的 Heap Size 如何设置? + +如何设置 Heap Size 的问题,其实和 Kafka 关系不大,它是一类非常通用的面试题目。一 旦你应对不当,面试方向很有可能被引到 JVM 和 GC 上去,那样的话,你被问住的几率就 会增大。因此,我建议你简单地介绍一下 Heap Size 的设置方法,并把重点放在 Kafka Broker 堆大小设置的最佳实践上。 + +比如,你可以这样回复:任何 Java 进程 JVM 堆大小的设置都需要仔细地进行考量和测 试。一个常见的做法是,以默认的初始 JVM 堆大小运行程序,当系统达到稳定状态后,手动触发一次 Full GC,然后通过 JVM 工具查看 GC 后的存活对象大小。之后,将堆大小设 置成存活对象总大小的 1.5~2 倍。对于 Kafka 而言,这个方法也是适用的。不过,业界有 个最佳实践,那就是将 Broker 的 Heap Size 固定为 6GB。经过很多公司的验证,这个大 小是足够且良好的。 + +#### 24.如何估算 Kafka 集群的机器数量? + +这道题目考查的是机器数量和所用资源之间的关联关系。所谓资源,也就是 CPU、内存、磁盘和带宽。 + +通常来说,CPU 和内存资源的充足是比较容易保证的,因此,你需要从磁盘空间和带宽占用两个维度去评估机器数量。 + +在预估磁盘的占用时,你一定不要忘记计算副本同步的开销。如果一条消息占用 1KB 的磁 盘空间,那么,在有 3 个副本的主题中,你就需要 3KB 的总空间来保存这条消息。显式地 将这些考虑因素答出来,能够彰显你考虑问题的全面性,是一个难得的加分项。 + +对于评估带宽来说,常见的带宽有 1Gbps 和 10Gbps,但你要切记,这两个数字仅仅是最大值。因此,你最好和面试官确认一下给定的带宽是多少。然后,明确阐述出当带宽占用接 近总带宽的 90% 时,丢包情形就会发生。这样能显示出你的网络基本功。 + +#### 25.Leader 总是 -1,怎么破? + +在生产环境中,你一定碰到过“某个主题分区不能工作了”的情形。使用命令行查看状态的 话,会发现 Leader 是 -1,于是,你使用各种命令都无济于事,最后只能用“重启大 法”。 + +但是,有没有什么办法,可以不重启集群,就能解决此事呢?这就是此题的由来。 + +我直接给答案:删除 ZooKeeper 节点 /controller,触发 Controller 重选举。 Controller 重选举能够为所有主题分区重刷分区状态,可以有效解决因不一致导致的 Leader 不可用问题。我几乎可以断定,当面试官问出此题时,要么就是他真的不知道怎么 解决在向你寻求答案,要么他就是在等你说出这个答案。所以,千万别一上来就说“来个重 启”之类的话。 + +#### 26.LEO、LSO、AR、ISR、HW 都表示什么含义? + +LEO:Log End Offset。日志末端位移值或末端偏移量,表示日志下一条待插入消息的 位移值。举个例子,如果日志有 10 条消息,位移值从 0 开始,那么,第 10 条消息的位 移值就是 9。此时,LEO = 10。 +LSO:Log Stable Offset。这是 Kafka 事务的概念。如果你没有使用到事务,那么这个 值不存在(其实也不是不存在,只是设置成一个无意义的值)。该值控制了事务型消费 者能够看到的消息范围。它经常与 Log Start Offset,即日志起始位移值相混淆,因为 有些人将后者缩写成 LSO,这是不对的。在 Kafka 中,LSO 就是指代 Log Stable Offset。 +AR:Assigned Replicas。AR 是主题被创建后,分区创建时被分配的副本集合,副本个 数由副本因子决定。 +ISR:In-Sync Replicas。Kafka 中特别重要的概念,指代的是 AR 中那些与 Leader 保 持同步的副本集合。在 AR 中的副本可能不在 ISR 中,但 Leader 副本天然就包含在 ISR 中。关于 ISR,还有一个常见的面试题目是如何判断副本是否应该属于 ISR。目前的判断 依据是:Follower 副本的 LEO 落后 Leader LEO 的时间,是否超过了 Broker 端参数 replica.lag.time.max.ms 值。如果超过了,副本就会被从 ISR 中移除。 +HW:高水位值(High watermark)。这是控制消费者可读取消息范围的重要字段。一 个普通消费者只能“看到”Leader 副本上介于 Log Start Offset 和 HW(不含)之间的 所有消息。水位以上的消息是对消费者不可见的。关于 HW,问法有很多,我能想到的 最高级的问法,就是让你完整地梳理下 Follower 副本拉取 Leader 副本、执行同步机制 的详细步骤。这就是我们的第 20 道题的题目,一会儿我会给出答案和解析。 + +#### 27.Kafka 能手动删除消息吗? + +其实,Kafka 不需要用户手动删除消息。它本身提供了留存策略,能够自动删除过期消息。 当然,它是支持手动删除消息的。因此,你最好从这两个维度去回答。 + +对于设置了 Key 且参数 cleanup.policy=compact 的主题而言,我们可以构造一条 的消息发送给 Broker,依靠 Log Cleaner 组件提供的功能删除掉该 Key 的消息。 +对于普通主题而言,我们可以使用 kafka-delete-records 命令,或编写程序调用 Admin.deleteRecords 方法来删除消息。这两种方法殊途同归,底层都是调用 Admin 的 deleteRecords 方法,通过将分区 Log Start Offset 值抬高的方式间接删除消息。 + +#### 28.consumer_offsets 是做什么用的? + +这是一个内部主题,公开的官网资料很少涉及到。因此,我认为,此题属于面试官炫技一类 的题目。你要小心这里的考点:该主题有 3 个重要的知识点,你一定要全部答出来,才会显得对这块知识非常熟悉。 + +它是一个内部主题,无需手动干预,由 Kafka 自行管理。当然,我们可以创建该主题。 + +它的主要作用是负责注册消费者以及保存位移值。可能你对保存位移值的功能很熟悉, 但其实该主题也是保存消费者元数据的地方。千万记得把这一点也回答上。另外,这里 的消费者泛指消费者组和独立消费者,而不仅仅是消费者组。 + +Kafka 的 GroupCoordinator 组件提供对该主题完整的管理功能,包括该主题的创建、 写入、读取和 Leader 维护等。 + +#### 29.分区 Leader 选举策略有几种? + +分区的 Leader 副本选举对用户是完全透明的,它是由 Controller 独立完成的。你需要回答的是,在哪些场景下,需要执行分区 Leader 选举。每一种场景对应于一种选举策略。当前,Kafka 有 4 种分区 Leader 选举策略。 + +OfflinePartition Leader 选举:每当有分区上线时,就需要执行 Leader 选举。所谓的分区上线,可能是创建了新分区,也可能是之前的下线分区重新上线。这是最常见的分区 Leader 选举场景。 + +ReassignPartition Leader 选举:当你手动运行 kafka-reassign-partitions 命令,或者是调用 Admin 的 alterPartitionReassignments 方法执行分区副本重分配时,可能触发此类选举。假设原来的 AR 是[1,2,3],Leader 是 1,当执行副本重分配后,副本集 合 AR 被设置成[4,5,6],显然,Leader 必须要变更,此时会发生 Reassign Partition Leader 选举。 + +PreferredReplicaPartition Leader 选举:当你手动运行 kafka-preferred-replica- election 命令,或自动触发了 Preferred Leader 选举时,该类策略被激活。所谓的 Preferred Leader,指的是 AR 中的第一个副本。比如 AR 是[3,2,1],那么, Preferred Leader 就是 3。 + +ControlledShutdownPartition Leader 选举:当 Broker 正常关闭时,该 Broker 上 的所有 Leader 副本都会下线,因此,需要为受影响的分区执行相应的 Leader 选举。 + +这 4 类选举策略的大致思想是类似的,即从 AR 中挑选首个在 ISR 中的副本,作为新 Leader。当然,个别策略有些微小差异。不过,回答到这种程度,应该足以应付面试官 了。毕竟,微小差别对选举 Leader 这件事的影响很小。 + +#### 30.Kafka 的哪些场景中使用了零拷贝(Zero Copy)? + +Zero Copy 是特别容易被问到的高阶题目。在 Kafka 中,体现 Zero Copy 使用场景的地方有两处:基于 mmap 的索引和日志文件读写所用的 TransportLayer。 + +先说第一个。索引都是基于 MappedByteBuffer 的,也就是让用户态和内核态共享内核态 的数据缓冲区,此时,数据不需要复制到用户态空间。不过,mmap 虽然避免了不必要的 拷贝,但不一定就能保证很高的性能。在不同的操作系统下,mmap 的创建和销毁成本可 能是不一样的。很高的创建和销毁开销会抵消 Zero Copy 带来的性能优势。由于这种不确 定性,在 Kafka 中,只有索引应用了 mmap,最核心的日志并未使用 mmap 机制。 + +再说第二个。TransportLayer 是 Kafka 传输层的接口。它的某个实现类使用了 FileChannel 的 transferTo 方法。该方法底层使用 sendfile 实现了 Zero Copy。对 Kafka 而言,如果 I/O 通道使用普通的 PLAINTEXT,那么,Kafka 就可以利用 Zero Copy 特 性,直接将页缓存中的数据发送到网卡的 Buffer 中,避免中间的多次拷贝。相反,如果 I/O 通道启用了 SSL,那么,Kafka 便无法利用 Zero Copy 特性了。 + +#### 31.如何调优 Kafka? + +回答任何调优问题的第一步,就是确定优化目标,并且定量给出目标!这点特别重要。对于 Kafka 而言,常见的优化目标是吞吐量、延时、持久性和可用性。每一个方向的优化思路都 是不同的,甚至是相反的。 + +确定了目标之后,还要明确优化的维度。有些调优属于通用的优化思路,比如对操作系统、 JVM 等的优化;有些则是有针对性的,比如要优化 Kafka 的 TPS。我们需要从 3 个方向去考虑 + +Producer 端:增加 batch.size、linger.ms,启用压缩,关闭重试等。 +Broker 端:增加 num.replica.fetchers,提升 Follower 同步 TPS,避免 Broker Full GC 等。 +Consumer:增加 fetch.min.bytes 等 + +#### 32.Controller 发生网络分区(Network Partitioning)时,Kafka 会怎么样? + +这道题目能够诱发我们对分布式系统设计、CAP 理论、一致性等多方面的思考。不过,针 对故障定位和分析的这类问题,我建议你首先言明“实用至上”的观点,即不论怎么进行理论分析,永远都要以实际结果为准。一旦发生 Controller 网络分区,那么,第一要务就是 查看集群是否出现“脑裂”,即同时出现两个甚至是多个 Controller 组件。这可以根据 Broker 端监控指标 ActiveControllerCount 来判断。 + +现在,我们分析下,一旦出现这种情况,Kafka 会怎么样。 + +由于 Controller 会给 Broker 发送 3 类请求,即LeaderAndIsrRequest、 StopReplicaRequest 和 UpdateMetadataRequest,因此,一旦出现网络分区,这些请求将不能顺利到达 Broker 端。这将影响主题的创建、修改、删除操作的信息同步,表现为 集群仿佛僵住了一样,无法感知到后面的所有操作。因此,网络分区通常都是非常严重的问 题,要赶快修复。 + +#### 33.Java Consumer 为什么采用单线程来获取消息? + +在回答之前,如果先把这句话说出来,一定会加分:Java Consumer 是双线程的设计。一 个线程是用户主线程,负责获取消息;另一个线程是心跳线程,负责向 Kafka 汇报消费者 存活情况。将心跳单独放入专属的线程,能够有效地规避因消息处理速度慢而被视为下线 的“假死”情况。 + +单线程获取消息的设计能够避免阻塞式的消息获取方式。单线程轮询方式容易实现异步非阻塞式,这样便于将消费者扩展成支持实时流处理的操作算子。因为很多实时流处理操作算子都不能是阻塞式的。另外一个可能的好处是,可以简化代码的开发。多线程交互的代码是非常容易出错的。 + +#### 34.简述 Follower 副本消息同步的完整流程。 + +首先,Follower 发送 FETCH 请求给 Leader。接着,Leader 会读取底层日志文件中的消 息数据,再更新它内存中的 Follower 副本的 LEO 值,更新为 FETCH 请求中的 fetchOffset 值。最后,尝试更新分区高水位值。Follower 接收到 FETCH 响应之后,会把 消息写入到底层日志,接着更新 LEO 和 HW 值。 + +Leader 和 Follower 的 HW 值更新时机是不同的,Follower 的 HW 更新永远落后于 Leader 的 HW。这种时间上的错配是造成各种不一致的原因。 + + + +### 参考资料 +https://blog.csdn.net/qq_28900249/article/details/90346599 +https://www.jianshu.com/p/511962462e58 diff --git a/Linux.md b/Linux.md new file mode 100644 index 0000000..2158792 --- /dev/null +++ b/Linux.md @@ -0,0 +1,252 @@ +## Linux + + +* [1.vim有几种工作模式?](#1vim有几种工作模式) +* [2.find 命令如何使用?](#2find-命令如何使用) +* [3.如何在 /usr 目录下找出大小超过 10MB 的文件?](#3如何在-usr-目录下找出大小超过-10mb-的文件) +* [4.如何在 /var 目录下找出 90 天之内未被访问过的文件?](#4如何在-var-目录下找出-90-天之内未被访问过的文件) +* [5.如何在 /home 目录下找出 120 天之前被修改过的文件?](#5如何在-home-目录下找出-120-天之前被修改过的文件) +* [6.在整个目录树下查找文件 “core” ,如发现则无需提示直接删除它们?](#6在整个目录树下查找文件-core-如发现则无需提示直接删除它们) +* [7.ls 命令](#7ls-命令) +* [8.df 命令](#8df-命令) +* [9.rm 命令](#9rm-命令) +* [10.mv 命令](#10mv-命令) +* [11.cp 命令](#11cp-命令) +* [12.tail 命令](#12tail-命令) +* [13.grep 命令](#13grep-命令) +* [14.sed 命令](#14sed-命令) +* [15.用 sed 命令将指定的路径 /usr/local/http 替换成为 /usr/src/local/http ?](#15用-sed-命令将指定的路径-usrlocalhttp-替换成为-usrsrclocalhttp-) +* [16.打印 /etc/ssh/sshd_config 的第一百行?](#16打印-etcsshsshd_config-的第一百行) +* [17.awk 命令](#17awk-命令) +* [18.打印 /etc/passwd 的 1 到 3 行?](#18打印-etcpasswd-的-1-到-3-行) +* [19.vim 命令](#19vim-命令) +* [20.diff 命令](#20diff-命令) +* [21.sort 命令](#21sort-命令) +* [22.xargs 命令](#22xargs-命令) +* [23.把当前目录下所有后缀名为 .txt 的文件的权限修改为 777 ?](#23把当前目录下所有后缀名为-txt-的文件的权限修改为-777-) +* [24.tar 命令](#24tar-命令) +* [25.gzip 命令](#25gzip-命令) +* [26.bzip2 命令](#26bzip2-命令) +* [27.unzip 命令](#27unzip-命令) +* [28.export 命令](#28export-命令) +* [29.yum 命令](#29yum-命令) +* [30.rpm 命令](#30rpm-命令) +* [31.shutdown 命令](#31shutdown-命令) +* [32.service 命令](#32service-命令) +* [33.whereis 命令](#33whereis-命令) +* [34.用一条命令显示本机 eth0 网卡的 IP 地址,不显示其它字符?](#34用一条命令显示本机-eth0-网卡的-ip-地址不显示其它字符) +* [35.如何禁止服务器被 ping ?](#35如何禁止服务器被-ping-) +* [36.设置 DNS 需要修改哪个配置文件?](#36设置-dns-需要修改哪个配置文件) +* [37.在 Linux 下如何指定dns服务器,来解析某个域名?](#37在-linux-下如何指定dns服务器来解析某个域名) +* [参考资料](#参考资料) + + + +#### 1.vim有几种工作模式? + +命令模式,行末模式,编辑模式 + +#### 2.find 命令如何使用? + +查找指定文件名的文件(不区分大小写):find -iname "MyProgram.c" 。 +对找到的文件执行某个命令:find -iname "MyProgram.c" -exec md5sum {} \; 。 +查找 home 目录下的所有空文件:find ~ -empty 。 + +#### 3.如何在 /usr 目录下找出大小超过 10MB 的文件? + +find /usr -type f -size +10240k + +#### 4.如何在 /var 目录下找出 90 天之内未被访问过的文件? + +find /var \! -atime -90 + +#### 5.如何在 /home 目录下找出 120 天之前被修改过的文件? + +find /home -mtime +120 + +#### 6.在整个目录树下查找文件 “core” ,如发现则无需提示直接删除它们? + +find / -name core -exec rm {} \; + +#### 7.ls 命令 + +以易读的方式显示文件大小(显示为 MB,GB…):ls -lh 。 +以最后修改时间升序列出文件:ls -ltr 。 +在文件名后面显示文件类型:ls -F 。 + +#### 8.df 命令 + +显示文件系统的磁盘使用情况,默认情况下 df -k 将以字节为单位输出磁盘的使用量。 +使用 df -h 选项可以以更符合阅读习惯的方式显示磁盘使用量。 +使用 df -T 选项显示文件系统类型。 + +#### 9.rm 命令 + +删除文件前先确认:rm -i filename.txt 。 +在文件名中使用 shell 的元字符会非常有用。删除文件前先打印文件名并进行确认:rm -i file* 。 +递归删除文件夹下所有文件,并删除该文件夹:rm -r example 。 + +#### 10.mv 命令 + +将 file1 重命名为 file2 ,如果 file2 存在则提示是否覆盖:mv -i file1 file2 。 +-v 会输出重命名的过程,当文件名中包含通配符时,这个选项会非常方便:mv -v file1 file2 。 + +#### 11.cp 命令 + +拷贝 file1 到 file2 ,并保持文件的权限、属主和时间戳:cp -p file1 file2 。 +拷贝 file1 到 file2 ,如果 file2 存在会提示是否覆盖:cp -i file1 file2 。 + +#### 12.tail 命令 + +tail 命令默认显示文件最后的 10 行文本:tail filename.txt 。 +你可以使用 -n 选项指定要显示的行数:tail -n N filename.txt 。 +你也可以使用 -f 选项进行实时查看,这个命令执行后会等待,如果有新行添加到文件尾部,它会继续输出新的行,在查看日志时这个选项会非常有用。你可以通过 CTRL-C 终止命令的执行:tail -f log-file 。 + +#### 13.grep 命令 + +在文件中查找字符串(不区分大小写):grep -i "the" demo_file 。 +输出成功匹配的行,以及该行之后的三行:grep -A 3 -i "example" demo_text 。 +在一个文件夹中递归查询包含指定字符串的文件:grep -r "ramesh" * 。 + +#### 14.sed 命令 + +当你将 Dos 系统中的文件复制到 Unix/Linux 后,这个文件每行都会以 \r\n 结尾,sed 可以轻易将其转换为 Unix 格式的文件,使用\n 结尾的文件:sed 's/.$//' filename 。 +反转文件内容并输出:sed -n '1!G; h; p' filename 。 +为非空行添加行号:sed '/./=' thegeekstuff.txt | sed 'N; s/\n/ /' 。 + +#### 15.用 sed 命令将指定的路径 /usr/local/http 替换成为 /usr/src/local/http ? + +echo "/usr/local/http/" | sed 's#/usr/local/#/usr/src/local/#' + +#### 16.打印 /etc/ssh/sshd_config 的第一百行? + +sed -n '100p' /etc/ssh/sshd_config + +#### 17.awk 命令 + +删除重复行:$ awk '!($0 in array) { array[$0]; print}' temp 。 +打印 /etc/passwd 中所有包含同样的 uid 和 gid 的行:awk -F ':' '$3=$4' /etc/passwd 。 +打印文件中的指定部分的字段:awk '{print $2,$5;}' employee.txt 。 + +#### 18.打印 /etc/passwd 的 1 到 3 行? + +使用 sed 命令:sed -n '1,3p' /etc/passwd +使用 awk 命令:awk 'NR>=1&&NR<=3{print $0}' /etc/passwd + +#### 19.vim 命令 + +打开文件并跳到第 10 行:vim +10 filename.txt 。 +打开文件跳到第一个匹配的行:vim +/search-term filename.txt 。 +以只读模式打开文件:vim -R /etc/passwd 。 + +#### 20.diff 命令 + +比较的时候忽略空白符:diff -w name_list.txt name_list_new.txt 。 + +#### 21.sort 命令 + +以升序对文件内容排序:sort names.txt 。 +以降序对文件内容排序:sort -r names.txt 。 +以第三个字段对 /etc/passwd 的内容排序:sort -t: -k 3n /etc/passwd | more 。 + +#### 22.xargs 命令 + +将所有图片文件拷贝到外部驱动器:ls *.jpg | xargs -n1 -i cp {} /external-hard-drive/directory 。 +将系统中所有 jpg 文件压缩打包:find / -name *.jpg -type f -print | xargs tar -cvzf images.tar.gz 。 +下载文件中列出的所有 url 对应的页面:cat url-list.txt | xargs wget –c 。 + +#### 23.把当前目录下所有后缀名为 .txt 的文件的权限修改为 777 ? + +方式一,使用 xargs 命令:find ./ -type f -name "*.txt" |xargs chmod 777 。 +方式二,使用 exec 命令:find ./ -type f -name "*.txt" -exec chmod 777 {} 。 + +#### 24.tar 命令 + +创建一个新的 tar 文件: tar cvf archive_name.tar dirname/ 。 +解压 tar 文件:tar xvf archive_name.tar 。 +查看 tar 文件:tar tvf archive_name.tar 。 + +#### 25.gzip 命令 + +创建一个 *.gz 的压缩文件:gzip test.txt 。 +解压 *.gz 文件:gzip -d test.txt.gz 。 +显示压缩的比率:gzip -l *.gz 。 + +#### 26.bzip2 命令 + +创建 *.bz2 压缩文件:bzip2 test.txt 。 +解压 *.bz2 文件:bzip2 -d test.txt.bz2 。 + +#### 27.unzip 命令 + +解压 *.zip 文件:unzip test.zip 。 +查看 *.zip 文件的内容:unzip -l jasper.zip 。 + +#### 28.export 命令 + +输出跟字符串 oracle 匹配的环境变量:export | grep ORCALE 。 +设置全局环境变量:export ORACLE_HOME=/u01/app/oracle/product/10.2.0 。 + +#### 29.yum 命令 + +使用 yum 安装 apache :yum install httpd 。 +更新 apache :yum update httpd 。 +卸载/删除 apache :yum remove httpd 。 + +#### 30.rpm 命令 + +使用 rpm 安装 apache :rpm -ivh httpd-2.2.3-22.0.1.el5.i386.rpm 。 +更新 apache :rpm -uvh httpd-2.2.3-22.0.1.el5.i386.rpm 。 +卸载/删除 apache :rpm -ev httpd 。 + +#### 31.shutdown 命令 + +关闭系统并立即关机:shutdown -h now 。 +10 分钟后关机:shutdown -h +10 。 +重启:shutdown -r now 。 +重启期间强制进行系统检查:shutdown -Fr now 。 + +#### 32.service 命令 + +service 命令用于运行 System V init 脚本,这些脚本一般位于 /etc/init.d 文件下,这个命令可以直接运行这个文件夹里面的脚本,而不用加上路径。 +查看服务状态:service ssh status 。 +查看所有服务状态:service --status-all 。 +重启服务:service ssh restart 。 + +#### 33.whereis 命令 + +当你不知道某个命令的位置时可以使用 whereis 命令,下面使用 whereis 查找 ls 的位置:whereis ls 。 +当你想查找某个可执行程序的位置,但这个程序又不在 whereis 的默认目录下,你可以使用 -B 选项,并指定目录作为这个选项的参数。下面的命令在 /tmp 目录下查找 lsmk 命令:whereis -u -B /tmp -f lsmk 。 + +#### 34.用一条命令显示本机 eth0 网卡的 IP 地址,不显示其它字符? + +方法一: +ifconfig eth0|grep inet|awk -F ':' '{print $2}'|awk '{print $1}' +方法二 +ifconfig eth0|grep "inet addr"|awk -F '[ :]+' '{print $4}' +方法三: +ifconfig eth0|awk -F '[ :]+' 'NR==2 {print $4}' +方法四: +ifconfig eth0|sed -n '2p'|sed 's#^.*addr:##g'|sed 's# Bc.*$##g' +方法五: +ifconfig eth0|sed -n '2p'|sed -r 's#^.*addr:(.*) Bc.*$#\1#g' +方法六(CENTOS7 也适用): +ip addr|grep eth0|grep inet|awk '{print $2}'|awk -F '/' '{print $1}' + +#### 35.如何禁止服务器被 ping ? + +echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all + +#### 36.设置 DNS 需要修改哪个配置文件? + +全局的配置,可以在 /etc/resolv.conf 文件中配置。 +指定网卡的配置,可以在 /etc/sysconfig/network-scripts/ifcfg-eth0 文件中配置。 + +#### 37.在 Linux 下如何指定dns服务器,来解析某个域名? + +使用谷歌 DNS 解析百度:dig @8.8.8.8 www.baidu.com + + + +### 参考资料 +https://blog.csdn.net/a303549861/article/details/93754526 diff --git a/MySQL.md b/MySQL.md new file mode 100644 index 0000000..1a871d6 --- /dev/null +++ b/MySQL.md @@ -0,0 +1,452 @@ +## MySQL + + +* [1. 什么是索引?](#1-什么是索引) +* [2.索引是个什么样的数据结构呢?](#2索引是个什么样的数据结构呢) +* [3.Hash索引和B+树索引有什么区别或者说优劣呢?](#3hash索引和b树索引有什么区别或者说优劣呢) +* [4.在建立索引的时候,都有哪些需要考虑的因素呢?](#4在建立索引的时候都有哪些需要考虑的因素呢) +* [5.了解过哪些存储引擎?各有什么优缺点?](#5了解过哪些存储引擎各有什么优缺点) +* [6.说一下什么是事务的ACID属性吧](#6说一下什么是事务的acid属性吧) +* [7.事务的隔离级别了解过吗?](#7事务的隔离级别了解过吗) +* [8.说说InnoDB的索引原理](#8说说innodb的索引原理) +* [说说InnoDB的MVCC机制](#说说innodb的mvcc机制) +* [9.有了解过“回表”的概念吗?什么情况下会出现“回表”?](#9有了解过回表的概念吗什么情况下会出现回表) +* [10.MySQL索引的类型](#10mysql索引的类型) +* [11.有做过MySQL的索引优化吗](#11有做过mysql的索引优化吗) +* [12.什么是聚簇索引?](#12什么是聚簇索引) +* [13.InnoDB有聚簇索引吗?MyIsam呢?](#13innodb有聚簇索引吗myisam呢) +* [14.MyIsam的数据是怎么存储的?](#14myisam的数据是怎么存储的) +* [15.InnoDB的数据是怎么存储的?](#15innodb的数据是怎么存储的) +* [16.InnoDB主键索引跟非主键索引在数据存储上的差异](#16innodb主键索引跟非主键索引在数据存储上的差异) +* [17.InnoDB删除某条记录后,内部会怎么处理?](#17innodb删除某条记录后内部会怎么处理) +* [18.InnoDB如果没有设置主键的话,它内部会怎么处理?](#18innodb如果没有设置主键的话它内部会怎么处理) +* [19.为什么InnoDB一定会生成主键?](#19为什么innodb一定会生成主键) +* [20.MySQL分库分表了解过吗?](#20mysql分库分表了解过吗) +* [21.MySQL的redo日志和undo日志分别有什么用?](#21mysql的redo日志和undo日志分别有什么用) +* [22.MySQL的redo日志的刷盘时机](#22mysql的redo日志的刷盘时机) +* [23.MySQL有哪些锁?以及各种锁的作用?](#23mysql有哪些锁以及各种锁的作用) +* [24.MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义](#24mysql中varchar与char的区别以及varchar50中的50代表的涵义) +* [25.MySQL有哪些日志,分别是什么用处?](#25mysql有哪些日志分别是什么用处) +* [26.在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢?](#26在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢) +* [27. 为什么要尽量设定一个主键?](#27-为什么要尽量设定一个主键) +* [28.主键使用自增ID还是UUID?](#28主键使用自增id还是uuid) +* [29.字段为什么要求定义为not null?](#29字段为什么要求定义为not-null) +* [30.如果要存储用户的密码散列,应该使用什么字段进行存储?](#30如果要存储用户的密码散列应该使用什么字段进行存储) +* [31.varchar(10)和int(10)代表什么含义?](#31varchar10和int10代表什么含义) +* [32.MySQL的binlog有有几种录入格式?分别有什么区别?](#32mysql的binlog有有几种录入格式分别有什么区别) +* [33.超大分页怎么处理?](#33超大分页怎么处理) +* [34.关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?](#34关心过业务系统里面的sql耗时吗统计过慢查询吗对慢查询都怎么优化过) +* [35.什么是存储过程?有哪些优缺点?](#35什么是存储过程有哪些优缺点) +* [36.说一说三个范式](#36说一说三个范式) +* [37.什么情况下应不建或少建索引](#37什么情况下应不建或少建索引) +* [38.什么是表分区?](#38什么是表分区) +* [39.表分区与分表的区别](#39表分区与分表的区别) +* [40.表分区有什么好处?](#40表分区有什么好处) +* [41.MVVC了解过吗](#41mvvc了解过吗) +* [42.在MVCC并发控制中,读操作可以分成哪几类?](#42在mvcc并发控制中读操作可以分成哪几类) +* [43.行级锁定的优点](#43行级锁定的优点) +* [44.行级锁定的缺点](#44行级锁定的缺点) +* [45.MySQL优化](#45mysql优化) +* [46.key和index的区别](#46key和index的区别) +* [47.delete、truncate、drop区别](#47deletetruncatedrop区别) +* [48.MySQL主从复制原理流程](#48mysql主从复制原理流程) +* [49.自增主键最大ID记录,MyISAM和InnoDB分别是如何存储的](#49自增主键最大id记录myisam和innodb分别是如何存储的) +* [50.Mysql如何优化DISTINCT?](#50mysql如何优化distinct) +* [51.解释MySQL外连接、内连接与自连接的区别](#51解释mysql外连接内连接与自连接的区别) +* [参考链接](#参考链接) + + + +#### 1. 什么是索引? + +索引是一种数据结构,可以帮助我们快速的进行数据的查找。 + +#### 2.索引是个什么样的数据结构呢? + +索引的数据结构和具体存储引擎的实现有关, 在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引。 + +#### 3.Hash索引和B+树索引有什么区别或者说优劣呢? + +首先要知道Hash索引和B+树索引的底层实现原理: + +hash索引底层就是hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据.B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据. + +那么可以看出他们有以下的不同: + +- hash索引进行等值查询更快(一般情况下),但是却无法进行范围查询. + +因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询.而B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),天然支持范围. + +- hash索引不支持使用索引进行排序,原理同上. +- hash索引不支持模糊查询以及多列索引的最左前缀匹配.原理也是因为hash函数的不可预测.AAAA和AAAAB的索引没有相关性. +- hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询. +- hash索引虽然在等值查询上较快,但是不稳定.性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差.而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低. + +因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度.而不需要使用hash索引. + +#### 4.在建立索引的时候,都有哪些需要考虑的因素呢? + +建立索引的时候一般要考虑到字段的使用频率,经常作为条件进行查询的字段比较适合.如果需要建立联合索引的话,还需要考虑联合索引中的顺序.此外也要考虑其他方面,比如防止过多的所有对表造成太大的压力.这些都和实际的表结构以及查询方式有关。 + +#### 5.了解过哪些存储引擎?各有什么优缺点? + +常用的是MyISAM和InnoDB。 +InnoDB:支持事务、支持外键、支持行级锁、不支持全文索引、 +MyISAM:不支持事务、不支持外键、不支持行级锁、支持全文索引 + +#### 6.说一下什么是事务的ACID属性吧 + +1. 原子性(atomicity) + 一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性 + +2. 一致性(consistency) + 事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。 + 如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态 + +3. 隔离性(isolation) + 事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务时隔离的,并发执行的各个事务之间不能相互干扰。 + 在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同,分别是:未授权读取,授权读取,可重复读取和串行化 + +4. 持久性(durability) + 一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。--即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态 + +#### 7.事务的隔离级别了解过吗? + +1、读未提交(Read Uncommited),该隔离级别允许脏读取,其隔离级别最低;比如事务A和事务B同时进行,事务A在整个执行阶段,会将某数据的值从1开始一直加到10,然后进行事务提交,此时,事务B能够看到这个数据项在事务A操作过程中的所有中间值(如1变成2,2变成3等),而对这一系列的中间值的读取就是未授权读取 + +2、授权读取也称为已提交读(Read Commited),授权读取只允许获取已经提交的数据。比如事务A和事务B同时进行,事务A进行+1操作,此时,事务B无法看到这个数据项在事务A操作过程中的所有中间值,只能看到最终的10。另外,如果说有一个事务C,和事务A进行非常类似的操作,只是事务C是将数据项从10加到20,此时事务B也同样可以读取到20,即授权读取允许不可重复读取。 + +3、可重复读(Repeatable Read) + +就是保证在事务处理过程中,多次读取同一个数据时,其值都和事务开始时刻是一致的,因此该事务级别禁止不可重复读取和脏读取,但是有可能出现幻影数据。所谓幻影数据,就是指同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。在上面的例子中,可重复读取隔离级别能够保证事务B在第一次事务操作过程中,始终对数据项读取到1,但是在下一次事务操作中,即使事务B(注意,事务名字虽然相同,但是指的是另一个事务操作)采用同样的查询方式,就可能读取到10或20; + +4、串行化 + +是最严格的事务隔离级别,它要求所有事务被串行执行,即事务只能一个接一个的进行处理,不能并发执行。 + +#### 8.说说InnoDB的索引原理 + +详见:https://www.cnblogs.com/williamjie/p/11081081.html + +#### 说说InnoDB的MVCC机制 + +详见:https://baijiahao.baidu.com/s?id=1629409989970483292 + +#### 9.有了解过“回表”的概念吗?什么情况下会出现“回表”? + +回表就是先通过数据库索引扫描出数据所在的行,再通过行主键id取出索引中未提供的数据,即基于非主键索引的查询需要多扫描一棵索引树。 +当查询的字段在二级索引上没有的时候,就需要“回表”在主键索引上再查一次。 + +#### 10.MySQL索引的类型 + +聚簇索引、二级(辅助)索引 +B树索引、hash索引 + +#### 11.有做过MySQL的索引优化吗 + +详见:https://blog.csdn.net/m0_37984616/article/details/81047676 + +#### 12.什么是聚簇索引? + +聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据 +非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据的对应行 + +#### 13.InnoDB有聚簇索引吗?MyIsam呢? + +InnoDB有聚簇索引,主键索引就是聚簇索引。MyIsam没有聚簇索引,因为他的索引和记录行是分开存储的。 + +#### 14.MyIsam的数据是怎么存储的? + +MyIsam索引的节点中存储的是数据的物理地址(磁道和扇区),在查找数据时,查找到索引后,根据索引节点中的物理地址,查找到具体的数据内容。 + +#### 15.InnoDB的数据是怎么存储的? + +InnoDB的主键索引文件上直接存放该行数据,称为聚簇索引,非主索引指向对主键的引用。 + +#### 16.InnoDB主键索引跟非主键索引在数据存储上的差异 + +主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。 +非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。 + +#### 17.InnoDB删除某条记录后,内部会怎么处理? + +记录头信息里的delete_mask标记位设置为1(表示该记录已删除),同时将记录从记录行链表中断开,并加入到垃圾链表中,垃圾链表的空间后续可以复用。 + +#### 18.InnoDB如果没有设置主键的话,它内部会怎么处理? + +优先使用用户自定义主键作为主键,如果用户没有定义主键,则选取一个Unique键作为主键,如果表中连Unique键都没有定义的话,则InnoDB会为表默认添加一个名为row_id的隐藏列作为主键。所以我们从上表中可以看出:InnoDB存储引擎会为每条记录都添加 transaction_id 和 roll_pointer 这两个列,但是 row_id 是可选的(在没有自定义主键以及Unique键的情况下才会添加该列)。这些隐藏列的值不用我们操心,InnoDB存储引擎会自己帮我们生成的。 + +#### 19.为什么InnoDB一定会生成主键? + +因为Innodb的数据结构是通过聚簇索引组织起来的,如果没有主键的话,通过其他索引回表的时候没法查到相应的数据行。 + +#### 20.MySQL分库分表了解过吗? + +详见:https://baijiahao.baidu.com/s?id=1622441635115622194 + +#### 21.MySQL的redo日志和undo日志分别有什么用? + +1)redo +作用:保证事务的持久性 +MySQL作为一个存储系统,为了保证数据的可靠性,最终得落盘。但是,又为了数据写入的速度,需要引入基于内存的"缓冲池"。其实不止MySQL,这种引入缓冲来解决速度问题的思想无处不在。既然数据是先缓存在缓冲池中,然后再以某种方式刷新到磁盘,那么就存在因宕机导致的缓冲池中的数据丢失,为了解决这种情况下的数据丢失问题,引入了redo log。在其他存储系统,比如Elasticsearch中,也有类似的机制,叫translog。 +但是一般讨论数据写入时,在MySQL中,一般叫事务操作,根据事务的ACID特性,如何保证一个事务提交后Durability的保证?而这就是 redo log 的作用。当向MySQL写用户数据时,先写redo log,然后redo log根据"某种方式"持久化到磁盘,变成redo log file,用户数据则在"buffer"中(比如数据页、索引页)。如果发生宕机,则读取磁盘上的 redo log file 进行数据的恢复。从这个角度来说,MySQL 事务的持久性是通过 redo log 来实现的。 + +2)undo +作用:实现事务回滚 +Undo log是InnoDB MVCC事务特性的重要组成部分。当我们对记录做了变更操作时就会产生undo记录,Undo记录默认被记录到系统表空间(ibdata)中,但从5.6开始,也可以使用独立的Undo 表空间。 +Undo记录中存储的是老版本数据,当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着undo链找到满足其可见性的记录。当版本链很长时,通常可以认为这是个比较耗时的操作。 +大多数对数据的变更操作包括INSERT/DELETE/UPDATE,其中INSERT操作在事务提交前只对当前事务可见,因此产生的Undo日志可以在事务提交后直接删除(谁会对刚插入的数据有可见性需求呢!!),而对于UPDATE/DELETE则需要维护多版本信息,在InnoDB里,UPDATE和DELETE操作产生的Undo日志被归成一类,即update_undo。 + +#### 22.MySQL的redo日志的刷盘时机 + +log buffer空间不足时 +事务提交时 +后台线程不停的刷刷刷 +正常关闭服务器时 +做所谓的checkpoint时 + +#### 23.MySQL有哪些锁?以及各种锁的作用? + +详见:https://blog.csdn.net/qq_40378034/article/details/90904573 + +#### 24.MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义 + +(1)、varchar与char的区别 +(2)、varchar(50)中50的涵义 +(3)、int(20)中20的涵义 +(4)、mysql为什么这么设计 + +#### 25.MySQL有哪些日志,分别是什么用处? + +mysql日志一般分为5种 + +- 错误日志:-log-err (记录启动,运行,停止mysql时出现的信息) +- 二进制日志:-log-bin (记录所有更改数据的语句,还用于复制,恢复数据库用) +- 查询日志:-log (记录建立的客户端连接和执行的语句) +- 慢查询日志: -log-slow-queries (记录所有执行超过long_query_time秒的所有查询) +- 更新日志: -log-update (二进制日志已经代替了老的更新日志,更新日志在MySQL5.1中不再使用) + +#### 26.在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢? + +- 使用不等于查询, +- 列参与了数学运算或者函数 +- 在字符串like时左边是通配符.类似于'%aaa'. +- 当mysql分析全表扫描比使用索引快的时候不使用索引. +- 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引. + +以上情况,MySQL无法使用索引. + +#### 27. 为什么要尽量设定一个主键? + + 主键是数据库确保数据行在整张表唯一性的保障,即使业务上本张表没有主键,也建议添加一个自增长的ID列作为主键.设定了主键之后,在后续的删改查的时候可能更加快速以及确保操作数据范围安全。 + +#### 28.主键使用自增ID还是UUID? + +推荐使用自增ID,不要使用UUID. + +因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降. + +总之,在数据量大一些的情况下,用自增主键性能会好一些. + +*图片来源于《高性能MySQL》: 其中默认后缀为使用自增ID,_uuid为使用UUID为主键的测试,测试了插入100w行和300w行的性能 + +![img](https://imgs.itxueyuan.com/990913-20190806091416714-780950030.png) + +关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。 + +#### 29.字段为什么要求定义为not null? + +MySQL官网这样介绍: + +> NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte. + +null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。 + +#### 30.如果要存储用户的密码散列,应该使用什么字段进行存储? + +密码散列,盐,用户身份证号等固定长度的字符串应该使用char而不是varchar来存储,这样可以节省空间且提高检索效率。 + +#### 31.varchar(10)和int(10)代表什么含义? + +varchar的10代表了申请的空间长度,也是可以存储的数据的最大长度,而int的10只是代表了展示的长度,不足10位以0填充.也就是说,int(1)和int(10)所能存储的数字大小以及占用的空间都是相同的,只是在展示时按照长度展示。 + +#### 32.MySQL的binlog有有几种录入格式?分别有什么区别? + +有三种格式,statement,row和mixed. + +- statement模式下,记录单元为语句.即每一个sql造成的影响会记录.由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制. +- row级别下,记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大. +- mixed. 一种折中的方案,普通操作使用statement记录,当无法使用statement的时候使用row. + +此外,新版的MySQL中对row级别也做了一些优化,当表结构发生变化的时候,会记录语句而不是逐行记录。 + +#### 33.超大分页怎么处理? + +超大的分页一般从两个方向上来解决. + +- 数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于`select * from table where age > 20 limit 1000000,10`这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为`select * from table where id in (select id from table where age > 20 limit 1000000,10)`.这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以`select * from table where id > 1000000 limit 10`,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据. +- 从需求的角度减少这种请求….主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击. + +解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可. + +在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种。 + +![img](https://imgs.itxueyuan.com/990913-20190806091439548-806498135.png) + +#### 34.关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过? + +在业务系统中,除了使用主键进行的查询,其他的我都会在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们。 + +慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大? + +所以优化也是针对这三个方向来的, + +- 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。 +- 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。 +- 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。 + +#### 35.什么是存储过程?有哪些优缺点? + +存储过程是一些预编译的SQL语句。 + +1、更加直白的理解:存储过程可以说是一个记录集,它是由一些T-SQL语句组成的代码块,这些T-SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取一个名字,在用到这个功能的时候调用他就行了。 + +2、存储过程是一个预编译的代码块,执行效率比较高,一个存储过程替代大量T_SQL语句 ,可以降低网络通信量,提高通信速率,可以一定程度上确保数据安全 + +但是,在互联网项目中,其实是不太推荐存储过程的,比较出名的就是阿里的《Java开发手册》中禁止使用存储过程,我个人的理解是,在互联网项目中,迭代太快,项目的生命周期也比较短,人员流动相比于传统的项目也更加频繁,在这样的情况下,存储过程的管理确实是没有那么方便,同时,复用性也没有写在服务层那么好。 + +#### 36.说一说三个范式 + +第一范式: 每个列都不可以再拆分. 第二范式: 非主键列完全依赖于主键,而不能是依赖于主键的一部分. 第三范式: 非主键列只依赖于主键,不依赖于其他非主键。 + +在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由.比如性能. 事实上我们经常会为了性能而妥协数据库的设计。 + +#### 37.什么情况下应不建或少建索引 + +1、表记录太少 + +2、经常插入、删除、修改的表 + +3、数据重复且分布平均的表字段,假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。 + +4、经常和主字段一块查询但主字段索引值比较多的表字段 + +#### 38.什么是表分区? + +表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。 + +#### 39.表分区与分表的区别 + +分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。 + +分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。 + +#### 40.表分区有什么好处? + +1、存储更多数据。分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。和单个磁盘或者文件系统相比,可以存储更多数据 + +2、优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。 + +3、分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。 + +4、避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。 + +#### 41.MVVC了解过吗 + +MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) + +注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control + +MVCC最大的好处:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,现阶段几乎所有的RDBMS,都支持了MVCC。 + +1. LBCC:Lock-Based Concurrency Control,基于锁的并发控制 + +2. MVCC:Multi-Version Concurrency Control + + 基于多版本的并发控制协议。纯粹基于锁的并发机制并发量低,MVCC是在基于锁的并发控制上的改进,主要是在读操作上提高了并发量。 + +#### 42.在MVCC并发控制中,读操作可以分成哪几类? + +1. 快照读 (snapshot read):读取的是记录的可见版本 (有可能是历史版本),不用加锁(共享读锁s锁也不加,所以不会阻塞其他事务的写) +2. 当前读 (current read):读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录 + +#### 43.行级锁定的优点 + +1、当在许多线程中访问不同的行时只存在少量锁定冲突。 + +2、回滚时只有少量的更改 + +3、可以长时间锁定单一的行。 + +#### 44.行级锁定的缺点 + +1. 比页级或表级锁定占用更多的内存。 +2. 当在表的大部分中使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。 +3. 如果你在大部分数据上经常进行GROUP BY操作或者必须经常扫描整个表,比其它锁定明显慢很多。 +4. 用高级别锁定,通过支持不同的类型锁定,你也可以很容易地调节应用程序,因为其锁成本小于行级锁定。 + +#### 45.MySQL优化 + +1. 开启查询缓存,优化查询 + +2. explain你的select查询,这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN 的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的 + +3. 当只要一行数据时使用limit 1,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据 + +4. 为搜索字段建索引 + +5. 使用 ENUM 而不是 VARCHAR。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用 ENUM 而不是VARCHAR + +6. Prepared StatementsPrepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用 prepared statements 获得很多好处,无论是性能问题还是安全问题。 + + Prepared Statements 可以检查一些你绑定好的变量,这样可以保护你的程序不会受到“SQL注入式”攻击 + +7. 垂直分表 + +8. 选择正确的存储引擎 + +#### 46.key和index的区别 + +1. key 是数据库的物理结构,它包含两层意义和作用,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的)。包括primary key, unique key, foreign key 等 +2. index是数据库的物理结构,它只是辅助查询的,它创建时会在另外的表空间(mysql中的innodb表空间)以一个类似目录的结构存储。索引要分类的话,分为前缀索引、全文本索引等; + +#### 47.delete、truncate、drop区别 + +- truncate和delete只删除数据,不删除表结构 ,drop删除表结构,并且释放所占的空间。 +- 删除数据的速度,drop> truncate > delete +- delete属于DML语言,需要事务管理,commit之后才能生效。drop和truncate属于DDL语言,操作立刻生效,不可回滚。使用场合: 当你不再需要该表时, 用 drop; 当你仍要保留该表,但要删除所有记录时, 用 truncate; 当你要删除部分记录时(always with a where clause), 用 delete。 + +#### 48.MySQL主从复制原理流程 + +- 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中; +- 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进 自己的relay log中; +- 从:sql执行线程——执行relay log中的语句; + +#### 49.自增主键最大ID记录,MyISAM和InnoDB分别是如何存储的 + +- MyISAM表把自增主键的最大ID记录到数据文件里 +- InnoDB表把自增主键的最大ID记录到内存中 + +#### 50.Mysql如何优化DISTINCT? + +DISTINCT在所有列上转换为GROUP BY,并与ORDER BY子句结合使用。 + +#### 51.解释MySQL外连接、内连接与自连接的区别 + +先说什么是交叉连接: 交叉连接又叫笛卡尔积,它是指不使用任何条件,直接将一个表的所有记录和另一个表中的所有记录一一匹配。 + +内连接 则是只有条件的交叉连接,根据某个条件筛选出符合条件的记录,不符合条件的记录不会出现在结果集中,即内连接只连接匹配的行。 +外连接 其结果集中不仅包含符合连接条件的行,而且还会包括左表、右表或两个表中 +的所有数据行,这三种情况依次称之为左外连接,右外连接,和全外连接。 + +左外连接,也称左连接,左表为主表,左表中的所有记录都会出现在结果集中,对于那些在右表中并没有匹配的记录,仍然要显示,右边对应的那些字段值以NULL来填充。右外连接,也称右连接,右表为主表,右表中的所有记录都会出现在结果集中。左连接和右连接可以互换,MySQL目前还不支持全外连接。 + +#### 参考链接 + +https://article.itxueyuan.com/eoJEMj + +https://www.cnblogs.com/williamjie/p/11081592.html + +http://www.100mian.com/mianshi/mysql/49909.html + +https://leisure.wang/procedural-framework/database/241.html diff --git a/Mybatis.md b/Mybatis.md new file mode 100644 index 0000000..95aaf0c --- /dev/null +++ b/Mybatis.md @@ -0,0 +1,178 @@ +## Mybatis + + + +* [1.什么是Mybatis?](#1什么是mybatis) +* [2.Mybatis的优缺点?](#2mybatis的优缺点) +* [3.Mybatis使用场合?](#3mybatis使用场合) +* [4.#{}和${}的区别是什么?](#4和的区别是什么) +* [5.当实体类的属性名和表种字段名不一致怎么办?](#5当实体类的属性名和表种字段名不一致怎么办) +* [6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?](#6mybatis是如何将sql执行结果封装为目标对象并返回的都有哪些映射形式) +* [7.如何获取自动生成的(主)键值?](#7如何获取自动生成的主键值) +* [8.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?](#8mybatis的xml映射文件中不同的xml映射文件id是否可以重复) +* [9. Mybatis动态SQL?](#9-mybatis动态sql) +* [10.说一下resultMap和resultType?](#10说一下resultmap和resulttype) +* [11. Mybatis全局配置文件中有哪些标签?分别代表什么意思?](#11-mybatis全局配置文件中有哪些标签分别代表什么意思) +* [12.Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。](#12mybatis能执行一对一一对多的关联查询吗都有哪些实现方式以及它们之间的区别) +* [13.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?](#13mybatis是否支持延迟加载如果支持它的实现原理是什么) +* [14.Mybatis都有哪些Executor执行器?它们之间的区别是什么?](#14mybatis都有哪些executor执行器它们之间的区别是什么) +* [15.Mybatis的一级、二级缓存](#15mybatis的一级二级缓存) +* [参考链接](#参考链接) + + + +#### 1.什么是Mybatis? + +1)mybatis是一个半ORM框架,它内部封装了JDBC,开发时只需要关乎sql语句本身,不需要花费精力去处理驱动,创建连接,创建1statement等繁复过程。 + +2)mybatis可以使用xml或注解来配置和映射原生信息。将pijo映射成数据库中的记录,避免了几乎所有的JDBC 代码和手动设置参数以及获取结果集。 + +3)通过xm文件或注解的方式将要执行的各种statement配置起来,并通java对象和statement中sql的动态参数进行映射生成最终的sql语句,最后由mybatis框架执行sql并将结果映射java对象返回. + +#### 2.Mybatis的优缺点? + +Mybaits的优点: + +(1)基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。 + +(2)与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接; + +(3)很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)。 + +(4)能够与Spring很好的集成; + +(5)提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。 + +MyBatis框架的缺点: + +(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。 + +(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。 + +MyBatis框架适用场合: + +(1)MyBatis专注于SQL本身,是一个足够灵活的DAO层解决方案。 + +(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis将是不错的选择。 + +#### 3.Mybatis使用场合? + +专注于sql本身,是一个足够灵活的dao层解决方案.,对性能的要求很高,或者需求多变的项目, + +#### 4.#{}和${}的区别是什么? + +\#{}是预编译处理,${}是字符串替换。 + +Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值; + +Mybatis在处理{}时,就是把时,就是把{}替换成变量的值。 + +使用#{}可以有效的防止SQL注入,提高系统安全性。 + +#### 5.当实体类的属性名和表种字段名不一致怎么办? + +有两种解决方案:可以在sql语句给字段名取别名,别名于实体类属性名同名,也可以用resultMap来映射字段名和实体类属性名一一对应. + + +#### 6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? + +第一种是使用resultMap标签,逐一定义数据库列名和对象属性名之间的映射关系。 + +第二种是使用sql列的别名功能,将列的别名书写为对象属性名。 + +有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。 + +#### 7.如何获取自动生成的(主)键值? + +``` + + insert into names (name) values (#{name}) + +``` + +#### 8.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复? + +不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复 + +#### 9. Mybatis动态SQL? + +1) 传统的JDBC的方法,在组合SQL语句的时候需要去拼接,稍微不注意就会少少了一个空格,标点符号,都会导致系统错误。Mybatis的动态SQL就是为了解决这种问题而产生的;Mybatis的动态SQL语句值基于OGNL表达式的,方便在SQL语句中实现某些逻辑;可以使用标签组合成灵活的sql语句,提供开发的效率。 + + 2) Mybatis的动态SQL标签主要由以下几类: If语句(简单的条件判断) Choose(when/otherwise),相当于java语言中的switch,与jstl中choose类似 Trim(对包含的内容加上prefix,或者suffix) Where(主要是用来简化SQL语句中where条件判断,能智能的处理and/or 不用担心多余的语法导致的错误) Set(主要用于更新时候) Foreach(一般使用在mybatis in语句查询时特别有用) + +#### 10.说一下resultMap和resultType? + +resultmap是手动提交,人为提交,resulttype是自动提交 +MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。 +在MyBatis进行查询映射时,其实查询出来的每一个属性都是放在一个对应的Map里面的,其中键是属性名,值则是其对应的值。 +1.当提供的返回类型属性是resultType时,MyBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性。所以其实MyBatis的每一个查询映射的返回类型都是ResultMap,只是当提供的返回类型属性是resultType的时候,MyBatis对自动的给把对应的值赋给resultType所指定对象的属性。 +2.当提供的返回类型是resultMap时,因为Map不能很好表示领域模型,就需要自己再进一步的把它转化为对应的对象,这常常在复杂查询中很有作用。 + +#### 11. Mybatis全局配置文件中有哪些标签?分别代表什么意思? + +configuration 配置 +properties 属性:可以加载properties配置文件的信息 +settings 设置:可以设置mybatis的全局属性 +typeAliases 类型命名 +typeHandlers 类型处理器 +objectFactory 对象工厂 +plugins 插件 +environments 环境 +environment 环境变量 +transactionManager 事务管理器 +dataSource 数据源 +mappers 映射器 + +#### 12.Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。 + +答:能,Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询,多对一查询,其实就是一对一查询,只需要把selectOne()修改为selectList()即可;多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。 + +  关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象,赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询,一部分列是A对象的属性值,另外一部分列是关联对象B的属性值,好处是只发一个sql查询,就可以把主对象和其关联对象查出来。 + +  那么问题来了,join查询出来100条记录,如何确定主对象是5个,而不是100个?其去重复的原理是 resultMap标签内的 id 子标签,指定了唯一确定一条记录的id列,Mybatis根据 id 列值来完成100条记录的去重复功能, id 可以有多个,代表了联合主键的语意。 + +  同样主对象的关联对象,也是根据这个原理去重复的,尽管一般情况下,只有主对象会有重复记录,关联对象一般不会重复。 + +#### 13.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? + +Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。 + +它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。 + +当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。 + +#### 14.Mybatis都有哪些Executor执行器?它们之间的区别是什么? + +Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。 + +SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。 + +ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。 + +BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。 + +作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。 + +#### 15.Mybatis的一级、二级缓存 + +(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。 + +(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ; + +(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。 + + + +### 参考链接 + +https://my.oschina.net/u/4116655/blog/3055230 + +https://www.w3cschool.cn/kzsow/kzsow-4ovb2grl.html + +https://www.cnblogs.com/lukelook/p/11099039.html + +https://zhuanlan.zhihu.com/p/60257737 + +https://www.cnblogs.com/qmillet/p/12523636.html + +https://blog.csdn.net/a745233700/article/details/80977133 diff --git a/Netty.md b/Netty.md new file mode 100644 index 0000000..ea43b93 --- /dev/null +++ b/Netty.md @@ -0,0 +1,142 @@ +## Netty + +* [1.你了解过哪些IO模型?](#1你了解过哪些io模型) +* [2.什么是Reactor模型?Reactor的3种版本都知道吗?](#2什么是reactor模型reactor的3种版本都知道吗) +* [3.了解过粘包拆包吗?为什么会出现粘包拆包?怎么处理粘包拆包?](#3了解过粘包拆包吗为什么会出现粘包拆包怎么处理粘包拆包) +* [4.UDP协议会有粘包拆包的问题吗?为什么?](#4udp协议会有粘包拆包的问题吗为什么) +* [5.Netty 是什么?](#5netty-是什么) +* [6.为什么要用 Netty?](#6为什么要用-netty) +* [7.Netty 的应用场景了解么?](#7netty-的应用场景了解么) +* [8.Netty 的零拷贝了解么?](#8netty-的零拷贝了解么) +* [9.Netty 的心跳机制了解么?](#9netty-的心跳机制了解么) +* [10.Netty 中有哪些重要组件?](#10netty-中有哪些重要组件) +* [11.Netty 发送消息有几种方式?](#11netty-发送消息有几种方式) +* [12.Netty 支持哪些心跳类型设置?](#12netty-支持哪些心跳类型设置) +* [13.说说Netty的执行流程?](#13说说netty的执行流程) +* [14.Netty高性能体现在哪些方面?](#14netty高性能体现在哪些方面) +* [参考资料](#参考资料) + + + +#### 1.你了解过哪些IO模型? + +详见:https://www.cnblogs.com/sharing-java/p/10791802.html + +#### 2.什么是Reactor模型?Reactor的3种版本都知道吗? + +Reactor模式究竟是个什么东西呢?这要从事件驱动的开发方式说起。我们知道,对于应用服务器,一个主要规律就是,CPU的处理速度是要远远快于IO速度的,如果CPU为了IO操作(例如从Socket读取一段数据)而阻塞显然是不划算的。好一点的方法是分为多进程或者线程去进行处理,但是这样会带来一些进程切换的开销,试想一个进程一个数据读了500ms,期间进程切换到它3次,但是CPU却什么都不能干,就这么切换走了,是不是也不划算? +这时先驱们找到了事件驱动,或者叫回调的方式,来完成这件事情。这种方式就是,应用业务向一个中间人注册一个回调(event handler),当IO就绪后,就这个中间人产生一个事件,并通知此handler进行处理。这种回调的方式,也体现了“好莱坞原则”(Hollywood principle)-“Don't call us, we'll call you”,在我们熟悉的IoC中也有用到。看来软件开发真是互通的! +好了,我们现在来看Reactor模式。在前面事件驱动的例子里有个问题:我们如何知道IO就绪这个事件,谁来充当这个中间人?Reactor模式的答案是:由一个不断等待和循环的单独进程(线程)来做这件事,它接受所有handler的注册,并负责先操作系统查询IO是否就绪,在就绪后就调用指定handler进行处理,这个角色的名字就叫做Reactor。 + +Reactor的3种版本:单线程模式、多线程模式、主从多线程模式 + +#### 3.了解过粘包拆包吗?为什么会出现粘包拆包?怎么处理粘包拆包? + +粘包的主要原因:发送方写入数据<套接字缓冲区大小;接收方读取套接字缓冲区数据不够及时。 + +拆包的主要原因:发送方写入数据>套接字缓冲区大小;发送方发送的数据大于协议的MTU(最大传输单元),不得已必须拆包。 + +如何处理:1、消息长度固定;2、消息之间用分隔符分隔;3、在消息头保留一个字段,用于描述消息的长度。 + +#### 4.UDP协议会有粘包拆包的问题吗?为什么? + +UDP不会有这个问题。 +因为TCP是“数据流”协议,UDP是“数据报”协议。 +UDP协议的数据包之间是没有联系,而且有明确边界的。 + + +#### 5.Netty 是什么? + +Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。 +它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。 +支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。 +用官方的总结就是:Netty 成功地找到了一种在不妥协可维护性和性能的情况下实现易于开发,性能,稳定性和灵活性的方法。 + +#### 6.为什么要用 Netty? + +统一的 API,支持多种传输类型,阻塞和非阻塞的。 +简单而强大的线程模型。 +自带编解码器解决 TCP 粘包/拆包问题。 +自带各种协议栈。 +真正的无连接数据包套接字支持。 +比直接使用 Java 核心 API 有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存复制。 +安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持。 +社区活跃 +成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了 Netty, 比如我们经常接触的 Dubbo、RocketMQ 等等。 + +#### 7.Netty 的应用场景了解么? + +Netty 主要用来做网络通信 : +作为 RPC 框架的网络通信工具 :我们在分布式系统中,不同服务节点之间经常需要相互调用,这个时候就需要 RPC 框架了。不同服务节点之间的通信是如何做的呢?可以使用 Netty 来做。比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个类中的哪个方法以及相关参数吧! +实现一个自己的 HTTP 服务器 :通过 Netty 我们可以自己实现一个简单的 HTTP 服务器,这个大家应该不陌生。说到 HTTP 服务器的话,作为 Java 后端开发,我们一般使用 Tomcat 比较多。一个最基本的 HTTP 服务器可要以处理常见的 HTTP Method 的请求,比如 POST 请求、GET 请求等等。 +实现一个即时通讯系统 :使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系统,这方面的开源项目还蛮多的,可以自行去 Github 找一找。 +实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的。 + +#### 8.Netty 的零拷贝了解么? + +维基百科是这样介绍零拷贝的:“零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU 不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省 CPU 周期和内存带宽。 + +在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。而在 Netty 层面 ,零拷贝主要体现在对于数据操作的优化。 + +Netty 中的零拷贝体现在以下几个方面: +使用 Netty 提供的 CompositeByteBuf 类, 可以将多个ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。 +ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。 +通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题。 + + +#### 9.Netty 的心跳机制了解么? + +在 TCP 保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候, client 与 server 之间如果没有交互的话,它们是无法发现对方已经掉线的。为了解决这个问题, 我们就需要引入 心跳机制。 + +心跳机制的工作原理是: 在 client 与 server 之间在一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性. + +TCP 实际上自带的就有长连接选项,本身是也有心跳包机制,也就是 TCP 的选项:SO_KEEPALIVE。但是,TCP 协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在 Netty 层面通过编码实现。通过 Netty 实现心跳机制的话,核心类是 IdleStateHandler 。 + +#### 10.Netty 中有哪些重要组件? + +Channel:Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等。 + +EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。 + +ChannelFuture:Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。 + +ChannelHandler:充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。 + +ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。 + +#### 11.Netty 发送消息有几种方式? + +Netty 有两种发送消息的方式: + +直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动; +写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。 + +#### 12.Netty 支持哪些心跳类型设置? + +readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。 +writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。 +allIdleTime:所有类型的超时时间。 + +#### 13.说说Netty的执行流程? + +创建ServerBootStrap实例 +设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel +设置并绑定服务端的channel +创建处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码 SSl安全认证 +绑定并启动监听端口 +当轮训到准备就绪的channel后,由Reactor线程:NioEventLoop执行pipline中的方法,最终调度并执行channelHandler + +#### 14.Netty高性能体现在哪些方面? + +1)传输:IO模型在很大程度上决定了框架的性能,相比于bio,netty建议采用异步通信模式,因为nio一个线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞IO一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。正如代码中所示,使用的是NioEventLoopGroup和NioSocketChannel来提升传输效率。 +2)协议:采用什么样的通信协议,对系统的性能极其重要,netty默认提供了对Google Protobuf的支持,也可以通过扩展Netty的编解码接口,用户可以实现其它的高性能序列化框架。 +3)线程:netty使用了Reactor线程模型,但Reactor模型不同,对性能的影响也非常大,下面介绍常用的Reactor线程模型有三种,分别如下: +Reactor单线程模型:单线程模型的线程即作为NIO服务端接收客户端的TCP连接,又作为NIO客户端向服务端发起TCP连接,即读取通信对端的请求或者应答消息,又向通信对端发送消息请求或者应答消息。理论上一个线程可以独立处理所有IO相关的操作,但一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送,又因为当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,NIO线程会成为系统的性能瓶颈。 +Reactor多线程模型:有专门一个NIO线程用于监听服务端,接收客户端的TCP连接请求;网络IO操作(读写)由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现。但百万客户端并发连接时,一个nio线程用来监听和接受明显不够,因此有了主从多线程模型。 +主从Reactor多线程模型:利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题,即把监听服务端,接收客户端的TCP连接请求分给一个线程池。因此,在代码中可以看到,我们在server端选择的就是这种方式,并且也推荐使用该线程模型。在启动类中创建不同的EventLoopGroup实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型。 + +### 参考资料 +https://baijiahao.baidu.com/s?id=1669639041722396699 +https://blog.csdn.net/PCCEO1/article/details/95899920 +https://www.cnblogs.com/xiaoyangjia/p/11526197.html + diff --git a/README.md b/README.md index d4f5f62..d106808 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,649 @@ -# JavaInterview -全网最齐全的Java面试题库-附答案-持续更新 + + +这个Repo主要用来分享Java面试题,目前已经涵盖Java基础、Java多线程、Java虚拟机、MySQL、Redis、消息中间件、Kafka、RabbitMQ、微服务、Spring、MyBatis、Netty、Zookeeper、计算机网络、数据结构与算法、设计模式等内容,后续还会不断更新。 + +如果对于有所帮助,可以给个star。有纰漏的地方,欢迎给我们提PR。 + +如果想获取本Repo的PDF版本,可以用微信扫描下方二维码,回复 “pdf” ,即可获取。 + + + + + + +* [Java基础](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#Java基础) + * [1.说下面向对象四大特性](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#1说下面向对象四大特性) + * [2.Java语言有哪些特点](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#2java语言有哪些特点) + * [3.什么是Java程序的主类?应用程序和小程序的主类有何不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#3什么是java程序的主类应用程序和小程序的主类有何不同) + * [4.访问修饰符public,private,protected,以及不写(默认)时的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#4访问修饰符publicprivateprotected以及不写默认时的区别) + * [5.float f=3.4;是否正确?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#5float-f34是否正确) + * [6.Java有没有goto?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#6java有没有goto) + * [7.&和&&的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#7和的区别) + * [8.Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#8mathround115-等于多少mathround-115等于多少) + * [9.用最有效率的方法计算2乘以8?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#9用最有效率的方法计算2乘以8) + * [10.什么是Java注释](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#10什么是java注释) + * [11.Java有哪些数据类型](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#11java有哪些数据类型) + * [12.final 有什么用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#12final-有什么用) + * [13.final finally finalize的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#13final-finally-finalize的区别) + * [14.String str = "i" 和String str = new String("1")一样吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#14string-str--i-和string-str--new-string1一样吗) + * [15.Java 中操作字符串都有哪些类?它们之间有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#15java-中操作字符串都有哪些类它们之间有什么区别) + * [16.Java中为什么要用 clone?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#16java中为什么要用-clone) + * [17.深克隆和浅克隆?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#17深克隆和浅克隆) + * [18.new一个对象的过程和clone一个对象的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#18new一个对象的过程和clone一个对象的区别) + * [19.Java中实现多态的机制是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#19java中实现多态的机制是什么) + * [20.谈谈你对多态的理解?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#20谈谈你对多态的理解) + * [21.构造器(constructor)是否可被重写(override)?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#21构造器constructor是否可被重写override) + * [22.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#22两个对象值相同xequalsy--true但却可有不同的hash-code这句话对不对) + * [23.是否可以继承String类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#23是否可以继承string类) + * [24.String类的常用方法有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#24string类的常用方法有哪些) + * [25.char型变量中能否能不能存储一个中文汉字,为什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#25char型变量中能否能不能存储一个中文汉字为什么) + * [26.this关键字的用法](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#26this关键字的用法) + * [27.super关键字的用法](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#27super关键字的用法) + * [28.this与super的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#28this与super的区别) + * [29.static存在的主要意义](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#29static存在的主要意义) + * [30.static的独特之处](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#30static的独特之处) + * [31.static应用场景](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#31static应用场景) + * [32.static注意事项](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#32static注意事项) + * [33.break ,continue ,return 的区别及作用](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#33break-continue-return-的区别及作用) + * [34.在Java中定义一个不做事且没有参数的构造方法的作用](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#34在java中定义一个不做事且没有参数的构造方法的作用) + * [35.构造方法有哪些特性?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#35构造方法有哪些特性) + * [36.静态变量和实例变量区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#36静态变量和实例变量区别) + * [37.静态方法和实例方法有何不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#37静态方法和实例方法有何不同) + * [38.什么是方法的返回值?返回值的作用是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#38什么是方法的返回值返回值的作用是什么) + * [39.什么是内部类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#39什么是内部类) + * [40.内部类的分类有哪些](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#40内部类的分类有哪些) + * [41.Java中异常分为哪些种类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#41java中异常分为哪些种类) + * [42.hashCode 与 equals (重要)](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#42hashcode-与-equals-重要) + * [43.hashCode()介绍](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#43hashcode介绍) + * [44.为什么要有 hashCode](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#44为什么要有-hashcode) + * [45.抽象类和接口(Java7)的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#45抽象类和接口java7的区别) + * [46.Java 8的接口新增了哪些特性?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#46java-8的接口新增了哪些特性) + * [47.重写和重载的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#47重写和重载的区别) + * [48.ArrayList和LinkedList有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#48arraylist和linkedlist有什么区别) + * [49.HashMap是怎么实现的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#49hashmap是怎么实现的) + * [50.HashMap在Java7和Java8中的实现有什么不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#50hashmap在java7和java8中的实现有什么不同) + * [51.HashMap有时候会死循环,你知道是什么原因吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#51hashmap有时候会死循环你知道是什么原因吗) + * [52.ConcurrentHashMap是怎么实现的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#52concurrenthashmap是怎么实现的) + * [53.静态代理和动态代理的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#53静态代理和动态代理的区别) + * [54.JDK动态代理和CGLIB动态代理的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#54jdk动态代理和cglib动态代理的区别) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E5%9F%BA%E7%A1%80.md#参考链接) + +* [多线程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md) + + * [1.说说synchronized的实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#1说说synchronized的实现原理) + * [2.ReentrantLock与synchronized的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#2reentrantlock与synchronized的区别) + * [3.ReentrantLock实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#3reentrantlock实现原理) + * [4.Java原子类AtomicInteger实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#4java原子类atomicinteger实现原理) + * [5.Java线程池实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#5java线程池实现原理) + * [6.ThreadLocal实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#6threadlocal实现原理) + * [7.InheritableThreadLocal原理知道吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#7inheritablethreadlocal原理知道吗) + * [8.说一下synchronized锁升级过程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#8说一下synchronized锁升级过程) + * [9.了解过什么是“伪共享”吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#9了解过什么是伪共享吗) + * [10.“伪共享”出现的原因是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#10伪共享出现的原因是什么) + * [11.如何避免“伪共享”?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#11如何避免伪共享) + * [12.Java里的线程有哪些状态?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#12java里的线程有哪些状态) + * [13.什么是悲观锁?什么是乐观锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#13什么是悲观锁什么是乐观锁) + * [14.怎么停止一个运行中的线程?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#14怎么停止一个运行中的线程) + * [15.说一下你对volatile的理解?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#15说一下你对volatile的理解) + * [16.并发编程三要素?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#16并发编程三要素) + * [17.创建线程有哪些方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#17创建线程有哪些方式) + * [18.线程池的优点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#18线程池的优点) + * [19.CyclicBarrier和CountDownLatch的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#19cyclicbarrier和countdownlatch的区别) + * [20.什么是CAS?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#20什么是cas) + * [21.CAS的问题](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#21cas的问题) + * [22.什么是AQS?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#22什么是aqs) + * [23.AQS支持几种同步方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#23aqs支持几种同步方式) + * [24.什么是自旋锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#24什么是自旋锁) + * [25.什么是多线程的上下文切换?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#25什么是多线程的上下文切换) + * [26.什么是线程和进程?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#26什么是线程和进程) + * [27.程序计数器为什么是私有的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#27程序计数器为什么是私有的) + * [28.虚拟机栈和本地方法栈为什么是私有的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#28虚拟机栈和本地方法栈为什么是私有的) + * [29.并发与并行的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#29并发与并行的区别) + * [30.什么是线程死锁?如何避免死锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#30什么是线程死锁如何避免死锁) + * [31.sleep() 方法和 wait() 方法的区别和共同点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#31sleep-方法和-wait-方法的区别和共同点) + * [32.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#32为什么我们调用-start-方法时会执行-run-方法为什么我们不能直接调用-run-方法) + * [33.什么是线程安全问题?如何解决?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#33什么是线程安全问题如何解决) + * [34.什么是活锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#34什么是活锁) + * [35.什么是线程的饥饿问题?如何解决?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#35什么是线程的饥饿问题如何解决) + * [36.什么是线程的阻塞问题?如何解决?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#36什么是线程的阻塞问题如何解决) + * [37.synchronized 关键字和 volatile 关键字的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#37synchronized-关键字和-volatile-关键字的区别) + * [38.说一说几种常见的线程池及适用场景?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#38说一说几种常见的线程池及适用场景) + * [39.线程池都有哪几种工作队列?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#39线程池都有哪几种工作队列) + * [40.什么是线程安全?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#40什么是线程安全) + * [41.Java中如何获取到线程dump文件](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#41java中如何获取到线程dump文件) + * [42.Java中用到的线程调度算法是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#42java中用到的线程调度算法是什么) + * [43.Thread.sleep(0)的作用是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#43threadsleep0的作用是什么) + * [44.单例模式的线程安全性](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#44单例模式的线程安全性) + * [45.Semaphore有什么作用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#45semaphore有什么作用) + * [46.Hashtable的size()方法中明明只有一条语句"return count",为什么还要做同步?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#46hashtable的size方法中明明只有一条语句return-count为什么还要做同步) + * [47.同步方法和同步块,哪个是更好的选择?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#47同步方法和同步块哪个是更好的选择) + * [48.高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#48高并发任务执行时间短的业务怎样使用线程池并发不高任务执行时间长的业务怎样使用线程池并发高业务执行时间长的业务怎样使用线程池) + * [49.在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#49在java中lock接口比synchronized块的优势是什么你需要实现一个高效的缓存它允许多个用户读但只允许一个用户写以此来保持它的完整性你会怎样去实现它) + * [50.你将如何使用thread dump?你将如何分析Thread dump?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#50你将如何使用thread-dump你将如何分析thread-dump) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%A4%9A%E7%BA%BF%E7%A8%8B.md#参考资料) + +* [Java虚拟机](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md) + * [1.说一下JVM的内存结构?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#1说一下jvm的内存结构) + * [2.栈帧里面包含哪些东西?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#2栈帧里面包含哪些东西) + * [3.程序计数器有什么作用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#3程序计数器有什么作用) + * [4.字符串常量存放在哪个区域?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#4字符串常量存放在哪个区域) + * [5.你熟悉哪些垃圾收集算法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#5你熟悉哪些垃圾收集算法) + * [6.Java里有哪些引用类型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#6java里有哪些引用类型) + * [7.JVM怎么判断一个对象是不是要回收?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#7jvm怎么判断一个对象是不是要回收) + * [8.GC Roots 有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#8gc-roots-有哪些) + * [9.你知道哪些GC类型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#9你知道哪些gc类型) + * [10.对象都是优先分配在年轻代上的吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#10对象都是优先分配在年轻代上的吗) + * [11.你了解过哪些垃圾收集器?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#11你了解过哪些垃圾收集器) + * [12.说说CMS垃圾收集器的工作原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#12说说cms垃圾收集器的工作原理) + * [13.说说G1垃圾收集器的工作原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#13说说g1垃圾收集器的工作原理) + * [14.说说ZGC垃圾收集器的工作原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#14说说zgc垃圾收集器的工作原理) + * [15.ZGC收集器中的染色指针有什么用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#15zgc收集器中的染色指针有什么用) + * [16.说说类加载的过程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#16说说类加载的过程) + * [17.说下有哪些类加载器?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#17说下有哪些类加载器) + * [18.什么是双亲委派机制?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#18什么是双亲委派机制) + * [19.双亲委派机制可以被违背吗?请举例说明。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#19双亲委派机制可以被违背吗请举例说明) + * [20.Tomcat是怎么打破双亲委派机制的呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#20tomcat是怎么打破双亲委派机制的呢) + * [21.Java对象的布局了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#21java对象的布局了解过吗) + * [22.什么情况下会发生栈内存溢出?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#22什么情况下会发生栈内存溢出) + * [23.JVM新生代中为什么要分为Eden和Survivor?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#23jvm新生代中为什么要分为eden和survivor) + * [24.JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#24jvm中一次完整的gc流程是怎样的对象如何晋升到老年代) + * [25.什么是指令重排序?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#25什么是指令重排序) + * [26.什么是内存屏障?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#26什么是内存屏障) + * [27.什么是happen-before原则?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#27什么是happen-before原则) + * [28.说说你知道的几种主要的JVM参数](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#28说说你知道的几种主要的jvm参数) + * [29.怎么打出线程栈信息?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#29怎么打出线程栈信息) + * [30.为什么需要双亲委派模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#30为什么需要双亲委派模式) + * [31.怎么打破双亲委派模型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#31怎么打破双亲委派模型) + * [32.说一下堆和栈的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#32说一下堆和栈的区别) + * [33.Java 8 为什么要将永久代(PermGen)替换为元空间(MetaSpace)呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#33java-8-为什么要将永久代permgen替换为元空间metaspace呢) + * [34.说一下Java对象的创建过程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#34说一下java对象的创建过程) + * [35.对象的访问定位有哪几种方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#35对象的访问定位有哪几种方式) + * [36.说一下堆内存中对象的分配的基本策略](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#36说一下堆内存中对象的分配的基本策略) + * [37.Minor Gc和Full GC 有什么不同呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#37minor-gc和full-gc-有什么不同呢) + * [38.Java会存在内存泄漏吗?请简单描述。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#38java会存在内存泄漏吗请简单描述) + * [39.如何判断一个类是无用的类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#39如何判断一个类是无用的类) + * [40.介绍一下类文件结构吧!](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#40介绍一下类文件结构吧) + * [41.说一下 JVM 调优的工具?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#41说一下-jvm-调优的工具) + * [42.JVM调优命令有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#42jvm调优命令有哪些) + * [43.JRE、JDK、JVM 及 JIT 之间有什么不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#43jrejdkjvm-及-jit-之间有什么不同) + * [程序计数器为什么是私有的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#程序计数器为什么是私有的) + * [如何判断一个常量是废弃常量 ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#如何判断一个常量是废弃常量-) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Java%E8%99%9A%E6%8B%9F%E6%9C%BA.md#参考资料) + +* [Java IO](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md) + * [1.Java 中有几种类型的流?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#1java-中有几种类型的流) + * [2.什么是 java序列化?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#2什么是-java序列化) + * [3.如何实现 java 序列化?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#3如何实现-java-序列化) + * [4.字节流和字符流的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#4字节流和字符流的区别) + * [5.PrintStream、BufferedWriter、PrintWriter的比较?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#5printstreambufferedwriterprintwriter的比较) + * [6.什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#6什么是节点流什么是处理流它们各有什么用处处理流的创建有什么特征) + * [7.流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#7流一般需要不需要关闭如果关闭的话在用什么方法一般要在那个代码块里面关闭比较好处理流是怎么关闭的如果有多个流互相调用传入是怎么关闭的) + * [8.什么是BIO](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#8什么是bio) + * [9.什么是NIO](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#9什么是nio) + * [10.什么是AIO](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#10什么是aio) + * [11.同步与异步](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#11同步与异步) + * [12.阻塞与非阻塞](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#12阻塞与非阻塞) + * [13.同步、异步、阻塞、非堵塞](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#13同步异步阻塞非堵塞) + * [14.通道是个什么意思?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#14通道是个什么意思) + * [15.缓冲区是什么意思?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#15缓冲区是什么意思) + * [16.IO多路复用的底层原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#16io多路复用的底层原理) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/JavaIO.md#参考链接) + +* [MySQL](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md) + * [1. 什么是索引?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#1-什么是索引) + * [2.索引是个什么样的数据结构呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#2索引是个什么样的数据结构呢) + * [3.Hash索引和B+树索引有什么区别或者说优劣呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#3hash索引和b树索引有什么区别或者说优劣呢) + * [4.在建立索引的时候,都有哪些需要考虑的因素呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#4在建立索引的时候都有哪些需要考虑的因素呢) + * [5.了解过哪些存储引擎?各有什么优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#5了解过哪些存储引擎各有什么优缺点) + * [6.说一下什么是事务的ACID属性吧](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#6说一下什么是事务的acid属性吧) + * [7.事务的隔离级别了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#7事务的隔离级别了解过吗) + * [8.说说InnoDB的索引原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#8说说innodb的索引原理) + * [说说InnoDB的MVCC机制](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#说说innodb的mvcc机制) + * [9.有了解过“回表”的概念吗?什么情况下会出现“回表”?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#9有了解过回表的概念吗什么情况下会出现回表) + * [10.MySQL索引的类型](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#10mysql索引的类型) + * [11.有做过MySQL的索引优化吗](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#11有做过mysql的索引优化吗) + * [12.什么是聚簇索引?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#12什么是聚簇索引) + * [13.InnoDB有聚簇索引吗?MyIsam呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#13innodb有聚簇索引吗myisam呢) + * [14.MyIsam的数据是怎么存储的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#14myisam的数据是怎么存储的) + * [15.InnoDB的数据是怎么存储的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#15innodb的数据是怎么存储的) + * [16.InnoDB主键索引跟非主键索引在数据存储上的差异](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#16innodb主键索引跟非主键索引在数据存储上的差异) + * [17.InnoDB删除某条记录后,内部会怎么处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#17innodb删除某条记录后内部会怎么处理) + * [18.InnoDB如果没有设置主键的话,它内部会怎么处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#18innodb如果没有设置主键的话它内部会怎么处理) + * [19.为什么InnoDB一定会生成主键?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#19为什么innodb一定会生成主键) + * [20.MySQL分库分表了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#20mysql分库分表了解过吗) + * [21.MySQL的redo日志和undo日志分别有什么用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#21mysql的redo日志和undo日志分别有什么用) + * [22.MySQL的redo日志的刷盘时机](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#22mysql的redo日志的刷盘时机) + * [23.MySQL有哪些锁?以及各种锁的作用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#23mysql有哪些锁以及各种锁的作用) + * [24.MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#24mysql中varchar与char的区别以及varchar50中的50代表的涵义) + * [25.MySQL有哪些日志,分别是什么用处?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#25mysql有哪些日志分别是什么用处) + * [26.在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#26在哪些情况下会发生针对该列创建了索引但是在查询的时候并没有使用呢) + * [27. 为什么要尽量设定一个主键?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#27-为什么要尽量设定一个主键) + * [28.主键使用自增ID还是UUID?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#28主键使用自增id还是uuid) + * [29.字段为什么要求定义为not null?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#29字段为什么要求定义为not-null) + * [30.如果要存储用户的密码散列,应该使用什么字段进行存储?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#30如果要存储用户的密码散列应该使用什么字段进行存储) + * [31.varchar(10)和int(10)代表什么含义?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#31varchar10和int10代表什么含义) + * [32.MySQL的binlog有有几种录入格式?分别有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#32mysql的binlog有有几种录入格式分别有什么区别) + * [33.超大分页怎么处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#33超大分页怎么处理) + * [34.关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#34关心过业务系统里面的sql耗时吗统计过慢查询吗对慢查询都怎么优化过) + * [35.什么是存储过程?有哪些优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#35什么是存储过程有哪些优缺点) + * [36.说一说三个范式](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#36说一说三个范式) + * [37.什么情况下应不建或少建索引](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#37什么情况下应不建或少建索引) + * [38.什么是表分区?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#38什么是表分区) + * [39.表分区与分表的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#39表分区与分表的区别) + * [40.表分区有什么好处?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#40表分区有什么好处) + * [41.MVVC了解过吗](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#41mvvc了解过吗) + * [42.在MVCC并发控制中,读操作可以分成哪几类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#42在mvcc并发控制中读操作可以分成哪几类) + * [43.行级锁定的优点](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#43行级锁定的优点) + * [44.行级锁定的缺点](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#44行级锁定的缺点) + * [45.MySQL优化](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#45mysql优化) + * [46.key和index的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#46key和index的区别) + * [47.delete、truncate、drop区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#47deletetruncatedrop区别) + * [48.MySQL主从复制原理流程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#48mysql主从复制原理流程) + * [49.自增主键最大ID记录,MyISAM和InnoDB分别是如何存储的](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#49自增主键最大id记录myisam和innodb分别是如何存储的) + * [50.Mysql如何优化DISTINCT?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#50mysql如何优化distinct) + * [51.解释MySQL外连接、内连接与自连接的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#51解释mysql外连接内连接与自连接的区别) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/MySQL.md#参考链接) + +* [Redis](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md) + * [1.什么是Redis?简述它的优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#1什么是redis简述它的优缺点) + * [2.Redis相比memcached有哪些优势?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#2redis相比memcached有哪些优势) + * [3.Redis有哪些数据结构?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#3redis有哪些数据结构) + * [4.Redis主要消耗什么物理资源?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#4redis主要消耗什么物理资源) + * [5.Redis的全称是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#5redis的全称是什么) + * [6.一个字符串类型的值能存储最大容量是多少?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#6一个字符串类型的值能存储最大容量是多少) + * [7.Redis为什么那么快?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#7redis为什么那么快) + * [8.Redis如何实现分布式锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#8redis如何实现分布式锁) + * [9.Redis是单线程还是多线程?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#9redis是单线程还是多线程) + * [10.Redis 官方为什么不提供 Windows 版本?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#10redis-官方为什么不提供-windows-版本) + * [11.为什么 Redis 需要把所有数据放到内存中?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#11为什么-redis-需要把所有数据放到内存中) + * [12.Redis如何设置密码及验证密码?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#12redis如何设置密码及验证密码) + * [13.Redis集群如何选择数据库?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#13redis集群如何选择数据库) + * [14.缓存失效?缓存穿透?缓存雪崩?缓存并发?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#14缓存失效缓存穿透缓存雪崩缓存并发) + * [15.Redis中的热key怎么处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#15redis中的热key怎么处理) + * [16.Redis中的大key怎么处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#16redis中的大key怎么处理) + * [17.使用Redis统计网站的UV,应该怎么做?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#17使用redis统计网站的uv应该怎么做) + * [18.Redis事务机制了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#18redis事务机制了解过吗) + * [19.Redis key的淘汰策略有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#19redis-key的淘汰策略有哪些) + * [20.Redis在什么情况下会触发key的回收?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#20redis在什么情况下会触发key的回收) + * [21.Redis的持久化了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#21redis的持久化了解过吗) + * [22.Redis在集群种查找key的时候,是怎么定位到具体节点的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#22redis在集群种查找key的时候是怎么定位到具体节点的) + * [23.Redis集群各个节点之间是怎么保持数据一致性的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#23redis集群各个节点之间是怎么保持数据一致性的) + * [24.用Redis做延时队列,具体应该怎么实现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#24用redis做延时队列具体应该怎么实现) + * [25.Redis String的内部编码有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#25redis-string的内部编码有哪些) + * [26.Redis 集群方案应该怎么做?都有哪些方案?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#26redis-集群方案应该怎么做都有哪些方案) + * [27.Redis 集群方案什么情况下会导致整个集群不可用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#27redis-集群方案什么情况下会导致整个集群不可用) + * [28.MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#28mysql-里有-2000w-数据redis-中只存-20w-的数据如何保证-redis-中的数据都是热点数据) + * [29.Redis有哪些适合的场景?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#29redis有哪些适合的场景) + * [30.Redis和Redisson有什么关系?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#30redis和redisson有什么关系) + * [31.Redis中的管道有什么用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#31redis中的管道有什么用) + * [32.Redis如何做内存优化?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#32redis如何做内存优化) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Redis.md#参考链接) + + +* [Spring](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md) + * [1.什么是spring?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#1什么是spring) + * [2.使用Spring框架的好处是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#2使用spring框架的好处是什么) + * [3.Spring由哪些模块组成?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#3spring由哪些模块组成) + * [4.Spring是怎么解决循环依赖的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#4spring是怎么解决循环依赖的) + * [5.Spring Boot手动装配有哪几种方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#5spring-boot手动装配有哪几种方式) + * [6.Spring Boot自动配置原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#6spring-boot自动配置原理) + * [7.谈谈自己对于Spring IOC的理解](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#7谈谈自己对于spring-ioc的理解) + * [8.谈谈自己对于Spring AOP的理解](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#8谈谈自己对于spring-aop的理解) + * [9.Spring AOP和AspectJ AOP有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#9spring-aop和aspectj-aop有什么区别) + * [10.Spring中的bean的作用域有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#10spring中的bean的作用域有哪些) + * [11.Spring中的单例bean的线程安全问题了解吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#11spring中的单例bean的线程安全问题了解吗) + * [12.Spring中的bean生命周期了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#12spring中的bean生命周期了解过吗) + * [13.Spring MVC的工作原理了解嘛?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#13spring-mvc的工作原理了解嘛) + * [14.Spring框架中用到了哪些设计模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#14spring框架中用到了哪些设计模式) + * [15.@Component和@Bean的区别是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#15component和bean的区别是什么) + * [16.将一个类声明为Spring的bean的注解有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#16将一个类声明为spring的bean的注解有哪些) + * [17.Spring事务管理的方式有几种?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#17spring事务管理的方式有几种) + * [18.Spring事务中的隔离级别有哪几种?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#18spring事务中的隔离级别有哪几种) + * [19.Spring事务中有哪几种事务传播行为?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#19spring事务中有哪几种事务传播行为) + * [20.Spring 事务底层原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#20spring-事务底层原理) + * [21.BeanFactory和ApplicationContext有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#21beanfactory和applicationcontext有什么区别) + * [22.Resource 是如何被查找、加载的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#22resource-是如何被查找加载的) + * [23.解释自动装配的各种模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#23解释自动装配的各种模式) + * [24.有哪些不同类型的IOC(依赖注入)?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#24有哪些不同类型的ioc依赖注入) + * [25.Spring AOP 实现原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#25spring-aop-实现原理) + * [26.ApplicationContext通常的实现是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#26applicationcontext通常的实现是什么) + * [27. Bean 工厂和 Application contexts 有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#27-bean-工厂和-application-contexts-有什么区别) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Spring.md#参考资料) + + +* [Spring Boot](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md) + + * [1.什么是springboot](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#1什么是springboot) + * [2.Spring Boot 有哪些优点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#2spring-boot-有哪些优点) + * [3. 创建一个 Spring Boot Project 的最简单的方法是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#3-创建一个-spring-boot-project-的最简单的方法是什么) + * [4.Spring 和 SpringBoot 有什么不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#4spring-和-springboot-有什么不同) + * [5.如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#5如何重新加载-spring-boot-上的更改而无需重新启动服务器) + * [6.Spring Boot 中的监视器是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#6spring-boot-中的监视器是什么) + * [7.如何在 Spring Boot 中禁用 Actuator 端点安全性?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#7如何在-spring-boot-中禁用-actuator-端点安全性) + * [8.怎么使用 Maven 来构建一个 SpringBoot 程序?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#8怎么使用-maven-来构建一个-springboot-程序) + * [9.Spring Initializr 是创建 Spring Boot Projects 的唯一方法吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#9spring-initializr-是创建-spring-boot-projects-的唯一方法吗) + * [10.为什么我们需要 spring-boot-maven-plugin?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#10为什么我们需要-spring-boot-maven-plugin) + * [11.什么是嵌入式服务器?我们为什么要使用嵌入式服务器呢?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#11什么是嵌入式服务器我们为什么要使用嵌入式服务器呢) + * [12.如何在 Spring Boot 中添加通用的 JS 代码?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#12如何在-spring-boot-中添加通用的-js-代码) + * [13.如何使用 Spring Boot 部署到不同的服务器?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#13如何使用-spring-boot-部署到不同的服务器) + * [14.如何使用配置文件通过 Spring Boot 配置特定环境的配置?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#14如何使用配置文件通过-spring-boot-配置特定环境的配置) + * [15.什么是Swagger?你用Spring Boot实现了吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#15什么是swagger你用spring-boot实现了吗) + * [16.如何实现Spring Boot应用程序的安全性?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#16如何实现spring-boot应用程序的安全性) + * [17.比较一下Spring Security和Shiro各自的优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#17比较一下spring-security和shiro各自的优缺点) + * [18.Spring Boot中如何解决跨域问题?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#18spring-boot中如何解决跨域问题) + * [19.Spring Boot的核心注解是哪些?他由哪几个注解组成的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#19spring-boot的核心注解是哪些他由哪几个注解组成的) + * [20.保护SpringBoot应用有哪些方法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#20保护springboot应用有哪些方法) + * [21.SpringBoot 2.X有哪些新特性?与1.X有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#21springboot-2x有哪些新特性与1x有什么区别) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringBoot.md#参考链接) + +* [Spring Cloud](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md) + + * [1.什么是 Spring Cloud?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#1什么是-spring-cloud) + * [2.使用Spring Cloud有什么优势?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#2使用spring-cloud有什么优势) + * [3.服务注册和发现是什么意思?Spring Cloud如何实现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#3服务注册和发现是什么意思spring-cloud如何实现) + * [4.Spring Cloud由哪些组件组成?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#4spring-cloud由哪些组件组成) + * [5.什么是Hystrix?它如何实现容错?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#5什么是hystrix它如何实现容错) + * [6.什么是Hystrix断路器?我们需要它吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#6什么是hystrix断路器我们需要它吗) + * [7.什么是Netflix Feign?它的优点是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#7什么是netflix-feign它的优点是什么) + * [8.Eureka的工作原理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#8eureka的工作原理) + * [9.说说Eureka的自我保护机制?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#9说说eureka的自我保护机制) + * [10.什么是zuul?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#10什么是zuul) + * [11.zuul的工作流程?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#11zuul的工作流程) + * [12.什么是服务熔断?什么是服务降级?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#12什么是服务熔断什么是服务降级) + * [13.什么是服务雪崩效应?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#13什么是服务雪崩效应) + * [14.ZuulFilter有哪些常用方法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#14zuulfilter有哪些常用方法) + * [15.如何实现动态Zuul网关路由转发?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#15如何实现动态zuul网关路由转发) + * [16.什么是 Spring Cloud Bus?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#16什么是-spring-cloud-bus) + * [17.Spring Cloud Bus 原理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#17spring-cloud-bus-原理) + * [18.SpringCloud Config可以实现实时刷新吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#18springcloud-config可以实现实时刷新吗) + * [19.Eureka和zookeeper都可以提供服务注册与发现的功能,两者的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#19eureka和zookeeper都可以提供服务注册与发现的功能两者的区别) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/SpringCloud.md#参考链接) + + + +* [Mybatis](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md) + + * [1.什么是Mybatis?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#1什么是mybatis) + * [2.Mybatis的优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#2mybatis的优缺点) + * [3.Mybatis使用场合?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#3mybatis使用场合) + * [4.https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#{}和${}的区别是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#4和的区别是什么) + * [5.当实体类的属性名和表种字段名不一致怎么办?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#5当实体类的属性名和表种字段名不一致怎么办) + * [6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#6mybatis是如何将sql执行结果封装为目标对象并返回的都有哪些映射形式) + * [7.如何获取自动生成的(主)键值?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#7如何获取自动生成的主键值) + * [8.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#8mybatis的xml映射文件中不同的xml映射文件id是否可以重复) + * [9. Mybatis动态SQL?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#9-mybatis动态sql) + * [10.说一下resultMap和resultType?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#10说一下resultmap和resulttype) + * [11. Mybatis全局配置文件中有哪些标签?分别代表什么意思?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#11-mybatis全局配置文件中有哪些标签分别代表什么意思) + * [12.Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#12mybatis能执行一对一一对多的关联查询吗都有哪些实现方式以及它们之间的区别) + * [13.Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#13mybatis是否支持延迟加载如果支持它的实现原理是什么) + * [14.Mybatis都有哪些Executor执行器?它们之间的区别是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#14mybatis都有哪些executor执行器它们之间的区别是什么) + * [15.Mybatis的一级、二级缓存](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#15mybatis的一级二级缓存) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Mybatis.md#参考链接) + +* [Netty](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md) + + * [1.你了解过哪些IO模型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#1你了解过哪些io模型) + * [2.什么是Reactor模型?Reactor的3种版本都知道吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#2什么是reactor模型reactor的3种版本都知道吗) + * [3.了解过粘包拆包吗?为什么会出现粘包拆包?怎么处理粘包拆包?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#3了解过粘包拆包吗为什么会出现粘包拆包怎么处理粘包拆包) + * [4.UDP协议会有粘包拆包的问题吗?为什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#4udp协议会有粘包拆包的问题吗为什么) + * [5.Netty 是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#5netty-是什么) + * [6.为什么要用 Netty?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#6为什么要用-netty) + * [7.Netty 的应用场景了解么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#7netty-的应用场景了解么) + * [8.Netty 的零拷贝了解么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#8netty-的零拷贝了解么) + * [9.Netty 的心跳机制了解么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#9netty-的心跳机制了解么) + * [10.Netty 中有哪些重要组件?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#10netty-中有哪些重要组件) + * [11.Netty 发送消息有几种方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#11netty-发送消息有几种方式) + * [12.Netty 支持哪些心跳类型设置?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#12netty-支持哪些心跳类型设置) + * [13.说说Netty的执行流程?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#13说说netty的执行流程) + * [14.Netty高性能体现在哪些方面?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#14netty高性能体现在哪些方面) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Netty.md#参考资料) + +* [微服务](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md) + + * [1.微服务有哪些优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#1微服务有哪些优缺点) + * [2.作为注册中心,Zookeeper和Eureka有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#2作为注册中心zookeeper和eureka有什么区别) + * [3.Service Mesh了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#3service-mesh了解过吗) + * [4.微服务有哪些特点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#4微服务有哪些特点) + * [5.单片,SOA 和微服务架构有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#5单片soa-和微服务架构有什么区别) + * [6.Spring Cloud 解决了哪些问题?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#6spring-cloud-解决了哪些问题) + * [7.服务注册和发现是什么意思?Spring Cloud 如何实现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#7服务注册和发现是什么意思spring-cloud-如何实现) + * [8.Spring Cloud 和dubbo的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#8spring-cloud-和dubbo的区别) + * [9.什么是微服务?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#9什么是微服务) + * [10.微服务之间是如何通讯的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#10微服务之间是如何通讯的) + * [11.请谈谈对SpringBoot 和SpringCloud的理解](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#11请谈谈对springboot-和springcloud的理解) + * [12.什么是服务熔断,什么是服务降级](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#12什么是服务熔断什么是服务降级) + * [13.你所知道的微服务技术栈有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#13你所知道的微服务技术栈有哪些) + * [14.什么是 Eureka服务注册与发现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#14什么是-eureka服务注册与发现) + * [15.Eureka的基本架构是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#15eureka的基本架构是什么) + * [16.作为服务注册中心,Eureka比Zookeeper好在哪里?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#16作为服务注册中心eureka比zookeeper好在哪里) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%BE%AE%E6%9C%8D%E5%8A%A1.md#参考资料) + +* [Zookeeper](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md) + + * [1.Zookeeper有哪些节点类型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#1zookeeper有哪些节点类型) + * [2.了解过Zookeeper的ZAB协议吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#2了解过zookeeper的zab协议吗) + * [3.Zookeeper怎么实现分布式锁?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#3zookeeper怎么实现分布式锁) + * [4.Zookeeper是怎么保证数据一致性的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#4zookeeper是怎么保证数据一致性的) + * [5.Zookeeper Leader选举过程是怎样的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#5zookeeper-leader选举过程是怎样的) + * [6.Zookeeper怎么实现服务注册?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#6zookeeper怎么实现服务注册) + * [7.ZooKeeper是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#7zookeeper是什么) + * [8.ZooKeeper提供了什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#8zookeeper提供了什么) + * [9.Zookeeper文件系统](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#9zookeeper文件系统) + * [10.Zookeeper Watcher 机制](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#10zookeeper-watcher-机制) + * [11.客户端注册Watcher实现](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#11客户端注册watcher实现) + * [12.服务端处理Watcher实现](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#12服务端处理watcher实现) + * [13.ACL权限控制机制](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#13acl权限控制机制) + * [14.服务器角色](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#14服务器角色) + * [15.Zookeeper 下 Server工作状态](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#15zookeeper-下-server工作状态) + * [16.数据同步](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#16数据同步) + * [17.zookeeper是如何保证事务的顺序一致性的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#17zookeeper是如何保证事务的顺序一致性的) + * [18.分布式集群中为什么会有Master?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#18分布式集群中为什么会有master) + * [19.zk节点宕机如何处理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#19zk节点宕机如何处理) + * [20.Zookeeper有哪几种部署模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#20zookeeper有哪几种部署模式) + * [21.集群最少要几台机器,集群规则是怎样的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#21集群最少要几台机器集群规则是怎样的) + * [22.集群支持动态添加机器吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#22集群支持动态添加机器吗) + * [23.Zookeeper对节点的watch监听通知是永久的吗?为什么不是永久的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#23zookeeper对节点的watch监听通知是永久的吗为什么不是永久的) + * [24.ZAB和Paxos算法的联系与区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#24zab和paxos算法的联系与区别) + * [25.Zookeeper的典型应用场景](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#25zookeeper的典型应用场景) + * [26.Zookeeper 和 Dubbo 的关系?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#26zookeeper-和-dubbo-的关系) + * [27.zookeeper负载均衡和nginx负载均衡区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#27zookeeper负载均衡和nginx负载均衡区别) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Zookeeper.md#参考资料) + + +* [消息队列](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md) + * [1.消息队列有哪些应用场景?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#1消息队列有哪些应用场景) + * [2.消息队列的弊端有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#2消息队列的弊端有哪些) + * [3.使用消息队列,怎么确保消息不丢失?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#3使用消息队列怎么确保消息不丢失) + * [4.使用消息队列,如果处理重复消息?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#4使用消息队列如果处理重复消息) + * [5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#5kafka的消息是有序的吗如果保证kafka消息的顺序性) + * [6.消息如何保证幂等性](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#6消息如何保证幂等性) + * [7.消息队列积压怎么办](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#7消息队列积压怎么办) + * [8.各种MQ的比较](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#8各种mq的比较) + * [9.如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时怎么解决?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#9如何解决消息队列的延时以及过期失效问题消息队列满了以后该怎么处理有几百万消息持续积压几小时怎么解决) + * [10.为什么使用消息队列?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#10为什么使用消息队列) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97.md#参考链接) + +* [Kafka](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md) + * [1.为什么要使用 kafka?为什么要使用消息队列?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#1为什么要使用-kafka为什么要使用消息队列) + * [2.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#2kafka中的israr又代表什么isr的伸缩又指什么) + * [3.kafka中的broker 是干什么的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#3kafka中的broker-是干什么的) + * [4.kafka中的 zookeeper 起到什么作用?可以不用zookeeper么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#4kafka中的-zookeeper-起到什么作用可以不用zookeeper么) + * [5.kafka follower如何与leader同步数据?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#5kafka-follower如何与leader同步数据) + * [6.什么情况下一个 broker 会从 ISR 中被踢出去?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#6什么情况下一个-broker-会从-isr-中被踢出去) + * [7.kafka 为什么那么快?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#7kafka-为什么那么快) + * [8.kafka producer如何优化打入速度?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#8kafka-producer如何优化打入速度) + * [9.kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit 了](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#9kafka-producer-打数据ack--为-0-1--1-的时候代表啥-设置--1-的时候什么情况下leader-会认为一条消息-commit-了) + * [10.kafka unclean 配置代表啥?会对 spark streaming 消费有什么影响?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#10kafka--unclean-配置代表啥会对-spark-streaming-消费有什么影响) + * [11.如果leader crash时,ISR为空怎么办?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#11如果leader-crash时isr为空怎么办) + * [12.kafka的message格式是什么样的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#12kafka的message格式是什么样的) + * [13.kafka中consumer group 是什么概念?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#13kafka中consumer-group-是什么概念) + * [14.Kafka中的消息是否会丢失和重复消费?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#14kafka中的消息是否会丢失和重复消费) + * [15.为什么Kafka不支持读写分离?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#15为什么kafka不支持读写分离) + * [16.Kafka中是怎么体现消息顺序性的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#16kafka中是怎么体现消息顺序性的) + * [17.kafka如何实现延迟队列?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#17kafka如何实现延迟队列) + * [18.什么是消费者组?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#18什么是消费者组) + * [19.解释下 Kafka 中位移(offset)的作用。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#19解释下-kafka-中位移offset的作用) + * [20.阐述下 Kafka 中的领导者副本(Leader Replica)和追随者副本 (Follower Replica)的区别。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#20阐述下-kafka-中的领导者副本leader-replica和追随者副本-follower-replica的区别) + * [21.如何设置 Kafka 能接收的最大消息的大小?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#21如何设置-kafka-能接收的最大消息的大小) + * [22.监控 Kafka 的框架都有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#22监控-kafka-的框架都有哪些) + * [23.Broker 的 Heap Size 如何设置?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#23broker-的-heap-size-如何设置) + * [24.如何估算 Kafka 集群的机器数量?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#24如何估算-kafka-集群的机器数量) + * [25.Leader 总是 -1,怎么破?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#25leader-总是--1怎么破) + * [26.LEO、LSO、AR、ISR、HW 都表示什么含义?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#26leolsoarisrhw-都表示什么含义) + * [27.Kafka 能手动删除消息吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#27kafka-能手动删除消息吗) + * [28.consumer_offsets 是做什么用的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#28consumer_offsets-是做什么用的) + * [29.分区 Leader 选举策略有几种?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#29分区-leader-选举策略有几种) + * [30.Kafka 的哪些场景中使用了零拷贝(Zero Copy)?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#30kafka-的哪些场景中使用了零拷贝zero-copy) + * [31.如何调优 Kafka?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#31如何调优-kafka) + * [32.Controller 发生网络分区(Network Partitioning)时,Kafka 会怎么样?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#32controller-发生网络分区network-partitioning时kafka-会怎么样) + * [33.Java Consumer 为什么采用单线程来获取消息?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#33java-consumer-为什么采用单线程来获取消息) + * [34.简述 Follower 副本消息同步的完整流程。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#34简述-follower-副本消息同步的完整流程) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Kafka.md#参考资料) + +* [RabbitMQ](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md) + * [1.什么是RabbitMQ?为什么使用RabbitMQ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#1什么是rabbitmq为什么使用rabbitmq) + * [2.RabbitMQ有什么优缺点?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#2rabbitmq有什么优缺点) + * [3.什么是元数据?元数据分为哪些类型?包括哪些内容?与cluster相关的元数据有哪些?元数据是如何保存的?元数据在cluster中是如何分布的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#3什么是元数据元数据分为哪些类型包括哪些内容与cluster相关的元数据有哪些元数据是如何保存的元数据在cluster中是如何分布的) + * [4.在单node系统和多node构成的cluster系统中声明queue、exchange,以及进行binding会有什么不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#4在单node系统和多node构成的cluster系统中声明queueexchange以及进行binding会有什么不同) + * [5.客户端连接到cluster中的任意node上是否都能正常工作?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#5客户端连接到cluster中的任意node上是否都能正常工作) + * [6.若cluster中拥有某个queue的owner node失效了,且该queue 被声明具有durable属性,是否能够成功从其他node上重新声明该 queue ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#6若cluster中拥有某个queue的owner-node失效了且该queue-被声明具有durable属性是否能够成功从其他node上重新声明该-queue-) + * [7.RabbitMQ 的消息是怎么发送的?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#7rabbitmq-的消息是怎么发送的) + * [8.RabbitMQ 怎么避免消息丢失?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#8rabbitmq-怎么避免消息丢失) + * [9.RabbitMQ的使用场景有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#9rabbitmq的使用场景有哪些) + * [10.RabbitMQ有哪些重要的角色?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#10rabbitmq有哪些重要的角色) + * [11.如何确保消息正确地发送至RabbitMQ?如何确保消息接收方消费了消息?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#11如何确保消息正确地发送至rabbitmq如何确保消息接收方消费了消息) + * [12.要保证消息持久化成功的条件有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#12要保证消息持久化成功的条件有哪些) + * [13.RabbitMQ 有几种广播类型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#13rabbitmq-有几种广播类型) + * [14.vhost 是什么?起什么作用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#14vhost-是什么起什么作用) + * [15.消息基于什么传输?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#15消息基于什么传输) + * [16.消息如何分发?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#16消息如何分发) + * [17.消息怎么路由?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#17消息怎么路由) + * [18.如何确保消息接收方消费了消息?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#18如何确保消息接收方消费了消息) + * [19.如何避免消息重复投递或重复消费?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#19如何避免消息重复投递或重复消费) + * [20.死信队列和延迟队列的使用](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#20死信队列和延迟队列的使用) + * [参考链接:](https://github.com/JavaInterviewHub/JavaInterview/blob/main/RabbitMQ.md#参考链接) + + +* [计算机网络](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md) + * [1.请简述TCP/UDP的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#1请简述tcpudp的区别) + * [2.TCP对应的协议和UDP对应的协议](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#2tcp对应的协议和udp对应的协议) + * [3.有哪些私有(保留)地址?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#3有哪些私有保留地址) + * [4.你能说一说OSI七层模型?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#4你能说一说osi七层模型) + * [5.说一说TCP/IP四层模型](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#5说一说tcpip四层模型) + * [6. 简述IP地址的分类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#6-简述ip地址的分类) + * [7.简述ARP地址解析协议工作原理](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#7简述arp地址解析协议工作原理) + * [8.简述ICMP、TFTP、HTTP、NAT、DHCP协议](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#8简述icmptftphttpnatdhcp协议) + * [9.说一说TCP的三次握手](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#9说一说tcp的三次握手) + * [10.为什么TCP要三次握手](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#10为什么tcp要三次握手) + * [11.TCP建立连接时为什么要传回 SYN](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#11tcp建立连接时为什么要传回-syn) + * [12.TCP为什么要四次挥手](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#12tcp为什么要四次挥手) + * [13.滑动窗口和流量控制](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#13滑动窗口和流量控制) + * [14.拥塞控制](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#14拥塞控制) + * [15.在浏览器中输入url地址到显示主页的过程](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#15在浏览器中输入url地址到显示主页的过程) + * [16.HTTP协议包括哪些请求?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#16http协议包括哪些请求) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md#参考链接) + + +* [数据结构与算法](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md) + * [1.什么是算法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#1什么是算法) + * [2.TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#2treemap和treeset在排序时如何比较元素collections工具类中的sort方法如何比较元素) + * [3.如何知道二叉树的深度?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#3如何知道二叉树的深度) + * [4.介绍一下,堆排序的原理是什么?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#4介绍一下堆排序的原理是什么) + * [5.数组和链表的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#5数组和链表的区别) + * [6.二分查找了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#6二分查找了解过吗) + * [7.说下你熟悉的排序算法](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#7说下你熟悉的排序算法) + * [8.布隆过滤器了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#8布隆过滤器了解过吗) + * [9.一致性hash算法了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#9一致性hash算法了解过吗) + * [10.如何在一个1到100的整数数组中找到丢失的数字?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#10如何在一个1到100的整数数组中找到丢失的数字) + * [11.请你讲讲LRU算法的实现原理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#11请你讲讲lru算法的实现原理) + * [12.为什么要设计后缀表达式,有什么好处?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#12为什么要设计后缀表达式有什么好处) + * [13. 什么是B树?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#13-什么是b树) + * [14.什么是B+树?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#14什么是b树) + * [15.谈一谈,id全局唯一且自增,如何实现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#15谈一谈id全局唯一且自增如何实现) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95.md#参考链接) + + +* [设计模式](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md) + * [1.接口是什么?为什么要使用接口而不是直接使用具体类?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#1接口是什么为什么要使用接口而不是直接使用具体类) + * [2.设计模式六大原则?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#2设计模式六大原则) + * [3.Java怎么实现单例模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#3java怎么实现单例模式) + * [4.什么是代理模式?什么是动态代理?Java中动态代理有哪些实现方式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#4什么是代理模式什么是动态代理java中动态代理有哪些实现方式) + * [5.设计模式的类型](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#5设计模式的类型) + * [6.说说你所熟悉或听说过的 j2ee 中的几种常用模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#6说说你所熟悉或听说过的-j2ee-中的几种常用模式) + * [7.简述一下你了解的 Java 设计模式(总结)](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#7简述一下你了解的-java-设计模式总结) + * [8.适配器模式是什么?什么时候使用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#8适配器模式是什么什么时候使用) + * [9.适配器模式与装饰器模式有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#9适配器模式与装饰器模式有什么区别) + * [10.适配器模式和代理模式之间有什么不同?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#10适配器模式和代理模式之间有什么不同) + * [11.什么是模板方法模式?试举例说明。](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#11什么是模板方法模式试举例说明) + * [12.OOP中的组合、聚合和关联有什么区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#12oop中的组合聚合和关联有什么区别) + * [13.给我一个符合开闭原则的设计模式的例子?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#13给我一个符合开闭原则的设计模式的例子) + * [14.工厂模式与抽象工厂模式的区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#14工厂模式与抽象工厂模式的区别) + * [15.举出一个例子,在这种情况你会更倾向于使用抽象类,而不是接口?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#15举出一个例子在这种情况你会更倾向于使用抽象类而不是接口) + * [16.Dubbo 源码使用了哪些设计模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#16dubbo-源码使用了哪些设计模式) + * [17.Spring 当中用到了哪些设计模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#17spring-当中用到了哪些设计模式) + * [参考链接](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#参考链接) + + +* [分布式](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md) + * [1.分布式id如何生成?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#1分布式id如何生成) + * [2.雪花算法了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#2雪花算法了解过吗) + * [3.什么是CAP定理?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#3什么是cap定理) + * [4.分布式事务了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#4分布式事务了解过吗) + * [5.什么是二阶段提交(2PC)?什么是三阶段提交(3PC)?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#5什么是二阶段提交2pc什么是三阶段提交3pc) + * [6.TCC了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#6tcc了解过吗) + * [7.Paxos算法了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#7paxos算法了解过吗) + * [8.Zookeeper的Zab协议了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#8zookeeper的zab协议了解过吗) + * [9.知道什么是Gossip协议吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#9知道什么是gossip协议吗) + * [10.了解过哪些负载均衡算法?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#10了解过哪些负载均衡算法) + * [11.负载均衡的实现方案有哪些?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#11负载均衡的实现方案有哪些) + * [12.正向代理和反向代理的区别](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#12正向代理和反向代理的区别) + * [13.分布式 Session了解过吗?如何实现?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#13分布式-session了解过吗如何实现) + * [14.如何防止表单重复提交?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#14如何防止表单重复提交) + * [15.如何设计一个秒杀系统?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#15如何设计一个秒杀系统) + * [16.分布式系统的接口幂等性设计](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#16分布式系统的接口幂等性设计) + * [17.如何保障请求执行顺序](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#17如何保障请求执行顺序) + * [18.BASE理论了解过吗?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#18base理论了解过吗) + * [19.SOA和微服务架构有哪些区别?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#19soa和微服务架构有哪些区别) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/%E5%88%86%E5%B8%83%E5%BC%8F.md#参考资料) + + +* [Linux](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md) + * [1.vim有几种工作模式?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#1vim有几种工作模式) + * [2.find 命令如何使用?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#2find-命令如何使用) + * [3.如何在 /usr 目录下找出大小超过 10MB 的文件?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#3如何在-usr-目录下找出大小超过-10mb-的文件) + * [4.如何在 /var 目录下找出 90 天之内未被访问过的文件?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#4如何在-var-目录下找出-90-天之内未被访问过的文件) + * [5.如何在 /home 目录下找出 120 天之前被修改过的文件?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#5如何在-home-目录下找出-120-天之前被修改过的文件) + * [6.在整个目录树下查找文件 “core” ,如发现则无需提示直接删除它们?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#6在整个目录树下查找文件-core-如发现则无需提示直接删除它们) + * [7.ls 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#7ls-命令) + * [8.df 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#8df-命令) + * [9.rm 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#9rm-命令) + * [10.mv 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#10mv-命令) + * [11.cp 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#11cp-命令) + * [12.tail 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#12tail-命令) + * [13.grep 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#13grep-命令) + * [14.sed 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#14sed-命令) + * [15.用 sed 命令将指定的路径 /usr/local/http 替换成为 /usr/src/local/http ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#15用-sed-命令将指定的路径-usrlocalhttp-替换成为-usrsrclocalhttp-) + * [16.打印 /etc/ssh/sshd_config 的第一百行?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#16打印-etcsshsshd_config-的第一百行) + * [17.awk 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#17awk-命令) + * [18.打印 /etc/passwd 的 1 到 3 行?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#18打印-etcpasswd-的-1-到-3-行) + * [19.vim 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#19vim-命令) + * [20.diff 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#20diff-命令) + * [21.sort 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#21sort-命令) + * [22.xargs 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#22xargs-命令) + * [23.把当前目录下所有后缀名为 .txt 的文件的权限修改为 777 ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#23把当前目录下所有后缀名为-txt-的文件的权限修改为-777-) + * [24.tar 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#24tar-命令) + * [25.gzip 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#25gzip-命令) + * [26.bzip2 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#26bzip2-命令) + * [27.unzip 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#27unzip-命令) + * [28.export 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#28export-命令) + * [29.yum 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#29yum-命令) + * [30.rpm 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#30rpm-命令) + * [31.shutdown 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#31shutdown-命令) + * [32.service 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#32service-命令) + * [33.whereis 命令](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#33whereis-命令) + * [34.用一条命令显示本机 eth0 网卡的 IP 地址,不显示其它字符?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#34用一条命令显示本机-eth0-网卡的-ip-地址不显示其它字符) + * [35.如何禁止服务器被 ping ?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#35如何禁止服务器被-ping-) + * [36.设置 DNS 需要修改哪个配置文件?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#36设置-dns-需要修改哪个配置文件) + * [37.在 Linux 下如何指定dns服务器,来解析某个域名?](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#37在-linux-下如何指定dns服务器来解析某个域名) + * [参考资料](https://github.com/JavaInterviewHub/JavaInterview/blob/main/Linux.md#参考资料) + diff --git a/RabbitMQ.md b/RabbitMQ.md new file mode 100644 index 0000000..85c68b7 --- /dev/null +++ b/RabbitMQ.md @@ -0,0 +1,280 @@ +## RabbitMQ + + +* [1.什么是RabbitMQ?为什么使用RabbitMQ?](#1什么是rabbitmq为什么使用rabbitmq) +* [2.RabbitMQ有什么优缺点?](#2rabbitmq有什么优缺点) +* [3.什么是元数据?元数据分为哪些类型?包括哪些内容?与cluster相关的元数据有哪些?元数据是如何保存的?元数据在cluster中是如何分布的?](#3什么是元数据元数据分为哪些类型包括哪些内容与cluster相关的元数据有哪些元数据是如何保存的元数据在cluster中是如何分布的) +* [4.在单node系统和多node构成的cluster系统中声明queue、exchange,以及进行binding会有什么不同?](#4在单node系统和多node构成的cluster系统中声明queueexchange以及进行binding会有什么不同) +* [5.客户端连接到cluster中的任意node上是否都能正常工作?](#5客户端连接到cluster中的任意node上是否都能正常工作) +* [6.若cluster中拥有某个queue的owner node失效了,且该queue 被声明具有durable属性,是否能够成功从其他node上重新声明该 queue ?](#6若cluster中拥有某个queue的owner-node失效了且该queue-被声明具有durable属性是否能够成功从其他node上重新声明该-queue-) +* [7.RabbitMQ 的消息是怎么发送的?](#7rabbitmq-的消息是怎么发送的) +* [8.RabbitMQ 怎么避免消息丢失?](#8rabbitmq-怎么避免消息丢失) +* [9.RabbitMQ的使用场景有哪些?](#9rabbitmq的使用场景有哪些) +* [10.RabbitMQ有哪些重要的角色?](#10rabbitmq有哪些重要的角色) +* [11.如何确保消息正确地发送至RabbitMQ?如何确保消息接收方消费了消息?](#11如何确保消息正确地发送至rabbitmq如何确保消息接收方消费了消息) +* [12.要保证消息持久化成功的条件有哪些?](#12要保证消息持久化成功的条件有哪些) +* [13.RabbitMQ 有几种广播类型?](#13rabbitmq-有几种广播类型) +* [14.vhost 是什么?起什么作用?](#14vhost-是什么起什么作用) +* [15.消息基于什么传输?](#15消息基于什么传输) +* [16.消息如何分发?](#16消息如何分发) +* [17.消息怎么路由?](#17消息怎么路由) +* [18.如何确保消息接收方消费了消息?](#18如何确保消息接收方消费了消息) +* [19.如何避免消息重复投递或重复消费?](#19如何避免消息重复投递或重复消费) +* [20.死信队列和延迟队列的使用](#20死信队列和延迟队列的使用) +* [参考链接:](#参考链接) + + + +#### 1.什么是RabbitMQ?为什么使用RabbitMQ? + +RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的,消息中间件; + +可以用它来:解耦、异步、削峰。 + +#### 2.RabbitMQ有什么优缺点? + +优点:解耦、异步、削峰; + +缺点:降低了系统的稳定性:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低; + +增加了系统的复杂性:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。 + +#### 3.什么是元数据?元数据分为哪些类型?包括哪些内容?与cluster相关的元数据有哪些?元数据是如何保存的?元数据在cluster中是如何分布的? + +在非cluster模式下,元数据主要分为Queue元数据(queue名字和属性 等)、Exchange元数据(exchange名字、类型和属性等)、Binding元数据 (存放路由关系的查找表)、Vhost元数据(vhost范围内针对前三者的名字空 间约束和安全属性设置)。 + +在cluster模式下,还包括cluster中node位置信息和node关系信息。元数据按照erlang node的类型确定是仅保存于RAM中,还是同时保存在RAM和disk上。元数据在cluster中是全node 分布的。 + +#### 4.在单node系统和多node构成的cluster系统中声明queue、exchange,以及进行binding会有什么不同? + +当你在单node上声明queue时,只要该node上相关元数据进行了变 更,你就会得到Queue.Declare-ok回应;而在cluster上声明queue,则要 求cluster上的全部node都要进行元数据成功更新,才会得到 Queue.Declare-ok回应。另外,若node类型为RAM node则变更的数据 仅保存在内存中,若类型为disk node则还要变更保存在磁盘上的数据。 + +#### 5.客户端连接到cluster中的任意node上是否都能正常工作? + +是的。客户端感觉不到有何不同。 + +#### 6.若cluster中拥有某个queue的owner node失效了,且该queue 被声明具有durable属性,是否能够成功从其他node上重新声明该 queue ? + +不能,在这种情况下,将得到404 NOT_FOUND错误。只能等queue所 属的node恢复后才能使用该queue。但若该queue本身不具有durable 属性,则可在其他node上重新声明。 + +#### 7.RabbitMQ 的消息是怎么发送的? + +首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。 + +#### 8.RabbitMQ 怎么避免消息丢失? + +①消息持久化; + +②ACK确认机制; + +③设置集群镜像模式; + +④消息补偿机制。 + +#### 9.RabbitMQ的使用场景有哪些? + +①跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。 + +②多个应用之间的耦合,由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送方和接收方同时在线。在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以作为集成的方法。 + +③应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。 + +④消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。 + +⑤应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。 + +⑥跨局域网,甚至跨城市的通讯(CDN行业),比如北京机房与广州机房的应用程序的通信。 + +#### 10.RabbitMQ有哪些重要的角色? + +RabbitMQ中重要的角色有:生产者、消费者和代理: + +①生产者:消息的创建者,负责创建和推送数据到消息服务器; + +②消费者:消息的接收方,用于处理数据和确认消息; + +③代理:就是RabbitMQ本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。 + +#### 11.如何确保消息正确地发送至RabbitMQ?如何确保消息接收方消费了消息? + +1、发送方确认模式 + +①将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。 + +②一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。 + +③如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条 nack(notacknowledged,未确认)消息。 + +④发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。 + +2、接收方确认机制 + +①消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。 + +②这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性。 + +3、下面罗列几种特殊情况 + +①如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重) + +②如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。 + +#### 12.要保证消息持久化成功的条件有哪些? + +①声明队列必须设置持久化durable设置为 true。 + +②消息推送投递模式必须设置持久化,deliveryMode设置为2(持久)。 + +③消息已经到达持久化交换器。 + +④消息已经到达持久化队列。 + +以上四个条件都满足才能保证消息持久化成功。 + +#### 13.RabbitMQ 有几种广播类型? + +三种广播模式: + +①fanout:所有bind到此exchange的queue都可以接收消息(纯广播,绑定到RabbitMQ的接受者都能收到消息); + +②direct:通过routingKey和exchange决定的那个唯一的queue可以接收消息; + +③topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息; + +#### 14.vhost 是什么?起什么作用? + +vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。 + +#### 15.消息基于什么传输? + +由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。 + +#### 16.消息如何分发? + +若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。 + +#### 17.消息怎么路由? + +从概念上来说,消息路由必须有三部分:交换器、路由、绑定。生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。 + +消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。 +通过队列路由键,可以把队列绑定到交换器上。 +消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。 +常用的交换器主要分为一下三种: + +direct:如果路由键完全匹配,消息就被投递到相应的队列 +fanout:如果交换器收到消息,将会广播到所有绑定的队列上 +topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符,比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的一系列的标识符组成。 + +#### 18.如何确保消息接收方消费了消息? + +接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。 + +下面罗列几种特殊情况: + +如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要根据bizId去重) +如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。 + +#### 19.如何避免消息重复投递或重复消费? + +在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;在消息消费时,要求消息体中必须要有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。 + +这个问题针对业务场景来答分以下几点: + +1.比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。 + +2.再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。 + +3.如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。 + +#### 20.死信队列和延迟队列的使用 + +死信消息: + +消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false +消息过期了 +队列达到最大的长度 +过期消息: + +在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息就成为了一个 死信 消息。 + +队列设置:在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒 + +单个消息设置:是设置消息属性的 expiration 参数的值,单位为 毫秒 + +延时队列:在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。 + +有了以上的基础知识,我们完成以下需求: + +需求:用户在系统中创建一个订单,如果超过时间用户没有进行支付,那么自动取消订单。 + +分析: + +1、上面这个情况,我们就适合使用延时队列来实现,那么延时队列如何创建 + +2、延时队列可以由 过期消息+死信队列 来时间 + +3、过期消息通过队列中设置 x-message-ttl 参数实现 + +4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明一个队列绑定x-dead-letter-exchange对应的交换器。 +```java +ConnectionFactory factory = new ConnectionFactory(); +factory.setHost("127.0.0.1"); +factory.setPort(AMQP.PROTOCOL.PORT); +factory.setUsername("guest"); +factory.setPassword("guest"); +Connection connection = factory.newConnection(); +Channel channel = connection.createChannel(); + +// 声明一个接收被删除的消息的交换机和队列 +String EXCHANGE_DEAD_NAME = "exchange.dead"; +String QUEUE_DEAD_NAME = "queue_dead"; +channel.exchangeDeclare(EXCHANGE_DEAD_NAME, BuiltinExchangeType.DIRECT); +channel.queueDeclare(QUEUE_DEAD_NAME, false, false, false, null); +channel.queueBind(QUEUE_DEAD_NAME, EXCHANGE_DEAD_NAME, "routingkey.dead"); + +String EXCHANGE_NAME = "exchange.fanout"; +String QUEUE_NAME = "queue_name"; +channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); +Map arguments = new HashMap(); +// 统一设置队列中的所有消息的过期时间 +arguments.put("x-message-ttl", 30000); +// 设置超过多少毫秒没有消费者来访问队列,就删除队列的时间 +arguments.put("x-expires", 20000); +// 设置队列的最新的N条消息,如果超过N条,前面的消息将从队列中移除掉 +arguments.put("x-max-length", 4); +// 设置队列的内容的最大空间,超过该阈值就删除之前的消息 +arguments.put("x-max-length-bytes", 1024); +// 将删除的消息推送到指定的交换机,一般x-dead-letter-exchange和x-dead-letter-routing-key需要同时设置 +arguments.put("x-dead-letter-exchange", "exchange.dead"); +// 将删除的消息推送到指定的交换机对应的路由键 +arguments.put("x-dead-letter-routing-key", "routingkey.dead"); +// 设置消息的优先级,优先级大的优先被消费 +arguments.put("x-max-priority", 10); +channel.queueDeclare(QUEUE_NAME, false, false, false, arguments); +channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); +String message = "Hello RabbitMQ: "; + +for(int i = 1; i <= 5; i++) { + // expiration: 设置单条消息的过期时间 + AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder() + .priority(i).expiration( i * 1000 + ""); + channel.basicPublish(EXCHANGE_NAME, "", properties.build(), (message + i).getBytes("UTF-8")); +} +channel.close(); +connection.close(); +``` + + +### 参考链接: + +https://blog.csdn.net/jerryDzan/article/details/89183625 + +https://www.cnblogs.com/woadmin/p/10537174.html + +https://blog.csdn.net/weixin_48272905/article/details/108988269 + +http://www.mianshigee.com/topic/1009ada/ + +http://blog.itpub.net/69902581/viewspace-2673724/ + diff --git a/Redis.md b/Redis.md new file mode 100644 index 0000000..0f915da --- /dev/null +++ b/Redis.md @@ -0,0 +1,292 @@ +## Redis + +* [1.什么是Redis?简述它的优缺点?](#1什么是redis简述它的优缺点) +* [2.Redis相比memcached有哪些优势?](#2redis相比memcached有哪些优势) +* [3.Redis有哪些数据结构?](#3redis有哪些数据结构) +* [4.Redis主要消耗什么物理资源?](#4redis主要消耗什么物理资源) +* [5.Redis的全称是什么?](#5redis的全称是什么) +* [6.一个字符串类型的值能存储最大容量是多少?](#6一个字符串类型的值能存储最大容量是多少) +* [7.Redis为什么那么快?](#7redis为什么那么快) +* [8.Redis如何实现分布式锁?](#8redis如何实现分布式锁) +* [9.Redis是单线程还是多线程?](#9redis是单线程还是多线程) +* [10.Redis 官方为什么不提供 Windows 版本?](#10redis-官方为什么不提供-windows-版本) +* [11.为什么 Redis 需要把所有数据放到内存中?](#11为什么-redis-需要把所有数据放到内存中) +* [12.Redis如何设置密码及验证密码?](#12redis如何设置密码及验证密码) +* [13.Redis集群如何选择数据库?](#13redis集群如何选择数据库) +* [14.缓存失效?缓存穿透?缓存雪崩?缓存并发?](#14缓存失效缓存穿透缓存雪崩缓存并发) +* [15.Redis中的热key怎么处理?](#15redis中的热key怎么处理) +* [16.Redis中的大key怎么处理?](#16redis中的大key怎么处理) +* [17.使用Redis统计网站的UV,应该怎么做?](#17使用redis统计网站的uv应该怎么做) +* [18.Redis事务机制了解过吗?](#18redis事务机制了解过吗) +* [19.Redis key的淘汰策略有哪些?](#19redis-key的淘汰策略有哪些) +* [20.Redis在什么情况下会触发key的回收?](#20redis在什么情况下会触发key的回收) +* [21.Redis的持久化了解过吗?](#21redis的持久化了解过吗) +* [22.Redis在集群种查找key的时候,是怎么定位到具体节点的?](#22redis在集群种查找key的时候是怎么定位到具体节点的) +* [23.Redis集群各个节点之间是怎么保持数据一致性的?](#23redis集群各个节点之间是怎么保持数据一致性的) +* [24.用Redis做延时队列,具体应该怎么实现?](#24用redis做延时队列具体应该怎么实现) +* [25.Redis String的内部编码有哪些?](#25redis-string的内部编码有哪些) +* [26.Redis 集群方案应该怎么做?都有哪些方案?](#26redis-集群方案应该怎么做都有哪些方案) +* [27.Redis 集群方案什么情况下会导致整个集群不可用?](#27redis-集群方案什么情况下会导致整个集群不可用) +* [28.MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?](#28mysql-里有-2000w-数据redis-中只存-20w-的数据如何保证-redis-中的数据都是热点数据) +* [29.Redis有哪些适合的场景?](#29redis有哪些适合的场景) +* [30.Redis和Redisson有什么关系?](#30redis和redisson有什么关系) +* [31.Redis中的管道有什么用?](#31redis中的管道有什么用) +* [32.Redis如何做内存优化?](#32redis如何做内存优化) +* [参考链接](#参考链接) + + +#### 1.什么是Redis?简述它的优缺点? + +Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。 + +因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 + +Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。 + +比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。 + +另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。 Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。 + +#### 2.Redis相比memcached有哪些优势? + +(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 + +(2) redis的速度比memcached快很多 + +(3) redis可以持久化其数据 + +#### 3.Redis有哪些数据结构? + +Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集合) 和 zset(有序集合)。这 5 种是 Redis 相关知识中最基础、最重要的部分。 + +#### 4.Redis主要消耗什么物理资源? + +内存。 + +#### 5.Redis的全称是什么? + +Remote Dictionary Server。 + +#### 6.一个字符串类型的值能存储最大容量是多少? + +512M + +#### 7.Redis为什么那么快? + +1、内存操作; +2、单线程,省去线程切换、锁竞争的开销; +3、非阻塞IO模型,epoll。 + +#### 8.Redis如何实现分布式锁? + +详见:https://www.cnblogs.com/wlwl/p/11651409.html + +#### 9.Redis是单线程还是多线程? + +Redis6.0采用多线程IO,不过命令的执行还是单线程的。 +Redis6.0之前,IO线程和执行线程都是单线程的。 + +#### 10.Redis 官方为什么不提供 Windows 版本? + +因为目前 Linux 版本已经相当稳定,而且用户量很大,无需开发 windows 版本,反而会带来兼容性等问题。 + +#### 11.为什么 Redis 需要把所有数据放到内存中? + +Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。 + +所以 redis 具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。 + +在内存越来越便宜的今天,redis 将会越来越受欢迎, 如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。 + +#### 12.Redis如何设置密码及验证密码? + +设置密码:config set requirepass 123456 + +授权密码:auth 123456 + +#### 13.Redis集群如何选择数据库? + +Redis集群目前无法做数据库选择,默认在0数据库。 + +#### 14.缓存失效?缓存穿透?缓存雪崩?缓存并发? + +1. 缓存失效 + 缓存失效指的是大量的缓存在同一时间失效,到时DB的瞬间压力飙升。造成这种现象的原因是,key的过期时间都设置成一样了。解决方案是,key的过期时间引入随机因素,比如5分钟+随机秒这种方式。 + +2. 缓存穿透 + 缓存穿透是指查询一条数据库和缓存都没有的一条数据,就会一直查询数据库,对数据库的访问压力就会增大,缓存穿透的解决方案,有以下2种: + 缓存空对象:代码维护较简单,但是效果不好。 + 布隆过滤器:代码维护复杂,效果很好。 + +3. 缓存雪崩 + 缓存雪崩 是指在某一个时间段,缓存集中过期失效。此刻无数的请求直接绕开缓存,直接请求数据库。 + 造成缓存雪崩的原因,有以下2种: + reids宕机。 + 大部分数据失效。 + +对于缓存雪崩的解决方案有以下2种: +搭建高可用的集群,防止单机的redis宕机。 +设置不同的过期时间,防止同意之间内大量的key失效。 + +4. 缓存并发 + 有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。 + 一般处理方案是在查DB的时候进行加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后再查缓存或者进入DB查询。 + +#### 15.Redis中的热key怎么处理? + +1、对热key进行分散处理。比如:在key上加上不同的前后缀,缓存多个key,使得各个key分散到不同的节点上。 +2、采用多级缓存。 + +#### 16.Redis中的大key怎么处理? + +大key指的是value特别大的key。比如很长的字符串,或者很大的set等等。 +大key会造成2个问题:1、数据倾斜,比如某些节点内存占用过高。2、当删除大key或者大key自动过期的时候,会造成QPS突降,因为Redis是单线程的缘故。 +处理方案:可以将一个大key进行分片处理,比如:将一个大set分成多个小的set。 + +#### 17.使用Redis统计网站的UV,应该怎么做? + +UV与PV不同,UV需要去重。一般有2种方案: +1、用BitMap。存的是用户的uid,计算UV的时候,做下bitcount就行了。 +2、用布隆过滤器。将每次访问的用户uid都放到布隆过滤器中。优点是省内存,缺点是无法得到精确的UV。但是对于不需要精确知道具体UV,只需要大概的数量级的场景,是个不错的选择。 + +#### 18.Redis事务机制了解过吗? + +Redis事务的概念: +Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。 +Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。   + +Redis事务没有隔离级别的概念: +批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。 +Redis不保证原子性: +Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。 + +Redis事务的三个阶段: +开始事务 +命令入队 +执行事务 + +Redis事务相关命令: +watch key1 key2 ... : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 ) +multi : 标记一个事务块的开始( queued ) +exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )  +discard : 取消事务,放弃事务块中的所有命令 +unwatch : 取消watch对所有key的监控 + +#### 19.Redis key的淘汰策略有哪些? + +8种:noeviction,volatile-lru,volatile-lfu,volatile-ttl,volatile-random,allkey-lru,allkeys-lfu,allkeys-random + +#### 20.Redis在什么情况下会触发key的回收? + +2种情况:1、定时(抽样)清理;2、执行命令时,判断内存是否超过maxmemory。 + +#### 21.Redis的持久化了解过吗? + +Redis持久化有RDB和AOF这2种方式。 +RDB:将数据库快照以二进制的方式保存到磁盘中。 +AOF:以协议文本方式,将所有对数据库进行过写入的命令和参数记录到AOF文件,从而记录数据库状态。 + +#### 22.Redis在集群种查找key的时候,是怎么定位到具体节点的? + +使用crc16算法对key进行hash +将hash值对16384取模,得到具体的槽位 +根据节点和槽位的映射信息(与集群建立连接后,客户端可以取得槽位映射信息),找到具体的节点地址 +去具体的节点找key +如果key不在这个节点上,则redis集群会返回moved指令,加上新的节点地址给客户端,同时,客户端会刷新本地的节点槽位映射关系 +如果槽位正在迁移中,那么redis集群会返回asking指令给客户端,这是临时纠正,客户端不会刷新本地的节点槽位映射关系 + +#### 23.Redis集群各个节点之间是怎么保持数据一致性的? + +主要考察点是Redis的Gossip协议。 +详见:https://mp.weixin.qq.com/s/dW0I29Sw86lU0qHpxyhdmw + +#### 24.用Redis做延时队列,具体应该怎么实现? + +可以使用Zset实现。member是任务描述,score是执行时间,然后用定时器定时去扫描,一旦有执行时间小于或等于当前时间的任务,就立即执行。 + +#### 25.Redis String的内部编码有哪些? + +int、embstr、raw +10000以下的整数会使用缓存里的int常量。 +长度小于等于44字节:embstr编码 +长度大于44字节:raw编码 + +#### 26.Redis 集群方案应该怎么做?都有哪些方案? + +- codis +- 目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在节点数量改变情况下,旧节点数据可恢复到新 hash 节点。 +- redis cluster3.0 自带的集群,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。 +- 在业务代码层实现,起几个毫无关联的 redis 实例,在代码层,对 key 进行 hash 计算,然后去对应的redis 实例操作数据。这种方式对 hash 层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。 +- Java 架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm 性能调优、Spring 源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx 等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代! + +#### 27.Redis 集群方案什么情况下会导致整个集群不可用? + +有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了,那么整个集群就会以为缺少5501-11000 这个范围的槽而不可用。 + +#### 28.MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据? + +redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。 + +其实面试除了考察 Redis,不少公司都很重视高并发高可用的技术,特别是一线互联网公司,分布式、 + +JVM、spring 源码分析、微服务等知识点已是面试的必考题。 + +#### 29.Redis有哪些适合的场景? + +(1)会话缓存(Session Cache) + +最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? + +幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。 + +(2)全页缓存(FPC) + +除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 + +再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 + +此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。 + +(3)队列 + +Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。 + +如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。 + +(4)排行榜/计数器 + +Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。 + +所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: + +当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: + +ZRANGE user_scores 0 10 WITHSCORES + +Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。 + +(5)发布/订阅 + +最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统! + +#### 30.Redis和Redisson有什么关系? + +Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。 + +#### 31.Redis中的管道有什么用? + +一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。 + +这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。 + +#### 32.Redis如何做内存优化? + +尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。 + +比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。 + +#### 参考链接 + +http://blog.itpub.net/31545684/viewspace-2213990/ + +https://blog.csdn.net/weixin_34081595/article/details/92420220 diff --git a/Spring.md b/Spring.md new file mode 100644 index 0000000..a276de4 --- /dev/null +++ b/Spring.md @@ -0,0 +1,303 @@ +## Spring + + +* [1.什么是spring?](#1什么是spring) +* [2.使用Spring框架的好处是什么?](#2使用spring框架的好处是什么) +* [3.Spring由哪些模块组成?](#3spring由哪些模块组成) +* [4.Spring是怎么解决循环依赖的?](#4spring是怎么解决循环依赖的) +* [5.Spring Boot手动装配有哪几种方式?](#5spring-boot手动装配有哪几种方式) +* [6.Spring Boot自动配置原理](#6spring-boot自动配置原理) +* [7.谈谈自己对于Spring IOC的理解](#7谈谈自己对于spring-ioc的理解) +* [8.谈谈自己对于Spring AOP的理解](#8谈谈自己对于spring-aop的理解) +* [9.Spring AOP和AspectJ AOP有什么区别?](#9spring-aop和aspectj-aop有什么区别) +* [10.Spring中的bean的作用域有哪些?](#10spring中的bean的作用域有哪些) +* [11.Spring中的单例bean的线程安全问题了解吗?](#11spring中的单例bean的线程安全问题了解吗) +* [12.Spring中的bean生命周期了解过吗?](#12spring中的bean生命周期了解过吗) +* [13.Spring MVC的工作原理了解嘛?](#13spring-mvc的工作原理了解嘛) +* [14.Spring框架中用到了哪些设计模式?](#14spring框架中用到了哪些设计模式) +* [15.@Component和@Bean的区别是什么?](#15component和bean的区别是什么) +* [16.将一个类声明为Spring的bean的注解有哪些?](#16将一个类声明为spring的bean的注解有哪些) +* [17.Spring事务管理的方式有几种?](#17spring事务管理的方式有几种) +* [18.Spring事务中的隔离级别有哪几种?](#18spring事务中的隔离级别有哪几种) +* [19.Spring事务中有哪几种事务传播行为?](#19spring事务中有哪几种事务传播行为) +* [20.Spring 事务底层原理](#20spring-事务底层原理) +* [21.BeanFactory和ApplicationContext有什么区别?](#21beanfactory和applicationcontext有什么区别) +* [22.Resource 是如何被查找、加载的?](#22resource-是如何被查找加载的) +* [23.解释自动装配的各种模式?](#23解释自动装配的各种模式) +* [24.有哪些不同类型的IOC(依赖注入)?](#24有哪些不同类型的ioc依赖注入) +* [25.Spring AOP 实现原理](#25spring-aop-实现原理) +* [26.ApplicationContext通常的实现是什么?](#26applicationcontext通常的实现是什么) +* [27. Bean 工厂和 Application contexts 有什么区别?](#27-bean-工厂和-application-contexts-有什么区别) +* [参考资料](#参考资料) + + + +#### 1.什么是spring? + +Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。 + +#### 2.使用Spring框架的好处是什么? + +- 轻量:Spring 是轻量的,基本的版本大约2MB。 +- 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。 +- 面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。 +- 容器:Spring 包含并管理应用中对象的生命周期和配置。 +- MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。 +- 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。 +- 异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常。 + +#### 3.Spring由哪些模块组成? + +以下是Spring 框架的基本模块: + +- Core module +- Bean module +- Context module +- Expression Language module +- JDBC module +- ORM module +- OXM module +- Java Messaging Service(JMS) module +- Transaction module +- Web module +- Web-Servlet module +- Web-Struts module +- Web-Portlet module + +#### 4.Spring是怎么解决循环依赖的? + +整个IOC容器解决循环依赖,用到的几个重要成员: +singletonObjects:一级缓存,存放完全初始化好的Bean的集合,从这个集合中取出来的Bean可以立马返回 +earlySingletonObjects:二级缓存,存放创建好但没有初始化属性的Bean的集合,它用来解决循环依赖 +singletonFactories:三级缓存,存放单实例Bean工厂的集合 +singletonsCurrentlyInCreation:存放正在被创建的Bean的集合 + +IOC容器解决循环依赖的思路: + +1. 初始化Bean之前,将这个BeanName放入三级缓存 +2. 创建Bean将准备创建的Bean放入 singletonsCurrentlyInCreation (正在创建的Bean) +3. createNewInstance 方法执行完后执行 addSingletonFactory,将这个实例化但没有属性赋值的Bean放入二级缓存,并从三级缓存中移除 +4. 属性赋值&自动注入时,引发关联创建 +5. 关联创建时,检查“正在被创建的Bean”中是否有即将注入的Bean。如果有,检查二级缓存中是否有当前创建好但没有赋值初始化的Bean。如果没有,检查三级缓存中是否有正在创建中的Bean。至此一般会有,将这个Bean放入二级缓存,并从三级缓存中移除 +6. 之后Bean被成功注入,最后执行 addSingleton,将这个完全创建好的Bean放入一级缓存,从二级缓存和三级缓存移除,并记录已经创建了的单实例Bean + +#### 5.Spring Boot手动装配有哪几种方式? + +1. 使用模式注解 @Component 等(Spring2.5+) +2. 使用配置类 @Configuration 与 @Bean (Spring3.0+) +3. 使用模块装配 @EnableXXX 与 @Import (Spring3.1+) + +其中使用 @Component 及衍生注解很常见,咱开发中常用的套路,不再赘述。 +但模式注解只能在自己编写的代码中标注,无法装配jar包中的组件。为此可以使用 @Configuration 与 @Bean,手动装配组件(如上一篇的 @Configuration 示例)。但这种方式一旦注册过多,会导致编码成本高,维护不灵活等问题。 +SpringFramework 提供了模块装配功能,通过给配置类标注 @EnableXXX 注解,再在注解上标注 @Import 注解,即可完成组件装配的效果。 + +#### 6.Spring Boot自动配置原理 + +详见:https://blog.csdn.net/Dongguabai/article/details/80865599 + + +#### 7.谈谈自己对于Spring IOC的理解 + +IOC(Inversion Of Controll,控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。 + +将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。 + +Spring时代我们一般通过XML文件来配置Bean,后来开发人员觉得用XML文件来配置不太好,于是Sprng Boot注解配置就慢慢开始流行起来。 + +#### 8.谈谈自己对于Spring AOP的理解 + +AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。 + +Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。 +当然也可以使用AspectJ,Spring AOP中已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量。我们需要增加新功能也方便,提高了系统的扩展性。日志功能、事务管理和权限管理等场景都用到了AOP。 + +#### 9.Spring AOP和AspectJ AOP有什么区别? + +Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。 + +Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。 + +如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。 + +#### 10.Spring中的bean的作用域有哪些? + +1.singleton:唯一bean实例,Spring中的bean默认都是单例的。 +2.prototype:每次请求都会创建一个新的bean实例。 +3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 +4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。 +5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。 + +#### 11.Spring中的单例bean的线程安全问题了解吗? + +大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 +有两种常见的解决方案: +1.在bean对象中尽量避免定义可变的成员变量(不太现实)。 +2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。 + +#### 12.Spring中的bean生命周期了解过吗? + +1.Bean容器找到配置文件中Spring Bean的定义。 +2.Bean容器利用Java Reflection API创建一个Bean的实例。 +3.如果涉及到一些属性值,利用set()方法设置一些属性值。 +4.如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。 +5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。 +6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例。 +7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 +8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法。 +9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。 +10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 +11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法。 +12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。 +13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。 + +#### 13.Spring MVC的工作原理了解嘛? + +1.客户端(浏览器)发送请求,直接请求到DispatcherServlet。 +2.DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。 +3.解析到对应的Handler(也就是我们平常说的Controller控制器)。 +4.HandlerAdapter会根据Handler来调用真正的处理器来处理请求和执行相对应的业务逻辑。 +5.处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。 +6.ViewResolver会根据逻辑View去查找实际的View。 +7.DispatcherServlet把返回的Model传给View(视图渲染)。 +8.把View返回给请求者(浏览器)。 + +#### 14.Spring框架中用到了哪些设计模式? + +1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。 +2.代理设计模式:Spring AOP功能的实现。 +3.单例设计模式:Spring中的bean默认都是单例的。 +4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。 +5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 +6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。 +7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。 + +#### 15.@Component和@Bean的区别是什么? + +1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。 +2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。 +3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。 + +#### 16.将一个类声明为Spring的bean的注解有哪些? + +我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现: +1.@Component注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。 +2.@Repository注解。对应持久层,即Dao层,主要用于数据库相关操作。 +3.@Service注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。 +4.@Controller注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。 + +#### 17.Spring事务管理的方式有几种? + +1.编程式事务:在代码中硬编码(不推荐使用)。 +2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。 + +#### 18.Spring事务中的隔离级别有哪几种? + +在TransactionDefinition接口中定义了五个表示隔离级别的常量: + +ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。 + +ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。 + +ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 + +ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。 + +ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 + +#### 19.Spring事务中有哪几种事务传播行为? + +在TransactionDefinition接口中定义了八个表示事务传播行为的常量。 + +支持当前事务的情况: +PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 +PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 +PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。 + +不支持当前事务的情况: +PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。 +PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 +PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。 + +其他情况: +PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。 + +#### 20.Spring 事务底层原理 + +划分处理单元——IoC +由于spring解决的问题是对单个数据库进行局部事务处理的,具体的实现首先用spring中的IoC划分了事务处理单元。并且将对事务的各种配置放到了ioc容器中(设置事务管理器,设置事务的传播特性及隔离机制)。 + +AOP拦截需要进行事务处理的类 +Spring事务处理模块是通过AOP功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用TransactionProxyFactoryBean接口来使用AOP功能,生成proxy代理对象,通过TransactionInterceptor完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取ioc容器事务配置属性,转化为spring事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为TransactionAttribute表示的数据对象。 + +对事务处理实现(事务的生成、提交、回滚、挂起) +spring委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSource数据源支持、hibernate数据源事务处理支持、JDO数据源事务处理支持,JPA、JTA数据源事务处理支持。这些支持都是通过设计PlatformTransactionManager、AbstractPlatforTransaction一系列事务处理的支持。 为常用数据源支持提供了一系列的TransactionManager。 + +总结 +PlatformTransactionManager实现了TransactionInterception接口,让其与TransactionProxyFactoryBean结合起来,形成一个Spring声明式事务处理的设计体系。 + +#### 21.BeanFactory和ApplicationContext有什么区别? + +ApplicationContext提供了一种解决文档信息的方法,一种加载文件资源的方式(如图片),他们可以向监听他们的beans发送消息。另外,容器或者容器中beans的操作,这些必须以bean工厂的编程方式处理的操作可以在应用上下文中以声明的方式处理。应用上下文实现了MessageSource,该接口用于获取本地消息,实际的实现是可选的。 + +相同点:两者都是通过xml配置文件加载bean,ApplicationContext和BeanFacotry相比,提供了更多的扩展功能。 + +不同点:BeanFactory是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。 + + +#### 22.Resource 是如何被查找、加载的? + +Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。 Spring 为 Resource 接口提供了如下实现类: + +UrlResource:访问网络资源的实现类。ClassPathResource:访问类加载路径里资源的实现类。FileSystemResource:访问文件系统里资源的实现类。ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:InputStreamResource:访问输入流资源的实现类。ByteArrayResource:访问字节数组资源的实现类。 这些 Resource 实现类,针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。 + +#### 23.解释自动装配的各种模式? + +自动装配提供五种不同的模式供Spring容器用来自动装配beans之间的依赖注入: + +no:默认的方式是不进行自动装配,通过手工设置ref 属性来进行装配bean。 + +byName:通过参数名自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byName。之后容器试图匹配、装配和该bean的属性具有相同名字的bean。 + +byType:通过参数的数据类型自动自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byType。之后容器试图匹配和装配和该bean的属性类型一样的bean。如果有多个bean符合条件,则抛出错误。 + +constructor:这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则抛出一个严重的错误。 + +autodetect:如果有默认的构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。 + +#### 24.有哪些不同类型的IOC(依赖注入)? + +构造器依赖注入:构造器依赖注入在容器触发构造器的时候完成,该构造器有一系列的参数,每个参数代表注入的对象。 + +Setter方法依赖注入:首先容器会触发一个无参构造函数或无参静态工厂方法实例化对象,之后容器调用bean中的setter方法完成Setter方法依赖注入。 + +#### 25.Spring AOP 实现原理 + +实现AOP的技术,主要分为两大类: + +一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。 +Spring AOP 的实现原理其实很简单:AOP 框架负责动态地生成 AOP 代理类,这个代理类的方法则由 Advice和回调目标对象的方法所组成, 并将该对象可作为目标对象使用。AOP 代理包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异,AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。 + +Spring AOP使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于JDK的动态代理(JDK本身只提供接口的代理)和基于CGlib的动态代理。 + +(1) JDK的动态代理 +JDK的动态代理主要涉及java.lang.reflect包中的两个类:Proxy和InvocationHandler。其中InvocationHandler只是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑与业务逻辑织在一起。而Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。 +其代理对象必须是某个接口的实现, 它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理.只能实现接口的类生成代理,而不能针对类。 +(2)CGLib +CGLib采用底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类的调用方法,并顺势织入横切逻辑.它运行期间生成的代理对象是目标类的扩展子类.所以无法通知final、private的方法,因为它们不能被覆写.是针对类实现代理,主要是为指定的类生成一个子类,覆盖其中方法。 +在spring中默认。况下使用JDK动态代理实现AOP,如果proxy-target-class设置为true或者使用了优化策略那么会使用CGLIB来创建动态代理.Spring AOP在这两种方式的实现上基本一样.以JDK代理为例,会使用JdkDynamicAopProxy来创建代理,在invoke()方法首先需要织入到当前类的增强器封装到拦截器链中,然后递归的调用这些拦截器完成功能的织入,最终返回代理对象。 + +#### 26.ApplicationContext通常的实现是什么? + +- FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。 +- ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。 +- WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。 + +#### 27. Bean 工厂和 Application contexts 有什么区别? + +Application contexts提供一种方法处理文本消息,一个通常的做法是加载文件资源(比如镜像),它们可以向注册为监听器的bean发布事件。另外,在容器或容器内的对象上执行的那些不得不由bean工厂以程序化方式处理的操作,可以在Application contexts中以声明的方式处理。Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。 + + +### 参考资料 +https://www.cnblogs.com/yanggb/p/11004887.html +https://www.jianshu.com/p/a5d960c6f6dd +https://ifeve.com/spring-interview-questions-and-answers/ diff --git a/SpringBoot.md b/SpringBoot.md new file mode 100644 index 0000000..28cd654 --- /dev/null +++ b/SpringBoot.md @@ -0,0 +1,373 @@ +## Spring Boot + + +* [1.什么是springboot](#1什么是springboot) +* [2.Spring Boot 有哪些优点?](#2spring-boot-有哪些优点) +* [3. 创建一个 Spring Boot Project 的最简单的方法是什么?](#3-创建一个-spring-boot-project-的最简单的方法是什么) +* [4.Spring 和 SpringBoot 有什么不同?](#4spring-和-springboot-有什么不同) +* [5.如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?](#5如何重新加载-spring-boot-上的更改而无需重新启动服务器) +* [6.Spring Boot 中的监视器是什么?](#6spring-boot-中的监视器是什么) +* [7.如何在 Spring Boot 中禁用 Actuator 端点安全性?](#7如何在-spring-boot-中禁用-actuator-端点安全性) +* [8.怎么使用 Maven 来构建一个 SpringBoot 程序?](#8怎么使用-maven-来构建一个-springboot-程序) +* [9.Spring Initializr 是创建 Spring Boot Projects 的唯一方法吗?](#9spring-initializr-是创建-spring-boot-projects-的唯一方法吗) +* [10.为什么我们需要 spring-boot-maven-plugin?](#10为什么我们需要-spring-boot-maven-plugin) +* [11.什么是嵌入式服务器?我们为什么要使用嵌入式服务器呢?](#11什么是嵌入式服务器我们为什么要使用嵌入式服务器呢) +* [12.如何在 Spring Boot 中添加通用的 JS 代码?](#12如何在-spring-boot-中添加通用的-js-代码) +* [13.如何使用 Spring Boot 部署到不同的服务器?](#13如何使用-spring-boot-部署到不同的服务器) +* [14.如何使用配置文件通过 Spring Boot 配置特定环境的配置?](#14如何使用配置文件通过-spring-boot-配置特定环境的配置) +* [15.什么是Swagger?你用Spring Boot实现了吗?](#15什么是swagger你用spring-boot实现了吗) +* [16.如何实现Spring Boot应用程序的安全性?](#16如何实现spring-boot应用程序的安全性) +* [17.比较一下Spring Security和Shiro各自的优缺点?](#17比较一下spring-security和shiro各自的优缺点) +* [18.Spring Boot中如何解决跨域问题?](#18spring-boot中如何解决跨域问题) +* [19.Spring Boot的核心注解是哪些?他由哪几个注解组成的?](#19spring-boot的核心注解是哪些他由哪几个注解组成的) +* [20.保护SpringBoot应用有哪些方法?](#20保护springboot应用有哪些方法) +* [21.SpringBoot 2.X有哪些新特性?与1.X有什么区别?](#21springboot-2x有哪些新特性与1x有什么区别) +* [参考链接](#参考链接) + + +#### 1.什么是springboot + +用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件) +创建独立的spring引用程序 main方法运行 +嵌入的Tomcat 无需部署war文件 +简化maven配置 +自动配置spring添加对应功能starter自动化配置 + +spring boot来简化spring应用开发,约定大于配置,去繁从简,just run就能创建一个独立的,产品级别的应用 + +#### 2.Spring Boot 有哪些优点? + +Spring Boot 的优点有: + +1、减少开发,测试时间和努力。 + +2、使用 JavaConfig 有助于避免使用 XML。 + +3、避免大量的 Maven 导入和各种版本冲突。 + +4、提供意见发展方法。 + +5、通过提供默认值快速开始开发。 + +6、没有单独的 Web 服务器需要。这意味着你不再需要启动 Tomcat,Glassfish或其他任何东西。 + +7、需要更少的配置 因为没有 web.xml 文件。只需添加用@ Configuration 注释的类,然后添加用@Bean 注释的方法,Spring 将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired 添加到 bean 方法中,以使 Spring 自动装入需要的依赖关系中。 + +8、基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring 将在(application{environment} .properties)中加载后续的应用程序属性文件。 + +#### 3. 创建一个 Spring Boot Project 的最简单的方法是什么? + +Spring Initializr是启动 Spring Boot Projects 的一个很好的工具。 + +![img](https://mmbiz.qpic.cn/mmbiz_png/z1ViaEyjXTiasVydib8iavtBEG3AxK2Z11pdsWll8fxLf3DloRzpRt6eh38xbSSycexCKsYJxB6bzgk4qMAYAp6xZg/640) + +- 就像上图中所展示的一样,我们需要做一下几步: +- 登录 Spring Initializr,按照以下方式进行选择: +- 选择 com.in28minutes.springboot 为组 +- 选择 studet-services 为组件 +- 选择下面的依赖项 +- Web +- Actuator +- DevTools +- 点击生 GenerateProject +- 将项目导入 Eclipse。文件 - 导入 - 现有的 Maven 项目 + +#### 4.Spring 和 SpringBoot 有什么不同? + +Spring 框架提供多种特性使得 web 应用开发变得更简便,包括依赖注入、数据绑定、切面编程、数据存取等等。 + +随着时间推移,Spring 生态变得越来越复杂了,并且应用程序所必须的配置文件也令人觉得可怕。这就是 Spirng Boot 派上用场的地方了 – 它使得 Spring 的配置变得更轻而易举。 + +实际上,Spring 是 *unopinionated*(予以配置项多,倾向性弱) 的,Spring Boot 在平台和库的做法中更 *opinionated* ,使得我们更容易上手。 + +这里有两条 SpringBoot 带来的好处: + +- 根据 classpath 中的 artifacts 的自动化配置应用程序 +- 提供非功能性特性例如安全和健康检查给到生产环境中的应用程序 + +#### 5.如何重新加载 Spring Boot 上的更改,而无需重新启动服务器? + +这可以使用 DEV 工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式tomcat 将重新启动。Spring Boot 有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java 开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。开发人员可以重新加载 Spring Boot 上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot 在发布它的第一个版本时没有这个功能。这是开发人员最需要的功能。DevTools 模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供 H2 数据库控制台以更好地测试应用程序。 + +```xml + +org.springframework.boot +spring-boot-devtools +true + +``` + +#### 6.Spring Boot 中的监视器是什么? + +Spring boot actuator 是 spring 启动框架中的重要功能之一。Spring boot 监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为 HTTP URL 访问的REST 端点来检查状态。 + +#### 7.如何在 Spring Boot 中禁用 Actuator 端点安全性? + +默认情况下,所有敏感的 HTTP 端点都是安全的,只有具有 ACTUATOR 角色的用户才能访问它们。安全性是使用标准的 HttpServletRequest.isUserInRole 方法实施的。 我们可以使用来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。 + +#### 8.怎么使用 Maven 来构建一个 SpringBoot 程序? + +就像引入其他库一样,我们可以在 Maven 工程中加入 SpringBoot 依赖。然而,最好是从 *spring-boot-starter-parent* 项目中继承以及声明依赖到 Spring Boot starters。这样做可以使我们的项目可以重用 SpringBoot 的默认配置。 + +继承 *spring-boot-starter-parent* 项目依赖很简单 – 我们只需要在 *pom.xml* 中定义一个 *parent* 节点: + +``` + + org.springframework.boot + spring-boot-starter-parent + 2.1.1.RELEASE + +``` + +我们可以在 Maven central 中找到 *spring-boot-starter-parent* 的最新版本。 + +使用 starter 父项目依赖很方便,但并非总是可行。例如,如果我们公司都要求项目继承标准 POM,我们就不能依赖 SpringBoot starter 了。 + +这种情况,我们可以通过对 POM 元素的依赖管理来处理: + +```xml + + + + org.springframework.boot + spring-boot-dependencies + 2.1.1.RELEASE + pom + import + + + +``` + + + +#### 9.Spring Initializr 是创建 Spring Boot Projects 的唯一方法吗? + +不是的。 + +Spring Initiatlizr 让创建 Spring Boot 项目变的很容易,但是,你也可以通过设置一个 maven 项目并添加正确的依赖项来开始一个项目。 + +在我们的 Spring 课程中,我们使用两种方法来创建项目。 + +第一种方法是 start.spring.io 。 + +另外一种方法是在项目的标题为“Basic Web Application”处进行手动设置。 + +手动设置一个 maven 项目 + +这里有几个重要的步骤: + +- 在 Eclipse 中,使用文件 - 新建 Maven 项目来创建一个新项目 +- 添加依赖项。 +- 添加 maven 插件。 +- 添加 Spring Boot 应用程序类。 + +到这里,准备工作已经做好! + +#### 10.为什么我们需要 spring-boot-maven-plugin? + +spring-boot-maven-plugin 提供了一些像 jar 一样打包或者运行应用程序的命令。 + +- spring-boot:run 运行你的 SpringBooty 应用程序。 +- spring-boot:repackage 重新打包你的 jar 包或者是 war 包使其可执行 +- spring-boot:start 和 spring-boot:stop 管理 Spring Boot 应用程序的生命周期(也可以说是为了集成测试)。 +- spring-boot:build-info 生成执行器可以使用的构造信息。 + +#### 11.什么是嵌入式服务器?我们为什么要使用嵌入式服务器呢? + +思考一下在你的虚拟机上部署应用程序需要些什么。 + +第一步: 安装 Java + +第二部: 安装 Web 或者是应用程序的服务器(Tomat/Wbesphere/Weblogic 等等) + +第三部: 部署应用程序 war 包 + +如果我们想简化这些步骤,应该如何做呢? + +让我们来思考如何使服务器成为应用程序的一部分? + +你只需要一个安装了 Java 的虚拟机,就可以直接在上面部署应用程序了, + +是不是很爽? + +这个想法是嵌入式服务器的起源。 + +当我们创建一个可以部署的应用程序的时候,我们将会把服务器(例如,tomcat)嵌入到可部署的服务器中。 + +例如,对于一个 Spring Boot 应用程序来说,你可以生成一个包含 Embedded Tomcat 的应用程序 jar。你就可以想运行正常 Java 应用程序一样来运行 web 应用程序了。 + +嵌入式服务器就是我们的可执行单元包含服务器的二进制文件(例如,tomcat.jar)。 + +#### 12.如何在 Spring Boot 中添加通用的 JS 代码? + +在源文件夹下,创建一个名为 static 的文件夹。然后,你可以把你的静态的内容放在这里面。 + +例如,myapp.js 的路径是 resources\static\js\myapp.js + +你可以参考它在 jsp 中的使用方法: + +![35道SpringBoot面试题及答案,面试常被问到!](https://img1.3s78.com/codercto/34a11ffc02e104dcfdaa76884a206650) + +错误:HAL browser gives me unauthorized error - Full authenticaition is required to access this resource. + +该如何来修复这个错误呢? + +![35道SpringBoot面试题及答案,面试常被问到!](https://img1.3s78.com/codercto/39f610d8c91c1b667bf9280a91a38e21) + +两种方法: + +方法 1:关闭安全验证 + +application.properties + +``` +management.security.enabled:FALSE +``` + +方法二:在日志中搜索密码并传递至请求标头中 + +#### 13.如何使用 Spring Boot 部署到不同的服务器? + +你需要做下面两个步骤: + +- 在一个项目中生成一个 war 文件。 +- 将它部署到你最喜欢的服务器(websphere 或者 Weblogic 或者 Tomcat and so on)。 + +第一步:这本入门指南应该有所帮助: + +https://spring.io/guides/gs/convert-jar-to-war/ + +第二步:取决于你的服务器。 + +#### 14.如何使用配置文件通过 Spring Boot 配置特定环境的配置? + +配置文件不是设别环境的关键。 + +在下面的例子中,我们将会用到两个配置文件 + +- dev +- prod + +缺省的应用程序配置在 application.properties 中。让我们来看下面的例子: + +application.properties + +``` +basic.value= true + basic.message= Dynamic Message + basic.number= 100 +``` + +我们想要为 dev 文件自定义 application.properties 属性。我们需要创建一个名为 application-dev.properties 的文件,并且重写我们想要自定义的属性。 + +application-dev.properties + +``` +basic.message: Dynamic Message in DEV +``` + +一旦你特定配置了配置文件,你需要在环境中设定一个活动的配置文件。 + +有多种方法可以做到这一点: + +- 在 VM 参数中使用 Dspring.profiles.active=prod +- 在 application.properties 中使用 spring.profiles.active=prod + +#### 15.什么是Swagger?你用Spring Boot实现了吗? + +Swagger 广泛用于可视化 API,使用 Swagger UI 为前端开发人员提供在线沙箱。Swagger 是用于生成 RESTful Web 服务的可视化表示的工具,规范和完整框架实现。它使文档能够以与服务器相同的速度更新。当通过 Swagger 正确定义时,消费者可以使用最少量的实现逻辑来理解远程服务并与其进行交互。因此,Swagger消除了调用服务时的猜测。 + +#### 16.如何实现Spring Boot应用程序的安全性? + +为了实现Spring Boot的安全性,使用spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少代码。配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。 + +#### 17.比较一下Spring Security和Shiro各自的优缺点? + +由于Spring Boot官方提供了大量的非常方便的开箱即用的Starter,包括Spring Security的Starter,使得在SpringBoot中使用Spring Security变得更加容易,甚至只需要添加一个一来就可以保护所有接口,所以如果是SpringBoot项目,一般选择Spring Security。当然这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。 + +Shiro和Spring Security相比,主要有如下特点: + +Spring Security是一个重量级的安全管理框架;Shiro则是一个轻量级的安全管理框架; +Spring Security概念复杂,配置繁琐;Shiro概念简单、配置简单; +Spring Security功能强大;Shiro功能简单 + +#### 18.Spring Boot中如何解决跨域问题? + +跨域可以在前端通过JSONP来解决,但是JSONP只可以发送GET请求,无法发送其他类型的请求,在RESTful风格的应用中,就显得非常鸡肋,因此推荐在后端通过(CORS,Cross-origin resource sharing)来解决跨域问题。这种解决方案并非Spring Boot特有的,在传统的SSM框架中,就可以通过CORS来解决跨域问题,只不过之前我们是在XML文件中配置CORS,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。 + +```java +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowCredentials(true) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .maxAge(3600); + } + +} + + +``` + +项目中前后端分离部署,所以需要解决跨域的问题。 +我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。 +当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。 +我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。 + +```java +@Configuration +public class CorsConfig { + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.addAllowedOrigin("*"); + corsConfiguration.addAllowedHeader("*"); + corsConfiguration.addAllowedMethod("*"); + corsConfiguration.setAllowCredentials(true); + UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); + urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); + return new CorsFilter(urlBasedCorsConfigurationSource); + } + +} + +``` + +#### 19.Spring Boot的核心注解是哪些?他由哪几个注解组成的? + +启动类上面的注解是@SpringBootApplication,他也是SpringBoot的核心注解,主要组合包含了以下3个注解: + +@SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能; +@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置的功能:@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}); +@ComponentScan:Spring组件扫描。 + +#### 20.保护SpringBoot应用有哪些方法? + +- 在生产中使用HTTPS +- 使用Snyk检查你的依赖关系 +- 升级到最新版本 +- 启用CSRF保护 +- 使用内容安全策略防止XSS攻击 + +#### 21.SpringBoot 2.X有哪些新特性?与1.X有什么区别? + +- 配置变更 +- JDK版本升级 +- 第三方类库升级 +- 响应式Spring编程支持 +- HTTP/2支持 +- 配置属性绑定 +- 更多改进与加强 + +#### 参考链接 + +https://blog.csdn.net/yuzongtao/article/details/84295732 +https://www.cnblogs.com/aishangJava/p/10546741.html +https://www.codercto.com/a/77896.html +https://blog.csdn.net/u012068483/article/details/105039330/ diff --git a/SpringCloud.md b/SpringCloud.md new file mode 100644 index 0000000..a45f05d --- /dev/null +++ b/SpringCloud.md @@ -0,0 +1,230 @@ +## Spring Cloud + + +* [1.什么是 Spring Cloud?](#1什么是-spring-cloud) +* [2.使用Spring Cloud有什么优势?](#2使用spring-cloud有什么优势) +* [3.服务注册和发现是什么意思?Spring Cloud如何实现?](#3服务注册和发现是什么意思spring-cloud如何实现) +* [4.Spring Cloud由哪些组件组成?](#4spring-cloud由哪些组件组成) +* [5.什么是Hystrix?它如何实现容错?](#5什么是hystrix它如何实现容错) +* [6.什么是Hystrix断路器?我们需要它吗?](#6什么是hystrix断路器我们需要它吗) +* [7.什么是Netflix Feign?它的优点是什么?](#7什么是netflix-feign它的优点是什么) +* [8.Eureka的工作原理?](#8eureka的工作原理) +* [9.说说Eureka的自我保护机制?](#9说说eureka的自我保护机制) +* [10.什么是zuul?](#10什么是zuul) +* [11.zuul的工作流程?](#11zuul的工作流程) +* [12.什么是服务熔断?什么是服务降级?](#12什么是服务熔断什么是服务降级) +* [13.什么是服务雪崩效应?](#13什么是服务雪崩效应) +* [14.ZuulFilter有哪些常用方法?](#14zuulfilter有哪些常用方法) +* [15.如何实现动态Zuul网关路由转发?](#15如何实现动态zuul网关路由转发) +* [16.什么是 Spring Cloud Bus?](#16什么是-spring-cloud-bus) +* [17.Spring Cloud Bus 原理?](#17spring-cloud-bus-原理) +* [18.SpringCloud Config可以实现实时刷新吗?](#18springcloud-config可以实现实时刷新吗) +* [19.Eureka和zookeeper都可以提供服务注册与发现的功能,两者的区别](#19eureka和zookeeper都可以提供服务注册与发现的功能两者的区别) +* [参考链接](#参考链接) + + +#### 1.什么是 Spring Cloud? + +spring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。 + +#### 2.使用Spring Cloud有什么优势? + +使用Spring Boot开发分布式微服务时,我们面临以下问题 + +- 与分布式系统相关的复杂性-这种开销包括网络问题,延迟开销,带宽问题,安全问题。 +- 服务发现-服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。 +- 冗余-分布式系统中的冗余问题。 +- 负载平衡 --负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。 +- 性能-问题 由于各种运营开销导致的性能问题。 +- 部署复杂性-Devops技能的要求。 + +#### 3.服务注册和发现是什么意思?Spring Cloud如何实现? + +当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka服务注册和发现可以在这种情况下提供帮助。由于所有服务都在Eureka服务器上注册并通过调用Eureka服务器完成查找,因此无需处理服务地点的任何更改和处理。 + +#### 4.Spring Cloud由哪些组件组成? + +`Eureka`:服务注册与发现 + +`Zuul`:服务网关 + +`Ribbon`:客户端负载均衡 + +`Feign`:声明性的Web服务客户端 + +`Hystrix`:断路器 + +`Config`:分布式统一配置管理 + +等20几个框架,开源一直在更新 + +![0e9c7b73ca58fcbd7ee8f5b7c3fe450.png](http://img1.sycdn.imooc.com/5f309a610001d87510410539.jpg) + + +#### 5.什么是Hystrix?它如何实现容错? + +Hystrix是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。 + +通常对于使用微服务架构开发的系统,涉及到许多微服务。这些微服务彼此协作。 + +思考以下微服务 + +![img](https://img-blog.csdn.net/20180922211907901?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21vYWt1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) + +假设如果上图中的微服务9失败了,那么使用传统方法我们将传播一个异常。但这仍然会导致整个系统崩溃。 + +随着微服务数量的增加,这个问题变得更加复杂。微服务的数量可以高达1000.这是hystrix出现的地方 我们将使用Hystrix在这种情况下的Fallback方法功能。我们有两个服务employee-consumer使用由employee-consumer公开的服务。 + +简化图如下所示 + +![img](https://img-blog.csdn.net/20180922211919539?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21vYWt1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) + +现在假设由于某种原因,employee-producer公开的服务会抛出异常。我们在这种情况下使用Hystrix定义了一个回退方法。这种后备方法应该具有与公开服务相同的返回类型。如果暴露服务中出现异常,则回退方法将返回一些值。 + +#### 6.什么是Hystrix断路器?我们需要它吗? + +由于某些原因,employee-consumer公开服务会引发异常。在这种情况下使用Hystrix我们定义了一个回退方法。如果在公开服务中发生异常,则回退方法返回一些默认值。 + +![img](https://img-blog.csdn.net/20180922211937187?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21vYWt1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) + +如果firstPage method() 中的异常继续发生,则Hystrix电路将中断,并且员工使用者将一起跳过firtsPage方法,并直接调用回退方法。 断路器的目的是给第一页方法或第一页方法可能调用的其他方法留出时间,并导致异常恢复。可能发生的情况是,在负载较小的情况下,导致异常的问题有更好的恢复机会 。 + +![img](https://img-blog.csdn.net/20180922211948793?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21vYWt1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) + +#### 7.什么是Netflix Feign?它的优点是什么? + +Feign是受到Retrofit,JAXRS-2.0和WebSocket启发的java客户端联编程序。Feign的第一个目标是将约束分母的复杂性统一到http apis,而不考虑其稳定性。在employee-consumer的例子中,我们使用了employee-producer使用REST模板公开的REST服务。 + +但是我们必须编写大量代码才能执行以下步骤 + +- 使用功能区进行负载平衡。 +- 获取服务实例,然后获取基本URL。 +- 利用REST模板来使用服务。 前面的代码如下 + +```java +@Controller +public class ConsumerControllerClient { + +@Autowired +private LoadBalancerClient loadBalancer; + +public void getEmployee() throws RestClientException, IOException { + +    ServiceInstance serviceInstance=loadBalancer.choose("employee-producer"); + +    System.out.println(serviceInstance.getUri()); + +    String baseUrl=serviceInstance.getUri().toString(); + +    baseUrl=baseUrl+"/employee"; + +    RestTemplate restTemplate = new RestTemplate(); +    ResponseEntity response=null; +    try{ +    response=restTemplate.exchange(baseUrl, +            HttpMethod.GET, getHeaders(),String.class); +    }catch (Exception ex) +    { +        System.out.println(ex); +    } +    System.out.println(response.getBody()); +} +``` + +之前的代码,有像NullPointer这样的例外的机会,并不是最优的。我们将看到如何使用Netflix Feign使呼叫变得更加轻松和清洁。如果Netflix Ribbon依赖关系也在类路径中,那么Feign默认也会负责负载平衡。 + +#### 8.Eureka的工作原理? + +`Eureka`:服务注册中心(可以是一个集群),对外暴露自己的地址 +`提供者`:启动后向Eureka注册自己信息(地址,提供什么服务) +`消费者`:向Eureka订阅服务,Eureka会将对应服务的所有提供者地址列表发送给消费者,并且定期更新 +`心跳(续约)`:提供者定期通过http方式向Eureka刷新自己的状态(每30s定时向EurekaServer发起请求) + +#### 9.说说Eureka的自我保护机制? + +当一个服务未按时进行心跳续约时,在生产环境下,因为网络延迟等原因,此时就把服务剔除列表并不妥当,因为服务可能没有宕机。 Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。但是有可能会造成一些挂掉的服务被剔除有延迟。 + +#### 10.什么是zuul? + +zuul是对SpringCloud提供的成熟对的`路由方案`,他会根据请求的路径不同,网关会定位到指定的微服务,并代理请求到不同的微服务接口,他对外隐蔽了微服务的真正接口地址。 +三个重要概念: + +- `动态路由表`:Zuul支持Eureka路由,手动配置路由,这俩种都支持自动更新 + +- `路由定位`:根据请求路径,Zuul有自己的一套定位服务规则以及路由表达式匹配 + +- `反向代理`:客户端请求到路由网关,网关受理之后,在对目标发送请求,拿到响应之后在 给客户端 + +Zuul的应用场景: 对外暴露,权限校验,服务聚合,日志审计等 + + +#### 11.zuul的工作流程? + +在Spring Cloud Netflix中,Zuul巧妙的整合了Eureka来实现面向服务的路由。 +实际上,API网关将自己注册到Eureka服务注册中心上,也会从注册中心获取所有服务以及它们的实例清单。在Eureka的帮助下,API网关已经维护了系统中所有`serviceId与实例地址的映射关系`。当有外部请求到达API网关的时候,根据请求的URL找到最匹配的path,API网关就可以知道要将该请求"路由"到哪个具体的serviceId上去。 最终通过`Ribbon的负载均衡策略`实现请求的路由。 + + +#### 12.什么是服务熔断?什么是服务降级? + +服务熔断: +熔断机制是应对`雪崩效应`的一种微服务`链路保护机制`。 +当某个微服务不可用或者响应时间太长时,会进行服务降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。 +在SpringCloud框架里熔断机制通过`Hystrix`实现,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内调用20次,如果失败,就会启动熔断机制。 +服务降级: +服务降级,一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的`fallback回调`,返回一个缺省值。 + +#### 13.什么是服务雪崩效应? + +雪崩效应是在大型互联网项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,大型项目的微服务之间的调用是互通的,这样就会将服务的不可用逐步扩大到各个其他服务中,从而使整个项目的服务宕机崩溃。 + + +#### 14.ZuulFilter有哪些常用方法? + +`Run()`:过滤器的具体业务逻辑 + +`shouldFilter()`:判断过滤器是否有效 + +`filterOrder()`:过滤器执行顺序 + +`filterType()`:过滤器拦截位置 + +#### 15.如何实现动态Zuul网关路由转发? + +通过`path`配置拦截请求,通过 `Serviceld`到配置中心获取转发的服务列表,zuul内部使用 `Ribbon`实现本地负载均衡和转发。 + +#### 16.什么是 Spring Cloud Bus? + +Spring Cloud Bus就像个分布式执行器,用于扩展的 Spring Boot应用程序的配置文件,但也可以用作应用程序之间的通信通道。 + +Spring Cloud Bus不能单独完成通信,需要配合MQ支持 + +Spring Cloud Bus一般是配合Spring Cloud Config做配置中心的 + +Spring Cloud config实时刷新也必须采用 SpringCloud Bus消息总线 + +#### 17.Spring Cloud Bus 原理? + +发送端(endpoint)构造事件event,将其publish到context上下文中(spring cloud bus有一个父上下文,bootstrap),然后将事件发送到channel中(json串message),接收端从channel中获取到message,将message转为事件event,然后将event事件publish到context上下文中,最后接收端(Listener)收到event,调用服务进行处理。 +整个流程中,只有发送/接收端从context上下文中取事件和发送事件是需要我们在代码中明确写出来的,其它部分都由框架封装完成。 + +#### 18.SpringCloud Config可以实现实时刷新吗? + +springcloud config实时刷新采用 `SpringCloud Bus`消息总线 + +#### 19.Eureka和zookeeper都可以提供服务注册与发现的功能,两者的区别 + +Zookeeper保证了CP(C:一致性,P:分区容错性),Eureka保证了AP(A:高可用,P:分区容错) +1、Zookeeper-----当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接down掉不可用的。也就是说服务注册功能对高可用性要求比较高,但是zk会出现这样的一种情况,当master节点因为网络故障与其他节点失去联系时,剩余的节点会重新选leader。问题在于,选取leader的时间过长(30~120s),且选取期间zk集群都不可用,这样就会导致选取期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务最终恢复,但是漫长的选择时间导致的注册长期不可用是不能容忍的 +2、Eureka则看明白这一点,因此再设计的优先保证了高可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响到正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端再向某个Eureka注册时如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保证注册服务的可用(保证可用性),只不过查到的信息可能不是最新的(不保证一致性)。除此之外Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时就会出现以下几种情况: +1>、Eureka不再从注册列表移除因为长时间没收到心跳而应该过期的服务 +2>、Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(保证当前节点可用) +3>、当网络稳定时,当前实例新的注册信息会被同步到其它节点中 +Eureka还有客户端缓存功能(Eureka分为客户端程序和服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。所以即便Eureka集群中所有节点都失效,或者发生网络分隔故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者任然可以通过Eureka客户端缓存来获取所有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生响应也没有更好的服务器解决方案来解决这种问题时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息,这点很重要,因此Eureka可以很好的应对网络故障导致部分节点失去联系的情况,而不像Zookeeper那样使整个注册服务瘫痪。 + +#### 参考链接 + +https://blog.csdn.net/qq_39827935/article/details/96282405 + +https://blog.csdn.net/moakun/article/details/82817757 + +https://www.imooc.com/article/309292 diff --git a/Zookeeper.md b/Zookeeper.md new file mode 100644 index 0000000..e1b985f --- /dev/null +++ b/Zookeeper.md @@ -0,0 +1,332 @@ +## Zookeeper + + +* [1.Zookeeper有哪些节点类型?](#1zookeeper有哪些节点类型) +* [2.了解过Zookeeper的ZAB协议吗?](#2了解过zookeeper的zab协议吗) +* [3.Zookeeper怎么实现分布式锁?](#3zookeeper怎么实现分布式锁) +* [4.Zookeeper是怎么保证数据一致性的?](#4zookeeper是怎么保证数据一致性的) +* [5.Zookeeper Leader选举过程是怎样的?](#5zookeeper-leader选举过程是怎样的) +* [6.Zookeeper怎么实现服务注册?](#6zookeeper怎么实现服务注册) +* [7.ZooKeeper是什么?](#7zookeeper是什么) +* [8.ZooKeeper提供了什么?](#8zookeeper提供了什么) +* [9.Zookeeper文件系统](#9zookeeper文件系统) +* [10.Zookeeper Watcher 机制](#10zookeeper-watcher-机制) +* [11.客户端注册Watcher实现](#11客户端注册watcher实现) +* [12.服务端处理Watcher实现](#12服务端处理watcher实现) +* [13.ACL权限控制机制](#13acl权限控制机制) +* [14.服务器角色](#14服务器角色) +* [15.Zookeeper 下 Server工作状态](#15zookeeper-下-server工作状态) +* [16.数据同步](#16数据同步) +* [17.zookeeper是如何保证事务的顺序一致性的?](#17zookeeper是如何保证事务的顺序一致性的) +* [18.分布式集群中为什么会有Master?](#18分布式集群中为什么会有master) +* [19.zk节点宕机如何处理?](#19zk节点宕机如何处理) +* [20.Zookeeper有哪几种部署模式?](#20zookeeper有哪几种部署模式) +* [21.集群最少要几台机器,集群规则是怎样的?](#21集群最少要几台机器集群规则是怎样的) +* [22.集群支持动态添加机器吗?](#22集群支持动态添加机器吗) +* [23.Zookeeper对节点的watch监听通知是永久的吗?为什么不是永久的?](#23zookeeper对节点的watch监听通知是永久的吗为什么不是永久的) +* [24.ZAB和Paxos算法的联系与区别?](#24zab和paxos算法的联系与区别) +* [25.Zookeeper的典型应用场景](#25zookeeper的典型应用场景) +* [26.Zookeeper 和 Dubbo 的关系?](#26zookeeper-和-dubbo-的关系) +* [27.zookeeper负载均衡和nginx负载均衡区别](#27zookeeper负载均衡和nginx负载均衡区别) +* [参考资料](#参考资料) + + +#### 1.Zookeeper有哪些节点类型? + +PERSISTENT-持久节点 +除非手动删除,否则节点一直存在于Zookeeper上 + +EPHEMERAL-临时节点 +临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。 + +PERSISTENT_SEQUENTIAL-持久顺序节点 +基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +EPHEMERAL_SEQUENTIAL-临时顺序节点 +基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。 + +#### 2.了解过Zookeeper的ZAB协议吗? + +ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议。 + +ZAB协议包括两种基本的模式:崩溃恢复和消息广播。 + +当整个zookeeper集群刚刚启动或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与Leader服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式,首先选举产生新的Leader服务器,然后集群中Follower服务器开始与新的Leader服务器进行数据同步,当集群中超过半数机器与该Leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。 + +#### 3.Zookeeper怎么实现分布式锁? + +有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。 +对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。 +对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。 + +#### 4.Zookeeper是怎么保证数据一致性的? + +详见:https://blog.csdn.net/liuchang19950703/article/details/111406622 + +#### 5.Zookeeper Leader选举过程是怎样的? + +详见:https://blog.csdn.net/liuchang19950703/article/details/111406622 + +#### 6.Zookeeper怎么实现服务注册? + +详见:https://segmentfault.com/a/1190000019670015 + +#### 7.ZooKeeper是什么? + +ZooKeeper是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 + +分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。 + +Zookeeper保证了如下分布式一致性特性: +顺序一致性 +原子性 +单一视图 +可靠性 +实时性(最终一致性) + +客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的zookeeper机器来处理。对于写请求,这些请求会同时发给其他zookeeper机器并且达成一致后,请求才会返回成功。因此,随着zookeeper的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。 + +有序性是zookeeper中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper最新的zxid。 + +#### 8.ZooKeeper提供了什么? + +1、文件系统 +2、通知机制 + +#### 9.Zookeeper文件系统 + +Zookeeper提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。 +Zookeeper为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M。 + +#### 10.Zookeeper Watcher 机制 + +Zookeeper允许客户端向服务端的某个Znode注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据Watcher通知状态和事件类型做出业务上的改变。 + +工作机制: +客户端注册watcher +服务端处理watcher +客户端回调watcher +Watcher特性总结: + +一次性 +无论是服务端还是客户端,一旦一个Watcher被触发,Zookeeper都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。 + +客户端串行执行 +客户端Watcher回调的过程是一个串行同步的过程。 + +轻量 +Watcher通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。 +客户端向服务端注册Watcher的时候,并不会把客户端真实的Watcher对象实体传递到服务端,仅仅是在客户端请求中使用boolean类型属性进行了标记。 + +watcher event异步发送watcher的通知事件从server发送到client是异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于Zookeeper本身提供了ordering guarantee,即客户端监听事件后,才会感知它所监视znode发生了变化。所以我们使用Zookeeper不能期望能够监控到节点每次的变化。Zookeeper只能保证最终的一致性,而无法保证强一致性。 + +注册watcher getData、exists、getChildren + +触发watcher create、delete、setData + +当一个客户端连接到一个新的服务器上时,watch将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到watch的。而当client重新连接时,如果需要的话,所有先前注册过的watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch可能会丢失:对于一个未创建的znode的exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个watch事件可能会被丢失。 + +#### 11.客户端注册Watcher实现 + +调用getData()/getChildren()/exist()三个API,传入Watcher对象 +标记请求request,封装Watcher到WatchRegistration +封装成Packet对象,发服务端发送request +收到服务端响应后,将Watcher注册到ZKWatcherManager中进行管理 +请求返回,完成注册。 + +#### 12.服务端处理Watcher实现 + +1)服务端接收Watcher并存储 +接收到客户端请求,处理请求判断是否需要注册Watcher,需要的话将数据节点的节点路径和ServerCnxn(ServerCnxn代表一个客户端和服务端的连接,实现了Watcher的process接口,此时可以看成一个Watcher对象)存储在WatcherManager的WatchTable和watch2Paths中去。 + +2)Watcher触发 +以服务端接收到 setData() 事务请求触发NodeDataChanged事件为例: + +封装WatchedEvent +将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个WatchedEvent对象 +查询Watcher +从WatchTable中根据节点路径查找Watcher +没找到;说明没有客户端在该数据节点上注册过Watcher +找到;提取并从WatchTable和Watch2Paths中删除对应Watcher(从这里可以看出Watcher在服务端是一次性的,触发一次就失效了) + +3)调用process方法来触发Watcher +这里process主要就是通过ServerCnxn对应的TCP连接发送Watcher事件通知。 + +#### 13.ACL权限控制机制 + +1)UGO(User/Group/Others) +目前在Linux/Unix文件系统中使用,也是使用最广泛的权限控制方式。是一种粗粒度的文件系统权限控制模式。 + +2)ACL(Access Control List)访问控制列表 +包括三个方面: + +权限模式(Scheme) +IP:从IP地址粒度进行权限控制 +Digest:最常用,用类似于 username:password 的权限标识来进行权限配置,便于区分不同应用来进行权限控制 +World:最开放的权限控制方式,是一种特殊的digest模式,只有一个权限标识“world:anyone” +Super:超级用户 + +授权对象 +授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或是机器灯。 + +权限 Permission +CREATE:数据节点创建权限,允许授权对象在该Znode下创建子节点 +DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点 +READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等 +WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作 +ADMIN:数据节点管理权限,允许授权对象对该数据节点进行ACL相关设置操作 + +#### 14.服务器角色 + +Leader +事务请求的唯一调度和处理者,保证集群事务处理的顺序性 +集群内部各服务的调度者 + +Follower +处理客户端的非事务请求,转发事务请求给Leader服务器 +参与事务请求Proposal的投票 +参与Leader选举投票 + +Observer +3.3.0版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力 +处理客户端的非事务请求,转发事务请求给Leader服务器 +不参与任何形式的投票 + +#### 15.Zookeeper 下 Server工作状态 + +服务器具有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。 +LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。 +FOLLOWING:跟随者状态。表明当前服务器角色是Follower。 +LEADING:领导者状态。表明当前服务器角色是Leader。 +OBSERVING:观察者状态。表明当前服务器角色是Observer。 + +#### 16.数据同步 + +整个集群完成Leader选举之后,Learner(Follower和Observer的统称)回向Leader服务器进行注册。当Learner服务器想Leader服务器完成注册后,进入数据同步环节。 + +数据同步流程:(均以消息传递的方式进行) +i. Learner向Learder注册 +ii. 数据同步 +iii. 同步确认 + +Zookeeper的数据同步通常分为四类: + +直接差异化同步(DIFF同步) +先回滚再差异化同步(TRUNC+DIFF同步) +仅回滚同步(TRUNC同步) +全量同步(SNAP同步) +在进行数据同步前,Leader服务器会完成数据同步初始化: +peerLastZxid:从learner服务器注册时发送的ACKEPOCH消息中提取lastZxid(该Learner服务器最后处理的ZXID) +minCommittedLog:Leader服务器Proposal缓存队列committedLog中最小ZXID +maxCommittedLog:Leader服务器Proposal缓存队列committedLog中最大ZXID + +直接差异化同步(DIFF同步) +场景:peerLastZxid介于minCommittedLog和maxCommittedLog之间 + +先回滚再差异化同步(TRUNC+DIFF同步) +场景:当新的Leader服务器发现某个Learner服务器包含了一条自己没有的事务记录,那么就需要让该Learner服务器进行事务回滚--回滚到Leader服务器上存在的,同时也是最接近于peerLastZxid的ZXID + +仅回滚同步(TRUNC同步) +场景:peerLastZxid 大于 maxCommittedLog + +全量同步(SNAP同步) +场景一:peerLastZxid 小于 minCommittedLog +场景二:Leader服务器上没有Proposal缓存队列且peerLastZxid不等于lastProcessZxid + +#### 17.zookeeper是如何保证事务的顺序一致性的? + +zookeeper采用了全局递增的事务Id来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader周期,如果有新的leader产生出来,epoch会自增,低32位用来递增计数。当新产生proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。 + +#### 18.分布式集群中为什么会有Master? + +在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行leader选举。 + +#### 19.zk节点宕机如何处理? + +Zookeeper本身也是集群,推荐配置不少于3个服务器。Zookeeper自身也要保证当一个节点宕机时,其他节点会继续提供服务。 +如果是一个Follower宕机,还有2台服务器提供访问,因为Zookeeper上的数据是有多个副本的,数据并不会丢失; +如果是一个Leader宕机,Zookeeper会选举出新的Leader。 +ZK集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。 + +所以 +3个节点的cluster可以挂掉1个节点(leader可以得到2票>1.5) +2个节点的cluster就不能挂掉任何1个节点了(leader可以得到1票<=1) + +#### 20.Zookeeper有哪几种部署模式? + +单机模式、伪集群模式、集群模式。 + +#### 21.集群最少要几台机器,集群规则是怎样的? + +集群规则为2N+1台,N>0,即3台。 + +#### 22.集群支持动态添加机器吗? + +其实就是水平扩容了,Zookeeper在这方面不太好。两种方式: +全部重启:关闭所有Zookeeper服务,修改配置之后启动。不影响之前客户端的会话。 +逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。 +3.5版本开始支持动态扩容。 + +#### 23.Zookeeper对节点的watch监听通知是永久的吗?为什么不是永久的? + +不是。官方声明:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。 + +为什么不是永久的,举个例子,如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。 +一般是客户端执行getData(“/节点A”,true),如果节点A发生了变更或删除,客户端会得到它的watch事件,但是在之后节点A又发生了变更,而客户端又没有设置watch事件,就不再给客户端发送。 +在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。 + +#### 24.ZAB和Paxos算法的联系与区别? + +相同点: +两者都存在一个类似于Leader进程的角色,由其负责协调多个Follower进程的运行 +Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交 +ZAB协议中,每个Proposal中都包含一个 epoch 值来代表当前的Leader周期,Paxos中名字为Ballot + +不同点: +ZAB用来构建高可用的分布式数据主备系统(Zookeeper),Paxos是用来构建分布式一致性状态机系统。 + +#### 25.Zookeeper的典型应用场景 + +数据发布/订阅 +负载均衡 +命名服务 +分布式协调/通知 +集群管理 +Master选举 +分布式锁 +分布式队列 + +#### 26.Zookeeper 和 Dubbo 的关系? + +Zookeeper的作用: +zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过添加机器来提高运算能力。通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。 + +dubbo: +是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。 +注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。 + +zookeeper和dubbo的关系: +Dubbo 的将注册中心进行抽象,它可以外接不同的存储媒介给注册中心提供服务,有 ZooKeeper,Memcached,Redis 等。 +引入了 ZooKeeper 作为存储媒介,也就把 ZooKeeper 的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个 ZooKeeper 群配合相应的 Web 应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不 够,节点之间的数据和资源需要同步,ZooKeeper 集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向 ZooKeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。 其他特性还有 Mast 选举,分布式锁等。 + +#### 27.zookeeper负载均衡和nginx负载均衡区别 + +zookeeper +不存在单点问题,zab机制保证单点故障可重新选举一个leader +只负责服务的注册与发现,不负责转发,减少一次数据交换(消费方与服务方直接通信) +需要自己实现相应的负载均衡算法 + +nginx +存在单点问题,单点负载高数据量大,需要通过KeepAlived+LVS备机实现高可用 +每次负载,都充当一次中间人转发角色,增加网络负载量(消费方与服务方间接通信) +自带负载均衡算法 + + + + +### 参考资料 +https://www.cnblogs.com/lanqiu5ge/p/9405601.html +https://blog.csdn.net/weixin_43122090/article/details/103645642 +https://www.cnblogs.com/liulong99/p/13036794.html +https://zhuanlan.zhihu.com/p/94146775 +https://segmentfault.com/a/1190000014479433 diff --git "a/imgs/\347\250\213\345\272\217\345\221\230\347\231\276\347\247\221\345\205\250\344\271\246.jpg" "b/imgs/\347\250\213\345\272\217\345\221\230\347\231\276\347\247\221\345\205\250\344\271\246.jpg" new file mode 100644 index 0000000..ee76cdb Binary files /dev/null and "b/imgs/\347\250\213\345\272\217\345\221\230\347\231\276\347\247\221\345\205\250\344\271\246.jpg" differ diff --git "a/\345\210\206\345\270\203\345\274\217.md" "b/\345\210\206\345\270\203\345\274\217.md" new file mode 100644 index 0000000..dca9148 --- /dev/null +++ "b/\345\210\206\345\270\203\345\274\217.md" @@ -0,0 +1,215 @@ +## 分布式 + + +* [1.分布式id如何生成?](#1分布式id如何生成) +* [2.雪花算法了解过吗?](#2雪花算法了解过吗) +* [3.什么是CAP定理?](#3什么是cap定理) +* [4.分布式事务了解过吗?](#4分布式事务了解过吗) +* [5.什么是二阶段提交(2PC)?什么是三阶段提交(3PC)?](#5什么是二阶段提交2pc什么是三阶段提交3pc) +* [6.TCC了解过吗?](#6tcc了解过吗) +* [7.Paxos算法了解过吗?](#7paxos算法了解过吗) +* [8.Zookeeper的Zab协议了解过吗?](#8zookeeper的zab协议了解过吗) +* [9.知道什么是Gossip协议吗?](#9知道什么是gossip协议吗) +* [10.了解过哪些负载均衡算法?](#10了解过哪些负载均衡算法) +* [11.负载均衡的实现方案有哪些?](#11负载均衡的实现方案有哪些) +* [12.正向代理和反向代理的区别](#12正向代理和反向代理的区别) +* [13.分布式 Session了解过吗?如何实现?](#13分布式-session了解过吗如何实现) +* [14.如何防止表单重复提交?](#14如何防止表单重复提交) +* [15.如何设计一个秒杀系统?](#15如何设计一个秒杀系统) +* [16.分布式系统的接口幂等性设计](#16分布式系统的接口幂等性设计) +* [17.如何保障请求执行顺序](#17如何保障请求执行顺序) +* [18.BASE理论了解过吗?](#18base理论了解过吗) +* [19.SOA和微服务架构有哪些区别?](#19soa和微服务架构有哪些区别) +* [参考资料](#参考资料) + + +#### 1.分布式id如何生成? + +详见:https://mp.weixin.qq.com/s/eakphQDWKrsUnIwTj8zMQA + +#### 2.雪花算法了解过吗? + +雪花算法生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。 雪花ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型。 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年 工作机器id(10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID + +#### 3.什么是CAP定理? + +任何分布式系统都无法同时满足一致性(consistency),可用性(availibity),分区容错性(partition tolerance)这三项,最多只可同时满足其中的两项。 + +#### 4.分布式事务了解过吗? + +涉及到多个数据库操作的事务即为分布式事务,目的是为保证分布式系统中的数据一致性. + +#### 5.什么是二阶段提交(2PC)?什么是三阶段提交(3PC)? + +二阶段提交2PC:第一步请求阶段通过协调者来统计表决结果,第二步执行表决后的结果,如果表决的结果是提交,那就提交执行,否则不执行提交.缺点是同步阻塞,而且万一协调者挂了就无法保证ACID. + +三阶段提交3PC:在2PC的第一步拆分成了2步并且引入了超时机制,解决了2PC的痛点.第一步先向参与者发出一个信号,看看大家是否都能提交,如果可以就返回yes,否则返回no.第二步PreCommit阶段,预提交一下,如果参与者可以完成commit,就返回ack进确认,如果不能则放弃提交本次事务.第三步doCommit阶段,进行真正的事务提交. + +#### 6.TCC了解过吗? + +try,commit,cancel的缩写,try阶段进行检测,commit提交执行,只要try阶段成功了commit就一定会被执行,cancel业务出现错误时执行,回滚事务,释放资源. + +#### 7.Paxos算法了解过吗? + +详见:https://blog.csdn.net/westbrookliu/article/details/99713365 + +#### 8.Zookeeper的Zab协议了解过吗? + +详见:https://blog.csdn.net/liuchang19950703/article/details/111406622 + +#### 9.知道什么是Gossip协议吗? + +详见:https://mp.weixin.qq.com/s/dW0I29Sw86lU0qHpxyhdmw + +#### 10.了解过哪些负载均衡算法? + +轮询(Round Robin) +轮询算法把每个请求轮流发送到每个服务器上。 +该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担多大的负载。 + +加权轮询(Weighted Round Robbin) +加权轮询是在轮询的基础上,根据服务器的性能差异,为服务器赋予一定的权值。 + +最少连接(least Connections) +由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过多,而另一台服务器的连接数过少,造成负载不均衡。最少连接算法就是将请求发送给当前最少连接数的服务器上。 + +加权最小连接(Weighted Least Connection) +在最小连接的基础上,根据服务器的性能为每台服务器分配权重,然后根据权重计算出每台服务器能处理的连接数。 + +随机算法(Random) +把请求随机发送到服务器上。和轮询算法类似,该算法比较适合服务器性能差不多的场景。 + +#### 11.负载均衡的实现方案有哪些? + +DNS 解析 +使用 DNS 作为负载均衡器,会根据负载情况返回不同服务器的 IP 地址。大型网站基本使用了这种方式最为第一级负载均衡手段,然后在内部在第二级负载均衡。 + +修改 MAC 地址 +使用 LVS(Linux Virtual Server)这种链路层负载均衡器,根据负载情况修改请求的 MAC 地址。 + +修改 IP 地址 +在网络层修改请求的目的 IP 地址。 + +HTTP 重定向 +HTTP 重定向负载均衡服务器收到 HTTP 请求之后会返回服务器的地址,并将该地址写入 HTTP 重定向响应中返回给浏览器,浏览器收到后再次发送请求。 + +#### 12.正向代理和反向代理的区别 + +正向代理:发生在客户端,是由用户主动发起的。比如翻墙,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端。 +反向代理:发生在服务器端,用户不知道发生了代理。 + +#### 13.分布式 Session了解过吗?如何实现? + +如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在 A、B 两台服务器,用户在第一次访问网站时,Nginx 通过其负载均衡机制将用户请求转发到 A 服务器,这时 A 服务器就会给用户创建一个 Session。当用户第二次发送请求时,Nginx 将其负载均衡到 B 服务器,而这时候 B 服务器并不存在 Session,所以就会将用户踢到登录页面。这将大大降低用户体验度,导致用户的流失,这种情况是项目绝不应该出现的。 + +1. 粘性 Session +原理 +粘性 Session 是指将用户锁定到某一个服务器上,比如上面说的例子,用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 Session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 Session 机制。 + +优点 +简单,不需要对 Session 做任何处理。 + +缺点 +缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 Session 信息都将失效。 + +适用场景 +发生故障对客户产生的影响较小; +服务器发生故障是低概率事件。 + +2. 服务器 Session 复制 + +原理 +任何一个服务器上的 Session 发生改变,该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 Session,以此来保证 Session 同步。 + +优点 +可容错,各个服务器间 Session 能够实时响应。 + +缺点 +会对网络负荷造成一定压力,如果 Session 量大的话可能会造成网络堵塞,拖慢服务器性能。 + +实现方式 +设置 Tomcat 的 server.xml 开启 tomcat 集群功能。 +在应用里增加信息:通知应用当前处于集群环境中,支持分布式,即在 web.xml 中添加 选项。 + +3. Session 共享机制 + +使用分布式缓存方案比如 Memcached、Redis,但是要求 Memcached 或 Redis 必须是集群。 +使用 Session 共享也分两种机制,两种情况如下: + +3.1 粘性 Session 共享机制 +和粘性 Session 一样,一个用户的 Session 会绑定到一个 Tomcat 上。Memcached 只是起到备份作用。 + +3.2 非粘性 Session 共享机制 + +原理 +Tomcat 本身不存储 Session,而是存入 Memcached 中。Memcached 集群构建主从复制架构。 + +优点 +可容错,Session 实时响应。 +实现方式 +用开源的 msm 插件解决 Tomcat 之间的 Session 共享:Memcached_Session_Manager(MSM) + +4. Session 持久化到数据库 + +原理 +拿出一个数据库,专门用来存储 Session 信息。保证 Session 的持久化。 + +优点 +服务器出现问题,Session 不会丢失。 + +缺点 +如果网站的访问量很大,把 Session 存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。 + +5. Terracotta 实现 Session 复制 + +原理 +Terracotta 的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta 只把变化的部分发送给 Terracotta 服务器,然后由服务器把它转发给真正需要这个数据的节点。它是服务器 Session 复制的优化。 + +优点 +这样对网络的压力就非常小,各个节点也不必浪费 CPU 时间和内存进行大量的序列化操作。把这种集群间数据共享的机制应用在 Session 同步上,既避免了对数据库的依赖,又能达到负载均衡和灾难恢复的效果。 + +#### 14.如何防止表单重复提交? + +前端。每次点击后都要等X秒才能点击。 +数据库添加唯一索引 +服务器返回表单页面时,会先生成一个token保存于session或redis,当表单提交时候携带token,如果token一致,则执行后续,并将服务器中的token删除。 + +#### 15.如何设计一个秒杀系统? + +前端:在秒杀之前,按钮置灰,并且不给前端真正的请求地址。前端定时请求后端接口,如果到了秒杀时间,则返回给前端真正的地址,前端放开按钮,每次点击后都要等X秒才能点击。 +服务器:服务器用nginx做集群、redis也做集群 +限流:在秒杀之前,将秒杀数量的令牌存入到redis中,可以用list,每次来请求都去redis中取出令牌,如果获取到说明秒杀成功,然后去访问数据库下单,如果没有获取到,则说明商品卖完了。 +消息中间件:如果秒杀数量比较多,例如上万十万,则秒杀成功之后,将成功的请求放入到mq或者kafka中间件中,再从消息队列中获取请求。 +服务降级:为了以防万一,还是要做服务熔断降级。 + + +#### 16.分布式系统的接口幂等性设计 + +唯一id。每次操作,都根据操作和内容生成唯一的id,在执行之前先判断id是否存在,如果不存在则执行后续操作,并且保存到数据库或者redis等。 +服务端提供发送token的接口,业务调用接口前先获取token,然后调用业务接口请求时,把token携带过去,务器判断token是否存在redis中,存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把redis中的token删除 +建去重表。将业务中有唯一标识的字段保存到去重表,如果表中存在,则表示已经处理过了 +版本控制。增加版本号,当版本号符合时,才能更新数据 +状态控制。例如订单有状态已支付 未支付 支付中 支付失败,当处于未支付的时候才允许修改为支付中等 + +#### 17.如何保障请求执行顺序 + +一般来说,从业务逻辑上最好设计系统不需要这种顺序的保证,因为一旦引入顺序性保障,会导致系统复杂度的上升,效率会降低,对于热点数据会压力过大等问题。 +首先使用一致性hash负载均衡策略,将同一个id的请求都分发到同一个机器上面去处理,比如订单可以根据订单id。如果处理的机器上面是多线程处理的,可以引入内存队列去处理,将相同id的请求通过hash到同一个队列当中,一个队列只对应一个处理线程。 +最好能将多个操作合并成一个操作。 + +#### 18.BASE理论了解过吗? + +BASE是 Basically Available (基本可用) Soft state(软状态) Eventually consistent(最终一致性)这几个单词的缩写,是从CAP理论发展而来的,其核心思想是:即使无法做到强一致性,但每个应用都可以根据自身特点,采取适当的方式来使系统达到最终一致性. + +#### 19.SOA和微服务架构有哪些区别? + +微服务是在SOA的基础上发展而来,从粒度上来说,微服务的粒度要比SOA更细. +微服务由于粒度更细,所以微服务架构的耦合度相对于SOA架构的耦合度更低. +微服务的服务规模相较于SOA一般要更大,所能承载的并发量也更高. + + + +### 参考资料 +https://www.cnblogs.com/workstation-nigoudongma/p/9546801.html +https://blog.csdn.net/zgsxhdzxl/article/details/104414475 +https://blog.csdn.net/lovexiaotaozi/article/details/89713937 diff --git "a/\345\244\232\347\272\277\347\250\213.md" "b/\345\244\232\347\272\277\347\250\213.md" new file mode 100644 index 0000000..97c5eff --- /dev/null +++ "b/\345\244\232\347\272\277\347\250\213.md" @@ -0,0 +1,437 @@ +## 多线程 + + +* [1.说说synchronized的实现原理](#1说说synchronized的实现原理) +* [2.ReentrantLock与synchronized的区别](#2reentrantlock与synchronized的区别) +* [3.ReentrantLock实现原理](#3reentrantlock实现原理) +* [4.Java原子类AtomicInteger实现原理](#4java原子类atomicinteger实现原理) +* [5.Java线程池实现原理](#5java线程池实现原理) +* [6.ThreadLocal实现原理](#6threadlocal实现原理) +* [7.InheritableThreadLocal原理知道吗?](#7inheritablethreadlocal原理知道吗) +* [8.说一下synchronized锁升级过程](#8说一下synchronized锁升级过程) +* [9.了解过什么是“伪共享”吗?](#9了解过什么是伪共享吗) +* [10.“伪共享”出现的原因是什么?](#10伪共享出现的原因是什么) +* [11.如何避免“伪共享”?](#11如何避免伪共享) +* [12.Java里的线程有哪些状态?](#12java里的线程有哪些状态) +* [13.什么是悲观锁?什么是乐观锁?](#13什么是悲观锁什么是乐观锁) +* [14.怎么停止一个运行中的线程?](#14怎么停止一个运行中的线程) +* [15.说一下你对volatile的理解?](#15说一下你对volatile的理解) +* [16.并发编程三要素?](#16并发编程三要素) +* [17.创建线程有哪些方式?](#17创建线程有哪些方式) +* [18.线程池的优点?](#18线程池的优点) +* [19.CyclicBarrier和CountDownLatch的区别](#19cyclicbarrier和countdownlatch的区别) +* [20.什么是CAS?](#20什么是cas) +* [21.CAS的问题](#21cas的问题) +* [22.什么是AQS?](#22什么是aqs) +* [23.AQS支持几种同步方式?](#23aqs支持几种同步方式) +* [24.什么是自旋锁?](#24什么是自旋锁) +* [25.什么是多线程的上下文切换?](#25什么是多线程的上下文切换) +* [26.什么是线程和进程?](#26什么是线程和进程) +* [27.程序计数器为什么是私有的?](#27程序计数器为什么是私有的) +* [28.虚拟机栈和本地方法栈为什么是私有的?](#28虚拟机栈和本地方法栈为什么是私有的) +* [29.并发与并行的区别?](#29并发与并行的区别) +* [30.什么是线程死锁?如何避免死锁?](#30什么是线程死锁如何避免死锁) +* [31.sleep() 方法和 wait() 方法的区别和共同点?](#31sleep-方法和-wait-方法的区别和共同点) +* [32.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?](#32为什么我们调用-start-方法时会执行-run-方法为什么我们不能直接调用-run-方法) +* [33.什么是线程安全问题?如何解决?](#33什么是线程安全问题如何解决) +* [34.什么是活锁?](#34什么是活锁) +* [35.什么是线程的饥饿问题?如何解决?](#35什么是线程的饥饿问题如何解决) +* [36.什么是线程的阻塞问题?如何解决?](#36什么是线程的阻塞问题如何解决) +* [37.synchronized 关键字和 volatile 关键字的区别](#37synchronized-关键字和-volatile-关键字的区别) +* [38.说一说几种常见的线程池及适用场景?](#38说一说几种常见的线程池及适用场景) +* [39.线程池都有哪几种工作队列?](#39线程池都有哪几种工作队列) +* [40.什么是线程安全?](#40什么是线程安全) +* [41.Java中如何获取到线程dump文件](#41java中如何获取到线程dump文件) +* [42.Java中用到的线程调度算法是什么?](#42java中用到的线程调度算法是什么) +* [43.Thread.sleep(0)的作用是什么?](#43threadsleep0的作用是什么) +* [44.单例模式的线程安全性](#44单例模式的线程安全性) +* [45.Semaphore有什么作用?](#45semaphore有什么作用) +* [46.Hashtable的size()方法中明明只有一条语句"return count",为什么还要做同步?](#46hashtable的size方法中明明只有一条语句return-count为什么还要做同步) +* [47.同步方法和同步块,哪个是更好的选择?](#47同步方法和同步块哪个是更好的选择) +* [48.高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?](#48高并发任务执行时间短的业务怎样使用线程池并发不高任务执行时间长的业务怎样使用线程池并发高业务执行时间长的业务怎样使用线程池) +* [49.在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?](#49在java中lock接口比synchronized块的优势是什么你需要实现一个高效的缓存它允许多个用户读但只允许一个用户写以此来保持它的完整性你会怎样去实现它) +* [50.你将如何使用thread dump?你将如何分析Thread dump?](#50你将如何使用thread-dump你将如何分析thread-dump) +* [参考资料](#参考资料) + + +#### 1.说说synchronized的实现原理 + +在 Java 中,每个对象都隐式包含一个 monitor(监视器)对象,加锁的过程其实就是竞争 monitor 的过程,当线程进入字节码 monitorenter 指令之后,线程将持有 monitor 对象,执行 monitorexit 时释放 monitor 对象,当其他线程没有拿到 monitor 对象时,则需要阻塞等待获取该对象。 + +#### 2.ReentrantLock与synchronized的区别 + +ReentrantLock 有如下特点: + +1. 可重入 + ReentrantLock 和 syncronized 关键字一样,都是可重入锁,不过两者实现原理稍有差别, RetrantLock 利用 AQS 的的 state 状态来判断资源是否已锁,同一线程重入加锁, state 的状态 +1 ; 同一线程重入解锁, state 状态 -1 (解锁必须为当前独占线程,否则异常); 当 state 为 0 时解锁成功。 +2. 需要手动加锁、解锁 + synchronized 关键字是自动进行加锁、解锁的,而 ReentrantLock 需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成,来手动加锁、解锁。 +3. 支持设置锁的超时时间 + synchronized 关键字无法设置锁的超时时间,如果一个获得锁的线程内部发生死锁,那么其他线程就会一直进入阻塞状态,而 ReentrantLock 提供 tryLock 方法,允许设置线程获取锁的超时时间,如果超时,则跳过,不进行任何操作,避免死锁的发生。 +4. 支持公平/非公平锁 + synchronized 关键字是一种非公平锁,先抢到锁的线程先执行。而 ReentrantLock 的构造方法中允许设置 true/false 来实现公平、非公平锁,如果设置为 true ,则线程获取锁要遵循"先来后到"的规则,每次都会构造一个线程 Node ,然后到双向链表的"尾巴"后面排队,等待前面的 Node 释放锁资源。 +5. 可中断锁 + ReentrantLock 中的 lockInterruptibly() 方法使得线程可以在被阻塞时响应中断,比如一个线程 t1 通过 lockInterruptibly() 方法获取到一个可重入锁,并执行一个长时间的任务,另一个线程通过 interrupt() 方法就可以立刻打断 t1 线程的执行,来获取t1持有的那个可重入锁。而通过 ReentrantLock 的 lock() 方法或者 Synchronized 持有锁的线程是不会响应其他线程的 interrupt() 方法的,直到该方法主动释放锁之后才会响应 interrupt() 方法。 + +#### 3.ReentrantLock实现原理 + +详见:https://blog.csdn.net/yanbin0830/article/details/107542529 + +#### 4.Java原子类AtomicInteger实现原理 + +详见:https://www.cnblogs.com/scuwangjun/p/9098057.html + +#### 5.Java线程池实现原理 + +详见:https://www.cnblogs.com/dolphin0520/p/3932921.html + +#### 6.ThreadLocal实现原理 + +详见:https://www.cnblogs.com/fsmly/p/11020641.html + +#### 7.InheritableThreadLocal原理知道吗? + +详见:https://www.jianshu.com/p/94ba4a918ff5 + +#### 8.说一下synchronized锁升级过程 + +1. 偏向锁 + 在 JDK1.8 中,其实默认是轻量级锁,但如果设定了 -XX:BiasedLockingStartupDelay = 0 ,那在对一个 Object 做 syncronized 的时候,会立即上一把偏向锁。当处于偏向锁状态时, markwork 会记录当前线程 ID 。 + +2. 升级到轻量级锁 + 当下一个线程参与到偏向锁竞争时,会先判断 markword 中保存的线程 ID 是否与这个线程 ID 相等,如果不相等,会立即撤销偏向锁,升级为轻量级锁。每个线程在自己的线程栈中生成一个 LockRecord ( LR ),然后每个线程通过 CAS (自旋)的操作将锁对象头中的 markwork 设置为指向自己的 LR 的指针,哪个线程设置成功,就意味着获得锁。关于 synchronized 中此时执行的 CAS 操作是通过 native 的调用 HotSpot 中 bytecodeInterpreter.cpp 文件 C++ 代码实现的,有兴趣的可以继续深挖。 + +3. 升级到重量级锁 + 如果锁竞争加剧(如线程自旋次数或者自旋的线程数超过某阈值, JDK1.6 之后,由 JVM 自己控制该规则),就会升级为重量级锁。此时就会向操作系统申请资源,线程挂起,进入到操作系统内核态的等待队列中,等待操作系统调度,然后映射回用户态。在重量级锁中,由于需要做内核态到用户态的转换,而这个过程中需要消耗较多时间,也就是"重"的原因之一。 + +#### 9.了解过什么是“伪共享”吗? + +CPU缓存从内存读数据时,是按缓存行读取的,即使只用到一个变量,也要将整行数据进行读取,这行数据量可能包含其他变量。当多个线程同时修改同一个缓存行里的不同变量时,由于同时只能有一个线程在操作,所以相比将每个变量放到不同缓存行里,性能会有所下降。多个线程同时修改了同一个缓存行上的不同变量,由于不能并发修改,所以称为“伪共享”。 + +#### 10.“伪共享”出现的原因是什么? + +因为CPU缓存和内存交换数据的单位是缓存行,而同一个缓存行里的多个变量不能同时被多个线程修改。 + +#### 11.如何避免“伪共享”? + +1. 字节填充(创建变量时,使用字段对其进行填充,避免多个变量被分派到同一个缓存行里)。 +2. JDK8提供了一个Contended注解来解决伪共享。 + +#### 12.Java里的线程有哪些状态? + +1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。 +2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 +3. 阻塞(BLOCKED):表示线程阻塞于锁。 +4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。 +5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。 +6. 终止(TERMINATED):表示该线程已经执行完毕。 + +#### 13.什么是悲观锁?什么是乐观锁? + +当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。 +这种借助数据库锁机制在修改数据之前先锁定,再修改的方式被称之为悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)。 +之所以叫做悲观锁,是因为这是一种对数据的修改抱有悲观态度的并发控制方式。我们一般认为数据被并发修改的概率比较大,所以需要在修改之前先加锁。 + +乐观锁( Optimistic Locking ) 是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。 +相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。 + +#### 14.怎么停止一个运行中的线程? + +详见:https://www.cnblogs.com/liyutian/p/10196044.html + +#### 15.说一下你对volatile的理解? + +详见:https://blog.csdn.net/yinbucheng/article/details/71305951/ + +#### 16.并发编程三要素? + +1)原子性 +原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。 + +2)可见性 +可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。 + +3)有序性 +有序性,即程序的执行顺序按照代码的先后顺序来执行。 + +#### 17.创建线程有哪些方式? + +1)继承Thread类创建线程类 + +2)通过Runnable接口创建线程类 + +3)通过Callable和Future创建线程 + +4)通过线程池创建 + +#### 18.线程池的优点? + +1)重用存在的线程,减少对象创建销毁的开销。 +2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。 +3)提供定时执行、定期执行、单线程、并发数控制等功能。 + +#### 19.CyclicBarrier和CountDownLatch的区别 + +1)CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。 +2)cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行! +3)CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。 +4)CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false。 + +#### 20.什么是CAS? + +CAS是compare and swap的缩写,即我们所说的比较交换。 + +cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。 + +CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。 + +java.util.concurrent.atomic 包下的类大多是使用CAS操作来实现的( AtomicInteger,AtomicBoolean,AtomicLong)。 + +#### 21.CAS的问题 + +1)CAS容易造成ABA问题 + +一个线程a将数值改成了b,接着又改成了a,此时CAS认为是没有变化,其实是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次version加1。在java5中,已经提供了AtomicStampedReference来解决问题。 + +2) 不能保证代码块的原子性 + +CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。 + +3)CAS造成CPU利用率增加 + +之前说过了CAS里面是一个循环判断的过程,如果线程一直没有获取到状态,cpu资源会一直被占用。 + +#### 22.什么是AQS? + +AQS是AbustactQueuedSynchronizer的简称,它是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。 + +AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。 + +#### 23.AQS支持几种同步方式? + +1)独占式 + +2)共享式 + +这样方便使用者实现不同类型的同步组件,独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock。总之,AQS为使用提供了底层支撑,如何组装实现,使用者可以自由发挥。 + +#### 24.什么是自旋锁? + +自旋锁是SMP架构中的一种low-level的同步机制。 +当线程A想要获取一把自旋锁而该锁又被其它线程锁持有时,线程A会在一个循环中自旋以检测锁是不是已经可用了。 + +自旋锁需要注意: +由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,这就会浪费CPU时间。 +持有自旋锁的线程在sleep之前应该释放自旋锁以便其它线程可以获得自旋锁。 + +#### 25.什么是多线程的上下文切换? + +即使是单核CPU也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程时同时执行的,时间片一般是几十毫秒(ms) + +上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行 +CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态 +从任务保存到再加载的过程就是一次上下文切换 + +#### 26.什么是线程和进程? + +进程:在操作系统中能够独立运行,并且作为资源分配的基本单位。它表示运行中的程序。系统运行一个程序就是一个进程从创建、运行到消亡的过程。 + +线程:是一个比进程更小的执行单位,能够完成进程中的一个功能,也被称为轻量级进程。一个进程在其执行的过程中可以产生多个线程。 + +线程与进程不同的是:同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多。 + +#### 27.程序计数器为什么是私有的? + +程序计数器主要有下面两个作用: + +字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。 +在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 +(需要注意的是,如果执行的是 native 方法,那么程序计数器记录的是 undefined 地址,只有执行的是 Java 代码时程序计数器记录的才是下一条指令的地址。) + +所以,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。 + +#### 28.虚拟机栈和本地方法栈为什么是私有的? + +虚拟机栈: 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 +本地方法栈: 和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。 +所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。 + +#### 29.并发与并行的区别? + +并发指的是多个任务交替进行,并行则是指真正意义上的“同时进行”。 + +实际上,如果系统内只有一个CPU,使用多线程时,在真实系统环境下不能并行,只能通过切换时间片的方式交替进行,从而并发执行任务。真正的并行只能出现在拥有多个CPU的系统中。 + +#### 30.什么是线程死锁?如何避免死锁? + +多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 + +假如线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。 + +避免死锁的几个常见方法: +避免一个线程同时获取多个锁 +避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 +尝试使用定时锁,使用 lock.tryLock(timeout) 来代替使用内部锁机制。 +对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。 + +#### 31.sleep() 方法和 wait() 方法的区别和共同点? + +相同点: +两者都可以暂停线程的执行,都会让线程进入等待状态。 + +不同点: +sleep()方法没有释放锁,而 wait()方法释放了锁。 +sleep()方法属于Thread类的静态方法,作用于当前线程;而wait()方法是Object类的实例方法,作用于对象本身。 +执行sleep()方法后,可以通过超时或者调用interrupt()方法唤醒休眠中的线程;执行wait()方法后,通过调用notify()或notifyAll()方法唤醒等待线程。 + +#### 32.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法? + +new 一个 Thread,线程进入初始状态;调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。 + +总结: 调用 start 方法可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。 + +#### 33.什么是线程安全问题?如何解决? + +线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程就发生了线程安全问题,表现形式为数据的缺失,数据不一致等。 + +线程安全问题发生的条件: + +1)多线程环境下,即存在包括自己在内存在有多个线程。 +2)多线程环境下存在共享资源,且多线程操作该共享资源。 +3)多个线程必须对该共享资源有非原子性操作。 + +线程安全问题的解决思路: +1)尽量不使用共享变量,将不必要的共享变量变成局部变量来使用。 +2)使用synchronized关键字同步代码块,或者使用jdk包中提供的Lock为操作进行加锁。 +3)使用ThreadLocal为每一个线程建立一个变量的副本,各个线程间独立操作,互不影响。 + +#### 34.什么是活锁? + +活锁体现了一种谦让的美德,每个线程都想把资源让给对方,但是由于机器“智商”不够,可能会产生一直将资源让来让去,导致资源在两个线程间跳动而无法使某一线程真正的到资源并执行,这就是活锁的问题。 + +#### 35.什么是线程的饥饿问题?如何解决? + +饥饿指的是某一线程或多个线程因为某些原因一直获取不到资源,导致程序一直无法执行。如某一线程优先级太低导致一直分配不到资源,或者是某一线程一直占着某种资源不放,导致该线程无法执行等。 + +解决方法: +与死锁相比,饥饿现象还是有可能在一段时间之后恢复执行的。可以设置合适的线程优先级来尽量避免饥饿的产生。 + +#### 36.什么是线程的阻塞问题?如何解决? + +阻塞是用来形容多线程的问题,几个线程之间共享临界区资源,那么当一个线程占用了临界区资源后,所有需要使用该资源的线程都需要进入该临界区等待,等待会导致线程挂起,一直不能工作,这种情况就是阻塞,如果某一线程一直都不释放资源,将会导致其他所有等待在这个临界区的线程都不能工作。当我们使用synchronized或重入锁时,我们得到的就是阻塞线程,如论是synchronized或者重入锁,都会在试图执行代码前,得到临界区的锁,如果得不到锁,线程将会被挂起等待,知道其他线程执行完成并释放锁且拿到锁为止。 + +解决方法: +可以通过减少锁持有时间,读写锁分离,减小锁的粒度,锁分离,锁粗化等方式来优化锁的性能。 + +#### 37.synchronized 关键字和 volatile 关键字的区别 + +volatile关键字是线程同步的轻量级实现,所以volatile性能比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。 +多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞。 +volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。 +volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。 + +#### 38.说一说几种常见的线程池及适用场景? + +FixedThreadPool:可重用固定线程数的线程池。(适用于负载比较重的服务器) +FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列 +该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。 + +SingleThreadExecutor:只会创建一个线程执行任务。(适用于需要保证顺序执行各个任务;并且在任意时间点,没有多线程活动的场景。) +SingleThreadExecutorl也使用无界队列LinkedBlockingQueue作为工作队列 +若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。 + +CachedThreadPool:是一个会根据需要调整线程数量的线程池。(大小无界,适用于执行很多的短期异步任务的小程序,或负载较轻的服务器) +CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的。 +线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。 + +ScheduledThreadPool:继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。使用DelayQueue作为任务队列。 + +#### 39.线程池都有哪几种工作队列? + +ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。 +LinkedBlockingQueue:是一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。 +SynchronousQueue:是一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。 +PriorityBlockingQueue:一个具有优先级的无限阻塞队列。 + +#### 40.什么是线程安全? + +如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。 + +这个问题有值得一提的地方,就是线程安全也是有几个级别的: + +1)不可变 +像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用 + +2)绝对线程安全 +不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet + +3)相对线程安全 +相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。 + +4)线程非安全 +这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类,点击这里了解为什么不安全。 + +#### 41.Java中如何获取到线程dump文件 + +死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步: + +1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java +2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid + +另外提一点,Thread类提供了一个getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法,因此此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈。 + +#### 42.Java中用到的线程调度算法是什么? + +抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。 + +#### 43.Thread.sleep(0)的作用是什么? + +由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。 + +#### 44.单例模式的线程安全性 + +1)饿汉式单例模式的写法:线程安全 + +2)懒汉式单例模式的写法:非线程安全 + +3)双检锁单例模式的写法:线程安全 + +#### 45.Semaphore有什么作用? + +Semaphore就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。 + +#### 46.Hashtable的size()方法中明明只有一条语句"return count",为什么还要做同步? + +1)同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程同时访问。所以,这样就有问题了,可能线程A在执行Hashtable的put方法添加数据,线程B则可以正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的,可能线程A添加了完了数据,但是没有对size++,线程B就已经读取size了,那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后,意味着线程B调用size()方法只有在线程A调用put方法完毕之后才可以调用,这样就保证了线程安全性 + +2)CPU执行代码,执行的不是Java代码,这点很关键,一定得记住。Java代码最终是被翻译成机器码执行的,机器码才是真正可以和硬件电路交互的代码。即使你看到Java代码只有一行,甚至你看到Java代码编译之后生成的字节码也只有一行,也不意味着对于底层来说这句语句的操作只有一个。一句"return count"假设被翻译成了三句汇编语句执行,一句汇编语句和其机器码做对应,完全可能执行完第一句,线程就切换了。 + +#### 47.同步方法和同步块,哪个是更好的选择? + +同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:同步的范围越小越好。 + +虽说同步的范围越少越好,但是在Java虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说StringBuffer,它是一个线程安全的类,自然最常用的append()方法是一个同步方法,我们写代码的时候会反复append字符串,这意味着要进行反复的加锁->解锁,这对性能不利,因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,因此Java虚拟机会将多次append方法调用的代码进行一个锁粗化的操作,将多次的append的操作扩展到append方法的头尾,变成一个大的同步块,这样就减少了加锁–>解锁的次数,有效地提升了代码执行的效率。 + +#### 48.高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池? +1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换 + +2)并发不高、任务执行时间长的业务要区分开看: +a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务 +b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换 +c)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考其他有关线程池的文章。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。 + +#### 49.在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? + +lock接口在多线程和并发编程中最大的优势是它们为读和写分别提供了锁,它能满足你写像ConcurrentHashMap这样的高性能数据结构和有条件的阻塞。Java线程面试的问题越来越会根据面试者的回答来提问。我强烈建议在你去参加多线程的面试之前认真读一下Locks,因为当前其大量用于构建电子交易终统的客户端缓存和交易连接空间。 + +#### 50.你将如何使用thread dump?你将如何分析Thread dump? + +在UNIX中你可以使用kill -3,然后thread dump将会打印日志,在windows中你可以使用”CTRL+Break”。非常简单和专业的线程面试问题,但是如果他问你怎样分析它,就会很棘手。 + +### 参考资料 + +https://blog.csdn.net/tanmomo/article/details/99671622 +https://blog.csdn.net/cmyperson/article/details/79610870 +https://www.cnblogs.com/toria/p/11234323.html +https://www.cnblogs.com/bors/p/dxc.html +http://ifeve.com/15-java-faq/ diff --git "a/\345\276\256\346\234\215\345\212\241.md" "b/\345\276\256\346\234\215\345\212\241.md" new file mode 100644 index 0000000..ec05da5 --- /dev/null +++ "b/\345\276\256\346\234\215\345\212\241.md" @@ -0,0 +1,196 @@ +## 微服务 + + +* [1.微服务有哪些优缺点?](#1微服务有哪些优缺点) +* [2.作为注册中心,Zookeeper和Eureka有什么区别?](#2作为注册中心zookeeper和eureka有什么区别) +* [3.Service Mesh了解过吗?](#3service-mesh了解过吗) +* [4.微服务有哪些特点?](#4微服务有哪些特点) +* [5.单片,SOA 和微服务架构有什么区别?](#5单片soa-和微服务架构有什么区别) +* [6.Spring Cloud 解决了哪些问题?](#6spring-cloud-解决了哪些问题) +* [7.服务注册和发现是什么意思?Spring Cloud 如何实现?](#7服务注册和发现是什么意思spring-cloud-如何实现) +* [8.Spring Cloud 和dubbo的区别?](#8spring-cloud-和dubbo的区别) +* [9.什么是微服务?](#9什么是微服务) +* [10.微服务之间是如何通讯的?](#10微服务之间是如何通讯的) +* [11.请谈谈对SpringBoot 和SpringCloud的理解](#11请谈谈对springboot-和springcloud的理解) +* [12.什么是服务熔断,什么是服务降级](#12什么是服务熔断什么是服务降级) +* [13.你所知道的微服务技术栈有哪些?](#13你所知道的微服务技术栈有哪些) +* [14.什么是 Eureka服务注册与发现?](#14什么是-eureka服务注册与发现) +* [15.Eureka的基本架构是什么?](#15eureka的基本架构是什么) +* [16.作为服务注册中心,Eureka比Zookeeper好在哪里?](#16作为服务注册中心eureka比zookeeper好在哪里) +* [参考资料](#参考资料) + + +#### 1.微服务有哪些优缺点? + +优点: +独立的可扩展性,每个微服务都可以独立进行横向或纵向扩展,根据业务实际增长情况来进行快速扩展; +独立的可升级性,每个微服务都可以独立进行服务升级、更新,不用依赖于其它服务,结合持续集成工具可以进行持续发布,开发人员就可以独立快速完成服务升级发布流程; +易维护性,每个微服务的代码均只专注于完成该单个业务范畴的事情,因此微服务项目代码数量将减少至IDE可以快速加载的大小,这样可以提高了代码的可读性,进而可以提高研发人员的生产效率; +语言无关性,研发人员可以选用自己最为熟悉的语言和框架来完成他们的微服务项目(当然,一般根据每个公司的实际技术栈需要来了),这样在面对新技术或新框架的选用时,微服务能够更好地进行快速响应; +故障和资源的隔离性,在系统中出现不好的资源操作行为时,例如内存泄露、数据库连接未关闭等情况,将仅仅只会影响单个微服务; +优化跨团队沟通,如果要完全实践微服务架构设计风格,研发团队势必会按照新的原则来进行划分,由之前的按照技能、职能划分的方式变为按照业务(单个微服务)来进行划分,如此这般团队里将有各个方向技能的研发人员,沟通效率上来说要优于之前按照技能进行划分的组织架构; +原生基于“云”的系统架构设计,基于微服务架构设计风格,我们能构建出来原生对于“云”具备超高友好度的系统,与常用容器工具如Docker能够很方便地结合,构建持续发布系统与IaaS、PaaS平台对接,使其能够方便的部署于各类“云”上,如公用云、私有云以及混合云。 + +缺点: +增加了系统复杂性; +运维难度增加; +本地调用变成RPC调用,接口耗时增加; +可能会引入分布式事务。 + +#### 2.作为注册中心,Zookeeper和Eureka有什么区别? + +详见:https://mp.weixin.qq.com/s/B6F9Ea1GnDIou41Po3NMAQ + +#### 3.Service Mesh了解过吗? + +详见:https://www.jianshu.com/p/27a742e349f7 + +#### 4.微服务有哪些特点? + +· 解耦 – 系统内的服务很大程度上是分离的。因此,整个应用程序可以轻松构建,更改和扩展 +· 组件化 – 微服务被视为可以轻松更换和升级的独立组件 +· 业务能力 – 微服务非常简单,专注于单一功能 +· 自治 – 开发人员和团队可以彼此独立工作,从而提高速度 +· 持续交付 – 通过软件创建,测试和批准的系统自动化,允许频繁发布软件 +· 责任 – 微服务不关注应用程序作为项目。相反,他们将应用程序视为他们负责的产品 +· 分散治理 – 重点是使用正确的工具来做正确的工作。这意味着没有标准化模式或任何技术模式。开发人员可以自由选择最有用的工具来解决他们的问题 +· 敏捷 – 微服务支持敏捷开发。任何新功能都可以快速开发并再次丢弃 + +#### 5.单片,SOA 和微服务架构有什么区别? + +· 单片架构类似于大容器,其中应用程序的所有软件组件组装在一起并紧密封装。 +· 一个面向服务的架构(SOA)是一种相互通信服务的集合。通信可以涉及简单的数据传递,也可以涉及两个或多个协调某些活动的服务。 +· 微服务架构是一种架构风格,它将应用程序构建为以业务域为模型的小型自治服务集合。 + +#### 6.Spring Cloud 解决了哪些问题? + +· 与分布式系统相关的复杂性 – 包括网络问题,延迟开销,带宽问题,安全问题。 +· 处理服务发现的能力 – 服务发现允许集群中的进程和服务找到彼此并进行通信。 +· 解决冗余问题 – 冗余问题经常发生在分布式系统中。 +· 负载平衡 – 改进跨多个计算资源(例如计算机集群,网络链接,中央处理单元)的工作负载分布。 +· 减少性能问题 – 减少因各种操作开销导致的性能问题。 + +#### 7.服务注册和发现是什么意思?Spring Cloud 如何实现? + +当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka 服务注册和发现可以在这种情况下提供帮助。由于所有服务都在 Eureka 服务器上注册并通过调用 Eureka 服务器完成查找,因此无需处理服务地点的任何更改和处理。 + +#### 8.Spring Cloud 和dubbo的区别? + +(1)服务调用方式 dubbo是RPC springcloud Rest Api +(2)注册中心,dubbo 是zookeeper springcloud是eureka,也可以是zookeeper +(3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。 + +#### 9.什么是微服务? + +微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分成一组小的服务,每个服务运行在其独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。 服务之间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。 + +从技术维度来说: +微服务化的核心就是将传统的一站式应用,根据业务拆分成一个一个的服务,彻底地去耦合,每一个微服务提供单个业务功能的服务,一个服务做一件事,从技术角度看就是一种小而独立的处理过程,类似进程概念,能够自行单独启动或销毁,拥有自己独立的数据库。 + +#### 10.微服务之间是如何通讯的? + +第一种:远程过程调用(Remote Procedure Invocation) + +直接通过远程过程调用来访问别的service。 +示例:REST、gRPC、Apache、Thrift + +优点: +简单,常见。因为没有中间件代理,系统更简单 + +缺点: +只支持请求/响应的模式,不支持别的,比如通知、请求/异步响应、发布/订阅、发布/异步响应降低了可用性,因为客户端和服务端在请求过程中必须都是可用的 + +第二种:消息 + +使用异步消息来做服务间通信。服务间通过消息管道来交换消息,从而通信。 +示例:Apache Kafka、RabbitMQ + +优点: +把客户端和服务端解耦,更松耦合 提高可用性,因为消息中间件缓存了消息,直到消费者可以消费 +支持很多通信机制比如通知、请求/异步响应、发布/订阅、发布/异步响应 + +缺点: +消息中间件有额外的复杂性 + +#### 11.请谈谈对SpringBoot 和SpringCloud的理解 + +SpringBoot专注于快速方便的开发单个个体微服务。 +SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务 +SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖的关系。 +SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。 +Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 + +#### 12.什么是服务熔断,什么是服务降级 + +服务熔断 +熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。 + +服务降级 +其实就是线程池中单个线程障处理,防止单个线程请求时间太长,导致资源长期被占有而得不到释放,从而导致线程池被快速占用完,导致服务崩溃。 + +Hystrix能解决如下问题: +请求超时降级,线程资源不足降级,降级之后可以返回自定义数据 +线程池隔离降级,分布式服务可以针对不同的服务使用不同的线程池,从而互不影响 +自动触发降级与恢复 +实现请求缓存和请求合并 + +#### 13.你所知道的微服务技术栈有哪些? + +服务开发Springboot、Spring、SpringMVC +服务配置与管理Netflix公司的Archaius、阿里的Diamond等 +服务注册与发现Eureka、Consul、Zookeeper等 +服务调用Rest、RPC、gRPC +服务熔断器Hystrix、Envoy等 +负载均衡Ribbon、Nginx等 +服务接口调用(客户端调用服务的简化工具)Feign等 +消息队列Kafka、RabbitMQ、ActiveMQ等 +服务配置中心管理SpringCloudConfig、Chef等 +服务路由(API网关)Zuul等 +服务监控Zabbix、Nagios、Metrics、Spectator等 +全链路追踪Zipkin,Brave、Dapper等 +服务部署Docker、OpenStack、Kubernetes等 +数据流操作开发包SpringCloud Stream(封装与Redis,Rabbit、Kafka等发送接收消息) +事件消息总线Spring Cloud Bus + +#### 14.什么是 Eureka服务注册与发现? + +Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如Zookeeper。 + +#### 15.Eureka的基本架构是什么? + +Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现(请对比Zookeeper)。 + +Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。 + +而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。SpringCloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。 + +Eureka包含两个组件: Eureka Server 和 Eureka Client + +Eureka Server提供服务注册服务各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到 + +EurekaClient是一个Java客户端用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒) + +#### 16.作为服务注册中心,Eureka比Zookeeper好在哪里? + +著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性P在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。 + +因此,Zookeeper 保证的是CP, Eureka 则是AP。 + +Zookeeper保证CP +当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。 + +Eureka保证AP +Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。 + +除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况: + +Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务 +Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用) +当网络稳定时,当前实例新的注册信息会被同步到其它节点中 +因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。 + + +### 参考资料 +https://www.cnblogs.com/zhuifeng523/p/12294904.html + +https://baijiahao.baidu.com/s?id=1655329799036379323 diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" new file mode 100644 index 0000000..e2aed00 --- /dev/null +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" @@ -0,0 +1,278 @@ +## 数据结构与算法 + + +* [1.什么是算法?](#1什么是算法) +* [2.TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?](#2treemap和treeset在排序时如何比较元素collections工具类中的sort方法如何比较元素) +* [3.如何知道二叉树的深度?](#3如何知道二叉树的深度) +* [4.介绍一下,堆排序的原理是什么?](#4介绍一下堆排序的原理是什么) +* [5.数组和链表的区别](#5数组和链表的区别) +* [6.二分查找了解过吗?](#6二分查找了解过吗) +* [7.说下你熟悉的排序算法](#7说下你熟悉的排序算法) +* [8.布隆过滤器了解过吗?](#8布隆过滤器了解过吗) +* [9.一致性hash算法了解过吗?](#9一致性hash算法了解过吗) +* [10.如何在一个1到100的整数数组中找到丢失的数字?](#10如何在一个1到100的整数数组中找到丢失的数字) +* [11.请你讲讲LRU算法的实现原理?](#11请你讲讲lru算法的实现原理) +* [12.为什么要设计后缀表达式,有什么好处?](#12为什么要设计后缀表达式有什么好处) +* [13. 什么是B树?](#13-什么是b树) +* [14.什么是B+树?](#14什么是b树) +* [15.谈一谈,id全局唯一且自增,如何实现?](#15谈一谈id全局唯一且自增如何实现) +* [参考链接](#参考链接) + + +#### 1.什么是算法? + +算法简单来说就是解决问题的步骤。 + +在Java中,算法通常都是由类的方法来实现的。前面的数据结构,比如链表为啥插入、删除快,而查找慢,平衡的二叉树插入、删除、查找都快,这都是实现这些数据结构的算法所造成的。后面我们讲的各种排序实现也是算法范畴的重要领域。 + +一、算法的五个特征 + +①、`有穷性:对于任意一组合法输入值,在执行又穷步骤之后一定能结束,即:算法中的每个步骤都能在有限时间内完成。` + +②、确定性:在每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行。并且在任何条件下,算法都只有一条执行路径。 + +③、`可行性:算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现之。` + +④、有输入:作为算法加工对象的量值,通常体现在算法当中的一组变量。有些输入量需要在算法执行的过程中输入,而有的算法表面上可以没有输入,实际上已被嵌入算法之中。 + +⑤、`有输出:它是一组与“输入”有确定关系的量值,是算法进行信息加工后得到的结果,这种确定关系即为算法功能。` + +二、算法的设计原则 + +①、正确性:首先,算法应当满足以特定的“规则说明”方式给出的需求。其次,对算法是否“正确”的理解可以有以下四个层次: + +一、程序语法错误。 + +二、程序对于几组输入数据能够得出满足需要的结果。 + +三、程序对于精心选择的、典型、苛刻切带有刁难性的几组输入数据能够得出满足要求的结果。 + +四、程序对于一切合法的输入数据都能得到满足要求的结果。 + +PS:通常以第 三 层意义的正确性作为衡量一个算法是否合格的标准。 + +②、可读性:算法为了人的阅读与交流,其次才是计算机执行。因此算法应该易于人的理解;另一方面,晦涩难懂的程序易于隐藏较多的错误而难以调试。 + +③、健壮性:当输入的数据非法时,算法应当恰当的做出反应或进行相应处理,而不是产生莫名其妙的输出结果。并且,处理出错的方法不应是中断程序执行,而是应当返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。 + +④、高效率与低存储量需求:通常算法效率值得是算法执行时间;存储量是指算法执行过程中所需要的最大存储空间,两者都与问题的规模有关。 + +前面三点 正确性,可读性和健壮性相信都好理解。对于第四点算法的执行效率和存储量,我们知道比较算法的时候,可能会说“A算法比B算法快两倍”之类的话,但实际上这种说法没有任何意义。因为当数据项个数发生变化时,A算法和B算法的效率比例也会发生变化,比如数据项增加了50%,可能A算法比B算法快三倍,但是如果数据项减少了50%,可能A算法和B算法速度一样。所以描述算法的速度必须要和数据项的个数联系起来。也就是“大O”表示法,它是一种算法复杂度的相对表示方式,这里我简单介绍一下,后面会根据具体的算法来描述。 + +相对(relative):你只能比较相同的事物。你不能把一个做算数乘法的算法和排序整数列表的算法进行比较。但是,比较2个算法所做的算术操作(一个做乘法,一个做加法)将会告诉你一些有意义的东西; + +表示(representation):大O(用它最简单的形式)把算法间的比较简化为了一个单一变量。这个变量的选择基于观察或假设。例如,排序算法之间的对比通常是基于比较操作(比较2个结点来决定这2个结点的相对顺序)。这里面就假设了比较操作的计算开销很大。但是,如果比较操作的计算开销不大,而交换操作的计算开销很大,又会怎么样呢?这就改变了先前的比较方式; + +复杂度(complexity):如果排序10,000个元素花费了我1秒,那么排序1百万个元素会花多少时间?在这个例子里,复杂度就是相对其他东西的度量结果。 + +然后我们在说说算法的存储量,包括: + +程序本身所占空间; + +输入数据所占空间; + +辅助变量所占空间; + +一个算法的效率越高越好,而存储量是越低越好。 + +#### 2.TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素? + +TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。 + +#### 3.如何知道二叉树的深度? + +实现二叉树的深度方式有两种,递归以及非递归。 + +①递归实现: + +为了求树的深度,可以先求其左子树的深度和右子树的深度,可以用递归实现,递归的出口就是节点为空。返回值为0; + +②非递归实现: + +利用层次遍历的算法,设置变量level记录当前节点所在的层数,设置变量last指向当前层的最后一个节点,当处理完当前层的最后一个节点,让level指向+1操作。设置变量cur记录当前层已经访问的节点的个数,当cur等于last时,表示该层访问结束。 + +层次遍历在求树的宽度、输出某一层节点,某一层节点个数,每一层节点个数都可以采取类似的算法。 + +树的宽度:在树的深度算法基础上,加一个记录访问过的层节点个数最多的变量max,在访问每层前max与last比较,如果max比较大,max不变,如果max小于last,把last赋值给max; + +#### 4.介绍一下,堆排序的原理是什么? + +堆排序就是把最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束。在堆中定义以下几种操作: + +(1)最大堆调整(Max-Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点。 + +(2)创建最大堆(Build-Max-Heap):将堆所有数据重新排序,使其成为最大堆。 + +(3)堆排序(Heap-Sort):移除位在第一个数据的根节点,并做最大堆调整的递归运算 + +#### 5.数组和链表的区别 + +1、数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。但是如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同样的道理,如果想删除一个元素,同样需要移动大量元素去填掉被移动的元素。如果应用需要快速访问数据,很少或不插入和删除元素,就应该用数组。 + +2、链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。比如:上一个元素有个指针指到下一个元素,以此类推,直到最后一个元素。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表数据结构了。 + +#### 6.二分查找了解过吗? + +##### 查找思路 +【a】待查找有序数组序列:1, 2, 3, 4, 5, 6, 7 + +起始: 定义start = 0 , end = 6, mid = (start + end ) / 2 = (0 + 6) / 2 = 3,arr[mid] = arr[3] = 4 + +【b】假设需要查找"2", 因为2 < arr[mid] = arr[3] = 4; 所以需要将end移动到mid左边一个位置,即end = mid - 1 = 3 - 1 = 2, + +此时重新计算mid = (start +end ) / 2 = (0 + 2) / 2 = 1; arr[mid] = arr[1] = 2 ,继续将2与arr[mid] = arr[1] = 2进行比较,发现相等,成功找到数字"2"所在的位置。 + +【c】假设需要查找"7",因为 7 > arr[mid] = arr[3] = 4,所以需要将start移动到mid右边一个位置,即start = mid + 1 = 4,此时重新计算mid = (start +end) / 2 = (4+ 6)/2 = 5, arr[mid] = arr[5] = 6, 因为7>arr[mid] = arr[5] = 6,所以还是需要将start移动到mid右边一个位置,即start = mid + 1 = 5 + 1 = 6, 此时重新计算mid = (start +end) / 2 = (6 + 6) / 2 = 6.arr[6] = 7, 此时arr[mid] = arr[6] = 7,刚好等于待查找数字7,说明成功找到数字"7"所在的位置. + + 【d】假设查找"0", 因为 0 < arr[mid] = arr[3] = 4, 所以需要将end移动到mid左边一个位置,即end = mid - 1 = 3 - 1 = 2,此时重新计算mid = (start +end) / 2 = (0 + 2) / 2 = 1,arr[mid] = arr[1] = 2, 因为0 < arr[mid] = arr[1] = 2,所以需要将end移动到mid左边一个位置,即end = mid - 1 = 1 - 1 = 0, 此时mid = (start +end) / 2 = (0 + 0) / 2 = 0,arr[mid] = arr[0] = 1,因为0 < arr[mid] = arr[0] = 1,所以需要将end移动到mid左边一个位置,即end = mid - 1 = 0 - 1 = -1 ,因为此时start = 0, end = -1,start >end,即start 已经大于end结束位置,说明没有找到相应的元素0。 + +##### 算法实现 + +```java +public class BinarySearchUtils { + + /** + * 根据指定值查找在数组中的位置 + * + * @param arr 待查找有序数组 + * @param value 指定值 + * @return 返回值在数组中对应的下标位置 + */ + public static int binarySearch(int[] arr, int value) { + //起始位置 + int start = 0; + //结束位置 + int end = arr.length - 1; + + while (true) { + //计算中间位置下标 + int mid = (start + end) / 2; + //中间值 + int midValue = arr[mid]; + + if (value == midValue) { + return mid; + } else { + //待查找数值比中间值小,需要将end = mid - 1 + if (midValue > value) { + end = mid - 1; + } else { + //待查找数值比中间值大,需要将start = mid + 1 + start = mid + 1; + } + } + + if (start > end) { + //start > end,说明未找到相应的元素,返回-1 + return -1; + } + } + } + +} +``` + +#### 7.说下你熟悉的排序算法 + +详见:https://www.cnblogs.com/onepixel/articles/7674659.html + +#### 8.布隆过滤器了解过吗? + +详见:https://www.cnblogs.com/liyulong1982/p/6013002.html + +#### 9.一致性hash算法了解过吗? + +详见:https://mp.weixin.qq.com/s/bCH-aU8cKS3uT6PwRYNJtA + +#### 10.如何在一个1到100的整数数组中找到丢失的数字? + +如果是丢了一个数字,用个遍历把这些数字累加求和, + +然后用 (1 + 100)*100/2 减去这个累加的总和,就是少的那一个数. + +如果是丢了一些数字, + +方法一: + +先 1-100 遍历 创建一个字典,key为1-100,值默认都为NO。 + +然后把那一些数字作为一个数组,判断是否包含每一个key,包含那key,则那key的值改为YES, + +最后值为NO的数则为缺失的数字 + +方法二: + +先排序,并创建一个用来装缺失数的空数组,排好序后遍历,最大的数用101减,其余用后一个值减去前一个值如果差值不是1而是为n,就把被减数分别加1到(n-1)得出的数保存下来就是缺少的数字 + +#### 11.请你讲讲LRU算法的实现原理? + +①LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也很高”,反过来说“如果数据最近这段时间一直都没有访问,那么将来被访问的概率也会很低”,两种理解是一样的;常用于页面置换算法,为虚拟页式存储管理服务。 + +②达到这样一种情形的算法是最理想的:每次调换出的页面是所有内存页面中最迟将被使用的;这可以最大限度的推迟页面调换,这种算法,被称为理想页面置换算法。可惜的是,这种算法是无法实现的。 为了尽量减少与理想算法的差距,产生了各种精妙的算法,最近最少使用页面置换算法便是其中一个。LRU 算法的提出,是基于这样一个事实:在前面几条指令中使用频繁的页面很可能在后面的几条指令中频繁使用。反过来说,已经很久没有使用的页面很可能在未来较长的一段时间内不会被用到 。这个,就是著名的局部性原理——比内存速度还要快的cache,也是基于同样的原理运行的。因此,我们只需要在每次调换时,找到最近最少使用的那个页面调出内存。 + +#### 12.为什么要设计后缀表达式,有什么好处? + +后缀表达式又叫逆波兰表达式,逆波兰记法不需要括号来标识操作符的优先级。 + +#### 13. 什么是B树? + +B树是一种多叉树,也叫多路搜索树,适合用于文件索引上,减少磁盘IO次数,子节点存储最大数成为B树的阶,图中为2-3树。 + +m阶B树特点: + +非叶节点最多有m棵子树。 +根节点最少有两棵子树,非根非叶节点最少有m/2棵子树。 +非叶节点保存的关键字个数等于该节点子树个数-1。 +非叶节点保存的关键字大小有序。 +节点中每个关键字左子树的关键字都小于该该关键字,右子树的关键字都大于该该关键字。 +所有叶节点都在同一层。 +查找: + +对节点关键字进行二分查找。 +如果找不到,进入对应的子树进行二分查找,如此循环。 + +#### 14.什么是B+树? + +B树的变种,拥有B树的特点 + +独有特点: + +节点中的关键字与子树数目相同。 +关键字对应的子树节点都大于等于该关键字,子树包含该关键字自身。 +所有关键字都出现在叶节点之中。 +所有叶节点都有指向下一个叶节点的指针。 +搜索:只在叶节点搜索。 + +叶子节点保存关键字和对应的数据,非叶节点只保存关键字和指向叶节点的指针,同等关键字数量的B树和B+树,B+树更小。 + +更适合做索引系统,原因: + +由于叶节点有指针项链,B+树更适合做范围检索。 +由于非叶节点只保存关键字和指向叶节点的指针,B+树可以容纳更多的关键字,树层数变小,磁盘查询次数更低。 +B+树的查询效率比较稳定,查询所有关键字的路径相同。(MySQL索引就提供了B+树的实现方式) + +#### 15.谈一谈,id全局唯一且自增,如何实现? + +SnowFlake雪花算法 + +雪花ID生成的是一个64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成: + +snowflake id生成规则 + +1位标识符:始终是0,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。 + +41位时间戳:41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。 + +10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成。 + +12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号 + +#### 参考链接 + +https://zhuanlan.zhihu.com/p/150340528?from_voters_page=true + +https://blog.csdn.net/Mr_BJL/article/details/88073694 + +https://blog.csdn.net/chenshiyang0806/article/details/90744039 + +https://blog.csdn.net/Weixiaohuai/article/details/83188174 diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227.md" new file mode 100644 index 0000000..5da2330 --- /dev/null +++ "b/\346\266\210\346\201\257\351\230\237\345\210\227.md" @@ -0,0 +1,228 @@ +## 消息队列 + + +* [1.消息队列有哪些应用场景?](#1消息队列有哪些应用场景) +* [2.消息队列的弊端有哪些?](#2消息队列的弊端有哪些) +* [3.使用消息队列,怎么确保消息不丢失?](#3使用消息队列怎么确保消息不丢失) +* [4.使用消息队列,如果处理重复消息?](#4使用消息队列如果处理重复消息) +* [5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性?](#5kafka的消息是有序的吗如果保证kafka消息的顺序性) +* [6.消息如何保证幂等性](#6消息如何保证幂等性) +* [7.消息队列积压怎么办](#7消息队列积压怎么办) +* [8.各种MQ的比较](#8各种mq的比较) +* [9.如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时怎么解决?](#9如何解决消息队列的延时以及过期失效问题消息队列满了以后该怎么处理有几百万消息持续积压几小时怎么解决) +* [10.为什么使用消息队列?](#10为什么使用消息队列) +* [参考链接](#参考链接) + + +#### 1.消息队列有哪些应用场景? + +异步处理、流量控制、服务解耦、消息广播 + +#### 2.消息队列的弊端有哪些? + +数据延迟;增加系统复杂度;可能产生数据不一致的问题。 + +#### 3.使用消息队列,怎么确保消息不丢失? + +在生产阶段,你需要捕获消息发送的错误,并重发消息。 在存储阶段,你可以通过配置刷盘和复制相关的参数,让消息写入到多个副本的磁盘上,来确保消息不会因为某个 Broker 宕机或者磁盘损坏而丢失。 在消费阶段,你需要在处理完全部消费业务逻辑之后,再发送消费确认。 + +#### 4.使用消息队列,如果处理重复消息? + +1)利用数据库的唯一约束实现幂等 2)为更新的数据设置前置条件(CAS) 3)记录并检查操作(在发送消息时,给每条消息指定一个全局唯一的 ID,消费时,先根据这个 ID 检查这条消息是否有被消费过,如果没有消费过,才更新数据,然后将消费状态置为已消费。) + +#### 5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性? + +Kafka只能保证局部有序,即只能保证一个分区里的消息有序。而其具体实现是通过生产者为每个分区的消息维护一个发送队列,我们需要将保证顺序的消息都发送到同一个分区中。并且由于Kafka会同时发送多个消息,所以还需指定max.in.flight.requests.per.connection为1,保证前一个消息发送成功,后一个消息才开始发送。 + +#### 6.消息如何保证幂等性 + +例如kafka的offset可能是消费者批量处理后才提交到zk,重启后再消费时就可能会收到重复消息,需要消费者在处理消息时做幂等性设计,即先判断是否消费过,把已消费的放到本地缓存或者redis中,每次消费时先做个判断即可。 + +#### 7.消息队列积压怎么办 + +当消费者出现异常,很容易引起队列积压,如果一秒钟1000个消息,那么一个小时就是几千万的消息积压,是非常可怕的事情,但是生产线上又有可能会出现; + +当消息积压来不及处理,rabbitMQ如果设置了消息过期时间,那么就有可能由于积压无法及时处理而过期,这消息就被丢失了; + +解决方法如下: + +> - 不建议在生产环境使用数据过期策略,一是数据是否丢失无法控制,二是一旦积压就很有可能丢失;建议数据的处理都有代码来控制; +> - 当出现消息积压时,做法就是临时扩大consumer个数,让消息快速消费,一般都是通过业务逻辑的手段来完成:如下: + +【rabbitmq解决积压范例】 + +> > 1. 修复consumer代码故障,确保consumer逻辑正确可以消费; +> > 2. 停止consumer,开启10倍20倍的queue个数; +> > \* 创建一个临时的consumer程序,消费积压的queue,并把消息写入到扩建的10倍queue中; +> > \* 再开启10倍20倍的consumer对新的扩充后队列进行消费; +> > \* 这种做法相当于通过物理资源扩充了10倍来快速消费; + +> > 1. 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费; + +【kafka解决范例】 + +> > 1. 修复consumer代码故障,确保consumer逻辑正确可以消费; +> > 2. 停止consumer,新建topic,新建10倍20倍的partition个数; +> > \* 创建对应原topic的partition个数的临时的consumer程序,消费原来的topic,并把消息写入到扩建的新topic中; +> > \* 再开启对应新partition个数的consumer对新的topic进行消费; +> > \* 这种做法相当于通过物理资源扩充了10倍来快速消费; + +> > 1. 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费; + +#### 8.各种MQ的比较 + +| 特性 | activeMQ | rabbitMQ | rocketMQ | kafka | +| ------------------- | ------------------------ | ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 单机吞吐量 | 万/秒 | 万/秒 | 10万/秒 | 10万/秒 | +| topic对吞吐量的影响 | 无 | 无 | topic达到几百/几千个级别,吞吐量会有小幅下降; 这是rocket的最大优势 所以非常适用于支撑大批量topic场景 | topic可以达到几十/几百个级别,吞吐量会有大幅下降 kafka不适用大批量topic场景,除非加机器 | +| 时效性 | 毫秒 | 微秒 这是rabbit 最大优势,延迟低 | 毫秒 | 毫秒 | +| 可用性 | 高。主从架构 | 高。主从架构 | 非常高。分布式。 | 非常高。分布式。数据多副本,不会丢数据,不会不可用。 | +| 可靠性 | 有较低概率丢失数据 | ---- | 经配置优化可达到0丢失 | 经配置优化可达到0丢失 | +| 功能特性 | 功能齐全,但已不怎么维护 | erlang开发,并发强,性能极好,延迟低 | MQ功能较为齐全,扩展好 | 功能简单,主要用于大数据实时计算和日志采集,事实标准 | + +综上,总结如下: + +------ + +【activeMQ】 + +> - 优点:技术成熟,功能齐全,历史悠久,有大量公司在使用 +> - 缺点:偶尔会有较低概率丢失数据,而且社区已经不怎么维护5.15.X版本 +> - 使用场景:主要用于系统解耦和异步处理,不适用与大数据量吞吐情况。互联网公司很少适用 + +------ + +【rabitMQ】 + +> - 优点:吞吐量高,功能齐全,管理界面易用,社区活跃,性能极好,; +> - 缺点:吞吐量只是万级,erlang难以二次开发和掌控;集群动态扩展非常麻烦; +> - 使用场景:吞吐量不高而要求低延迟,并且不会频繁调整和扩展的场景。非常适合国内中小型互联网公司适用,因为管理界面非常友好,可以在界面进行配置和优化/集群监控。 + +------ + +【rocketMQ】 + +> - 优点:支持百千级大规模topic。吞吐量高(十万级,日处理上百亿)。接口易用。,分布式易扩展,阿里支持。java开发易于掌控。 +> - 缺点:与阿里(社区)存在绑定。不兼容JMS规范。 +> - 使用场景:高吞吐量 + +------ + +【kafka】 + +> - 优点:超高吞吐量,超高可用性和可靠性,分布式易扩展 +> - 缺点:topic支持少,MQ功能简单,消息可能会重复消费影响数据精确度 +> - 使用场景:超高吞吐量场景而数据精确度没那么高,天然适合大数据实时计算和日志采集场景 + +#### 9.如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时怎么解决? + +(一)、大量消息在mq里积压了几个小时了还没解决 + +几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多 +这个是我们真实遇到过的一个场景,确实是线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不能在面试的时候说吧。 + +一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条,所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。 + +一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下: + +先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉。 +新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。 +然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。 +接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。 +这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。 +等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息。 +(二)、消息队列过期失效问题 + +假设你用的是rabbitmq,rabbitmq是可以设置过期时间的,就是TTL,如果消息在queue中积压超过一定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。 + +这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。 + +这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入mq里面去,把白天丢的数据给他补回来。也只能是这样了。 + +假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个订单给查出来,手动发到mq里去再补一次。 + +(三)、消息队列满了怎么搞? + +如果走的方式是消息积压在mq里,那么如果你很长时间都没处理掉,此时导致mq都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。 + +#### 10.为什么使用消息队列? + +##### 面试官心理分析 + +其实面试官主要是想看看: + +- 第一,你知不知道你们系统里为什么要用消息队列这个东西? + + 不少候选人,说自己项目里用了 Redis、MQ,但是其实他并不知道自己为什么要用这个东西。其实说白了,就是为了用而用,或者是别人设计的架构,他从头到尾都没思考过。 + + 没有对自己的架构问过为什么的人,一定是平时没有思考的人,面试官对这类候选人印象通常很不好。因为面试官担心你进了团队之后只会木头木脑的干呆活儿,不会自己思考。 + +- 第二,你既然用了消息队列这个东西,你知不知道用了有什么好处&坏处? + + 你要是没考虑过这个,那你盲目弄个 MQ 进系统里,后面出了问题你是不是就自己溜了给公司留坑?你要是没考虑过引入一个技术可能存在的弊端和风险,面试官把这类候选人招进来了,基本可能就是挖坑型选手。就怕你干 1 年挖一堆坑,自己跳槽了,给公司留下无穷后患。 + +- 第三,既然你用了 MQ,可能是某一种 MQ,那么你当时做没做过调研? + + 你别傻乎乎的自己拍脑袋看个人喜好就瞎用了一个 MQ,比如 Kafka,甚至都从没调研过业界流行的 MQ 到底有哪几种。每一个 MQ 的优点和缺点是什么。每一个 MQ 没有绝对的好坏,但是就是看用在哪个场景可以扬长避短,利用其优势,规避其劣势。 + + 如果是一个不考虑技术选型的候选人招进了团队,leader 交给他一个任务,去设计个什么系统,他在里面用一些技术,可能都没考虑过选型,最后选的技术可能并不一定合适,一样是留坑。 + +##### 面试题剖析 + +其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么? + +面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这个业务场景有个什么技术挑战,如果不用 MQ 可能会很麻烦,但是你现在用了 MQ 之后带给了你很多的好处。 + +先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦、异步、削峰。 + +##### 解耦 + +看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃...... + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223436765-607649977.jpg) + + + +在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊! + +如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。 + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223437000-220196142.jpg) + +总结:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。 + +面试技巧:你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。在简历中体现出来这块东西,用 MQ 作解耦。 + +##### 异步 + +再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。 + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223437240-841013337.jpg) + +一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。 + +如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快 + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223437468-1330530320.jpg) + +##### 削峰 + +每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL 的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。 + +一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,导致系统崩溃,用户也就没法再使用系统了。 + +但是高峰期一过,到了下午的时候,就成了低峰期,可能也就 1w 的用户同时在网站上操作,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力。 + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223437702-1402821528.jpg) + +如果使用 MQ,每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。而 MQ 每秒钟 5k 个请求进来,就 2k 个请求出去,结果就导致在中午高峰期(1 个小时),可能有几十万甚至几百万的请求积压在 MQ 中。 + +![file](https://img2018.cnblogs.com/blog/1756639/201909/1756639-20190908223437928-344432831.jpg) + +这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是 A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给解决掉。 + +### 参考链接 + +https://www.jianshu.com/p/88a4da652e23 + +https://blog.csdn.net/qq_41701956/article/details/103276267 diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" new file mode 100644 index 0000000..48d98aa --- /dev/null +++ "b/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" @@ -0,0 +1,240 @@ +## 计算机网络 + + +* [1.请简述TCP/UDP的区别](#1请简述tcpudp的区别) +* [2.TCP对应的协议和UDP对应的协议](#2tcp对应的协议和udp对应的协议) +* [3.有哪些私有(保留)地址?](#3有哪些私有保留地址) +* [4.你能说一说OSI七层模型?](#4你能说一说osi七层模型) +* [5.说一说TCP/IP四层模型](#5说一说tcpip四层模型) +* [6. 简述IP地址的分类?](#6-简述ip地址的分类) +* [7.简述ARP地址解析协议工作原理](#7简述arp地址解析协议工作原理) +* [8.简述ICMP、TFTP、HTTP、NAT、DHCP协议](#8简述icmptftphttpnatdhcp协议) +* [9.说一说TCP的三次握手](#9说一说tcp的三次握手) +* [10.为什么TCP要三次握手](#10为什么tcp要三次握手) +* [11.TCP建立连接时为什么要传回 SYN](#11tcp建立连接时为什么要传回-syn) +* [12.TCP为什么要四次挥手](#12tcp为什么要四次挥手) +* [13.滑动窗口和流量控制](#13滑动窗口和流量控制) +* [14.拥塞控制](#14拥塞控制) +* [15.在浏览器中输入url地址到显示主页的过程](#15在浏览器中输入url地址到显示主页的过程) +* [16.HTTP协议包括哪些请求?](#16http协议包括哪些请求) +* [参考链接](#参考链接) + + +#### 1.请简述TCP/UDP的区别 + +TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。 +两者的区别大致如下: + +- TCP面向连接,UDP面向非连接即发送数据前不需要建立链接 +- TCP提供可靠的服务(数据传输),UDP无法保证 +- TCP面向字节流,UDP面向报文 +- TCP数据传输慢,UDP数据传输快 +- TCP提供一种面向连接的、可靠的字节流服务 +- 在一个TCP连接中,仅有两方进行彼此通信,因此广播和多播不能用于TCP +- TCP使用校验和,确认和重传机制来保证可靠传输 +- TCP使用累积确认 +- TCP使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制 + + + +#### 2.TCP对应的协议和UDP对应的协议 + +TCP对应的协议: + +- FTP:定义了文件传输协议,使用21端口。 +- Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。 +- SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。 +- POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。 +- HTTP:是从Web服务器传输超文本到本地浏览器的传送协议。 + +UDP对应的协议: + +- DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。 +- SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。 +- TFTP(Trival File TransferProtocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。 + +#### 3.有哪些私有(保留)地址? + +A类:10.0.0.0 - 10.255.255.255 + +B类:172.16.0.0 - 172.31.255.255 + +C类:192.168.0.0 - 192.168.255.255 + +#### 4.你能说一说OSI七层模型? + +![img](https://pic3.zhimg.com/80/v2-2af488004591cbc12cd82c44518523de_720w.jpg) + +#### 5.说一说TCP/IP四层模型 + +如果你不了解,请直接点击阅读:[TCP/IP四层模型](https://link.zhihu.com/?target=http%3A//www.cnblogs.com/BlueTzar/articles/811160.html) + + +#### 6. 简述IP地址的分类? + +IP地址分为网络号和主机号, A类地址的前8位是网络地址,B类地址的前16位是网络地址,C类地址的前24位是网络地址。 + +A类地址: 1.0.0.0~126.0.0.0 + +B类地址:128.0.0.0 ~ 191.255.255.255 + +C类地址:192.0.0.0 ~ 223.255.255.255 + +D类地址:224.0.0.0 ~ 239.255.255.255 (作为多播使用) + +E类地址:保留 + +A,B,C是基本类,D、E类作为多播和保留使用。主机号,全0的是网络号,主机号全1的是广播地址。 + +#### 7.简述ARP地址解析协议工作原理 + +首先, 每个主机会在自己的ARP缓冲区简历一个ARP列表,以表示IP地址和MAC地址之间的对应关系。 + +当源主机要发送数据时,首先检查自己的ARP列表中是否有对应的目的主机的MAC地址,如果有就直接发送数据,如果没有,就向本网段的所有的主机发送ARP数据包, 该数据包括的内容由:源主机IP地址,源主机的MAC地址,目的主机的IP地址 + +当本网络的所有主机收到ARP数据包时,首先检查数据包中的IP地址是否是自己的IP地址,如果不是,则忽略该数据包,如果是,则首先从数据包中取出源主机的IP和MAC地址写入到ARP列表中,如果已经存在,则覆盖,然后将自己的MAC地址中放入到ARP响应包中,告诉源主机自己是它想找的MAC地址。 + +源主机接收到ARP响应包后,将目的主机的IP和MAC地址写入到ARP列表,并利用此消息发送数据。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。 + +广播发送ARP请求,单播发送ARP响应。 + +#### 8.简述ICMP、TFTP、HTTP、NAT、DHCP协议 + +ICMP : 因特网控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息 + +TFTP:是TCP/IP协议族中的一个用来在客户机和服务器之间进行简单的文件传输的协议,提供不复杂、开销不大的文件传输服务 + +HTTP:超文本传输层协议,是一个属于应用层的面向对象的协议 + +NAT协议:网络地址转换接入广域网(WAN)技术,是一种将私有地址转换为合法IP地址的转换技术 + +DHCP协议:动态主机配置协议,使用UDP协议工作。给内部的网络和网络服务供应商自动的分配IP地址。 + +RARP是逆地址解析协议,作用是完成从硬件地址到IP地址的映射,RARP只能用于具有广播能力的网络。封装一个RARP的数据包里面有MAC地址, 然后广播到网络上,当服务器收到请求包后,就查找对应的MAC地址的IP地址装入到响应报文中发送给请求者。 + +一些常见的端口号及其用途: + +TCP 21端口 : FTP 文件传输服务 + +TCP 23 端口:TELNET 终端仿真服务 + +TCP 25端口:SMTP简单邮件传输服务 + +UDP 53端口:DNS域名解析服务 + +TCP 80端口:HTTP超文本传输服务 + +TCP 109端口:POP2邮局协议2 + +TCP 110端口 : POP3邮局协议版本3使用的端口 + +UDP 69 端口:TFTP 简单文件传输协议 + +3306:Mysql端口号 + +#### 9.说一说TCP的三次握手 + +在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换TCP窗口大小信息 + +![图片描述](https://segmentfault.com/img/bVTyxj?w=600&h=669) + +- 核心思想:让双方都证实对方能发收。知道对方能收是因为收到对方的因为收到信息之后发的回应(ACK)。 +- 客户端–发送带有 SYN 标志的数据包–一次握手–服务端 +- 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端 +- 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端 + +#### 10.为什么TCP要三次握手 + +三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。 + +第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常 + +第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常 + +第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常 + +所以三次握手就能确认双发收发功能都正常,缺一不可。 + +#### 11.TCP建立连接时为什么要传回 SYN + +接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。 + +> SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。 + + +#### 12.TCP为什么要四次挥手 + +任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。 + +举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。 + +上面讲的比较概括,推荐一篇讲的比较细致的文章:https://blog.csdn.net/qzcsu/article/details/72861891 + + + +#### 13.滑动窗口和流量控制 + +TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 + + + +#### 14.拥塞控制 + +在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。 + +为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。 + +TCP的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。 + +- 慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。 +- 拥塞避免: 拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送放的cwnd加1. +- 快重传与快恢复: 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。  当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。 + +#### 15.在浏览器中输入url地址到显示主页的过程 + +百度好像最喜欢问这个问题。 + +> 打开一个网页,整个过程会使用哪些协议 + +图解(图片来源:《图解HTTP》): + +![img](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/url%E8%BE%93%E5%85%A5%E5%88%B0%E5%B1%95%E7%A4%BA%E5%87%BA%E6%9D%A5%E7%9A%84%E8%BF%87%E7%A8%8B.jpg) + +总体来说分为以下几个过程: + +1. DNS解析 +2. TCP连接 +3. 发送HTTP请求 +4. 服务器处理请求并返回HTTP报文 +5. 浏览器解析渲染页面 +6. 连接结束 + +具体可以参考下面这篇文章: + +- https://segmentfault.com/a/1190000006879700 + + +#### 16.HTTP协议包括哪些请求? + +- GET:对服务器资源的简单请求 +- POST:用于发送包含用户提交数据的请求 +- HEAD:类似于GET请求,不过返回的响应中没有具体内容,用于获取报头 +- PUT:传说中请求文档的一个版本 +- DELETE:发出一个删除指定文档的请求 +- TRACE:发送一个请求副本,以跟踪其处理进程 +- OPTIONS:返回所有可用的方法,检查服务器支持哪些方法 +- CONNECT:用于ssl隧道的基于代理的请求 + + + +### 参考链接 + +https://blog.csdn.net/sdgihshdv/article/details/79503274 + +https://segmentfault.com/a/1190000010819141 + +https://blog.csdn.net/qq_29869043/article/details/82812986 + +https://zhuanlan.zhihu.com/p/24001696 + +https://www.cnblogs.com/wuwuyong/p/12198928.html diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217.md" new file mode 100644 index 0000000..0f868c3 --- /dev/null +++ "b/\350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -0,0 +1,378 @@ +## 设计模式 + + +* [1.接口是什么?为什么要使用接口而不是直接使用具体类?](#1接口是什么为什么要使用接口而不是直接使用具体类) +* [2.设计模式六大原则?](#2设计模式六大原则) +* [3.Java怎么实现单例模式?](#3java怎么实现单例模式) +* [4.什么是代理模式?什么是动态代理?Java中动态代理有哪些实现方式?](#4什么是代理模式什么是动态代理java中动态代理有哪些实现方式) +* [5.设计模式的类型](#5设计模式的类型) +* [6.说说你所熟悉或听说过的 j2ee 中的几种常用模式?](#6说说你所熟悉或听说过的-j2ee-中的几种常用模式) +* [7.简述一下你了解的 Java 设计模式(总结)](#7简述一下你了解的-java-设计模式总结) +* [8.适配器模式是什么?什么时候使用?](#8适配器模式是什么什么时候使用) +* [9.适配器模式与装饰器模式有什么区别?](#9适配器模式与装饰器模式有什么区别) +* [10.适配器模式和代理模式之间有什么不同?](#10适配器模式和代理模式之间有什么不同) +* [11.什么是模板方法模式?试举例说明。](#11什么是模板方法模式试举例说明) +* [12.OOP中的组合、聚合和关联有什么区别?](#12oop中的组合聚合和关联有什么区别) +* [13.给我一个符合开闭原则的设计模式的例子?](#13给我一个符合开闭原则的设计模式的例子) +* [14.工厂模式与抽象工厂模式的区别?](#14工厂模式与抽象工厂模式的区别) +* [15.举出一个例子,在这种情况你会更倾向于使用抽象类,而不是接口?](#15举出一个例子在这种情况你会更倾向于使用抽象类而不是接口) +* [16.Dubbo 源码使用了哪些设计模式?](#16dubbo-源码使用了哪些设计模式) +* [17.Spring 当中用到了哪些设计模式?](#17spring-当中用到了哪些设计模式) +* [参考链接](#参考链接) + + +#### 1.接口是什么?为什么要使用接口而不是直接使用具体类? + +接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以使用可随机访问的 ArrayList,也可以使用方便插入和删除的 LinkedList。接口中不允许写代码,以此来保证抽象,但是 Java 8 中你可以在接口声明静态的默认方法,这种方法是具体的。 + +#### 2.设计模式六大原则? + +1、开闭原则(Open Close Principle) + +开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 + +2、里氏代换原则(Liskov Substitution Principle) + +里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 + +3、依赖倒转原则(Dependence Inversion Principle) + +这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。 + +4、接口隔离原则(Interface Segregation Principle) + +这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。 + +5、迪米特法则(最少知道原则)(Demeter Principle) + +为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 + +6、合成复用原则(Composite Reuse Principle) + +原则是尽量使用合成/聚合的方式,而不是使用继承 + +#### 3.Java怎么实现单例模式? + +- 懒汉式:懒加载,线程不安全 + +```java +public class Singleton +{ + private static Singleton singleton; + + private Singleton() + { + } + + public static Singleton getInstance() + { + if (singleton == null) + singleton = new Singleton(); + return singleton; + } +} + +``` + +- 懒汉式线程安全版:同步效率低 + +```java +public class Singleton +{ + private static Singleton singleton; + + private Singleton() + { + } + + public synchronized static Singleton getInstance() + { + if (singleton == null) + singleton = new Singleton(); + return singleton; + } +} + +``` + +- 饿汉式: + +```java +public class Singleton +{ + private static Singleton singleton = new Singleton(); + + private Singleton() + { + } + + public static Singleton getInstance() + { + return singleton; + } +} + +``` + +- 饿汉式变种: + +```java +public class Singleton +{ + private static Singleton singleton; + static + { + singleton = new Singleton(); + } + + private Singleton() + { + } + + public static Singleton getInstance() + { + return singleton; + } +} + +``` + +- 静态内部类方式:利用 JVM 的加载机制,当使用到 SingletonHolder 才会进行初始化。 + +```java +public class Singleton +{ + private Singleton() + { + } + + private static class SingletonHolder + { + private static final Singleton singleton = new Singleton(); + } + + public static Singleton getInstance() + { + return SingletonHolder.singleton; + } +} + +``` + +- 枚举: + +```java +public enum Singletons +{ + INSTANCE; + // 此处表示单例对象里面的各种方法 + public void Method() + { + } +} +``` + +- 双重校验锁: + +```java +public class Singleton +{ + private volatile static Singleton singleton; + + private Singleton() + { + } + + public static Singleton getInstance() + { + if (singleton == null) + { + synchronized (Singleton.class) + { + if (singleton == null) + { + singleton = new Singleton(); + } + } + } + return singleton; + } +} + +``` + +#### 4.什么是代理模式?什么是动态代理?Java中动态代理有哪些实现方式? + +详见:https://www.cnblogs.com/qlqwjy/p/7550609.html + +#### 5.设计模式的类型 + +根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。 + +| 序号 | 模式 & 描述 | 包括 | +| :--- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| 1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern) | +| 2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式(Flyweight Pattern)代理模式(Proxy Pattern) | +| 3 | 行为型模式 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern)命令模式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式(Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式(State Pattern)空对象模式(Null Object Pattern)策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern) | +| 4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern)业务代表模式(Business Delegate Pattern)组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)前端控制器模式(Front Controller Pattern)拦截过滤器模式(Intercepting Filter Pattern)服务定位器模式(Service Locator Pattern)传输对象模式(Transfer Object Pattern) | + +#### 6.说说你所熟悉或听说过的 j2ee 中的几种常用模式? + +IO 流的装饰器模式,Web 过滤器的责任链模式,Spring 的单例模式和工厂模式, +Spring 中根据不同配置方式进行初始化的策略模式 + +#### 7.简述一下你了解的 Java 设计模式(总结) + +标星号的为常用设计模式 + +``` +★单例模式:保证某个类只能有一个唯一实例,并提供一个全局的访问点。 +★简单工厂:一个工厂类根据传入的参数决定创建出那一种产品类的实例。 +工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。 +抽象工厂:创建一组相关或依赖对象族,比如创建一组配套的汉堡可乐鸡翅。 +★建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造,最后再build。 +★原型模式:通过复制现有的实例来创建新的实例,减少创建对象成本(字段需要复杂计算或者创建成本高)。 + +★适配器模式:将一个类的方法接口转换成我们希望的另外一个接口。 +★组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。(无限层级的知识点树) +★装饰模式:动态的给对象添加新的功能。 +★代理模式:为对象提供一个代理以增强对象内的方法。 +亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象(Integer中的少量缓存)。 +★外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。 +桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化(比如插座和充电器,他们之间相插是固定的, +但是至于插座是插在220V还是110V,充电器是充手机还是pad可以自主选择)。 + +★模板方法模式:定义一个算法步骤,每个小步骤由子类各自实现。 +解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。 +★策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。 +★状态模式:允许一个对象根据其内部状态改变而改变它的行为。 +★观察者模式:被观测的对象发生改变时通知它的所有观察者。 +备忘录模式:保存一个对象的某个状态,以便在适当的时候恢复对象。 +中介者模式:许多对象利用中介者来进行交互,将网状的对象关系变为星状的(最少知识原则)。 +命令模式:将命令请求封装为一个对象,可用于操作的撤销或重做。 +访问者模式:某种物体的使用方式是不一样的,将不同的使用方式交给访问者,而不是给这个物体。(例如对铜的使用,造币厂 +造硬币。雕刻厂造铜像,不应该把造硬币和造铜像的功能交给铜自己实现,这样才能解耦) +★责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链, +并且沿着这条链传递请求,直到有对象处理它为止。 +迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。 +``` + +#### 8.适配器模式是什么?什么时候使用? + +适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一些接口,你就可以写一个适配去来连接这些接口。 + +#### 9.适配器模式与装饰器模式有什么区别? + +虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加新的功能。 + +装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者模提供了比继承更有弹性的替代方案。 +通俗的解释:装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。 + +适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。 +适配器模式有三种:类的适配器模式、对象的适配器模式、接口的适配器模式。 +通俗的说法:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。 + +举例如下: + +1、适配器模式 + +```java +//file 为已定义好的文件流 +FileInputStream fileInput = new FileInputStream(file); +InputStreamReader inputStreamReader = new InputStreamReader(fileInput); +``` + +以上就是适配器模式的体现,FileInputStream是字节流,而并没有字符流读取字符的一些api,因此通过InputStreamReader将其转为Reader子类,因此有了可以操作文本的文件方法。 + +2、装饰者模式 + +BufferedReader bufferedReader=new BufferedReader(inputStreamReader);构造了缓冲字符流,将FileInputStream字节流包装为BufferedReader过程就是装饰的过程,刚开始的字节流FileInputStream只有read一个字节的方法,包装为inputStreamReader后,就有了读取一个字符的功能,在包装为BufferedReader后,就拥有了read一行字符的功能。 + +#### 10.适配器模式和代理模式之间有什么不同? + +这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便支持分配、控制或智能访问。 + +#### 11.什么是模板方法模式?试举例说明。 + +详见:https://www.cnblogs.com/adamjwh/p/10919149.html + +#### 12.OOP中的组合、聚合和关联有什么区别? + +如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中的两种形式的关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一个的拥有者,而聚合则是指一个对象使用另一个对象。如果对象 A 是由对象 B 组合的,则 A 不存在的话,B一定不存在,但是如果 A 对象聚合了一个对象 B,则即使 A 不存在了,B 也可以单独存在。 + +#### 13.给我一个符合开闭原则的设计模式的例子? + +开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。 + + + +#### 14.工厂模式与抽象工厂模式的区别? + +首先来看看这两者的定义区别: + +工厂模式:定义一个用于创建对象的借口,让子类决定实例化哪一个类 + +抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类 + +​ 个人觉得这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。 + +再来看看工厂方法模式与抽象工厂模式对比: + +| 工厂方法模式 | 抽象工厂模式 | +| ------------------------------------------ | ------------------------------------------ | +| 针对的是一个产品等级结构 | 针对的是面向多个产品等级结构 | +| 一个抽象产品类 | 多个抽象产品类 | +| 可以派生出多个具体产品类 | 每个抽象产品类可以派生出多个具体产品类 | +| 一个抽象工厂类,可以派生出多个具体工厂类 | 一个抽象工厂类,可以派生出多个具体工厂类 | +| 每个具体工厂类只能创建一个具体产品类的实例 | 每个具体工厂类可以创建多个具体产品类的实例 | + + #### 15.举出一个例子,在这种情况你会更倾向于使用抽象类,而不是接口? + +这是很常用但又是很难回答的设计面试问题。接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题: + +1. 在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。 +2. 如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现。 + +#### 16.Dubbo 源码使用了哪些设计模式? + +责任链模式:责任链中的每个节点实现 Filter 接口,然后由 ProtocolFilterWrapper,将所有 Filter 串连起来。 + Dubbo 的许多功能都是通过 Filter 扩展实现的,比如监控、日志、缓存、安全、telnet 以及 RPC 本身都是。 + +观察者模式:消费者在初始化的时候回调用 subscribe 方法,注册一个观察者,如果观察者引用的服务地址列表发生改变, + 就会通过 NotifyListener 通知消费者。 + +装饰器模式:比如 ProtocolFilterWrapper 类是对 Protocol 类的修饰。 + +工厂模式:如 ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()。 + +#### 17.Spring 当中用到了哪些设计模式? + +模板方法模式:例如 jdbcTemplate,通过封装固定的数据库访问比如获取 connection、获取 statement,关闭 connection、关闭 statement 等 + 然后将特殊的 sql 操作交给用户自己实现。 + +策略模式:Spring 在初始化对象的时候,可以选择单例或者原型模式。 + +简单工厂:Spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 bean 对象。 + +工厂方法模式:一般情况下,应用程序有自己的工厂对象来创建 bean.如果将应用程序自己的工厂对象交给 Spring 管理, 那么 Spring 管理的就不是普通的 bean,而是工厂 Bean。 + +单例模式:保证全局只有唯一一个对象。 + +适配器模式:SpringAOP 的 Advice 有如下:BeforeAdvice、AfterAdvice、AfterAdvice,而需要将这些增强转为 aop 框架所需的 + 对应的拦截器 MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。 + +代理模式:Spring 的 Proxy 模式在 aop 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。 + +装饰者模式:如 HttpServletRequestWrapper,自定义请求包装器包装请求,将字符编码转换的工作添加到 getParameter()方法中。 + +观察者模式:如启动初始化 Spring 时的 ApplicationListener 监听器。 + +#### 参考链接 + +https://www.cnblogs.com/study-makes-me-happy/p/7839052.html + +https://ld246.com/article/1583476977456 + +https://www.runoob.com/design-pattern/design-pattern-tutorial.html + +https://creativecommons.org/licenses/by-sa/4.0/