得不到的始终在骚动 金三银四的季节你们有点躁动了 总结一下面试题java
其中有不少其余博客或者论坛摘抄的内容mysql
一. 基础nginx
1.什么是Java虚拟机?为何Java被称做是“平台无关的编程语言”?
答:Java虚拟机是一个能够执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。程序员
2.面向对象的三大特性
答:面向对象的特征主要有如下几个方面:
- 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
- 继承:继承是从已有类获得继承信息建立新类的过程。提供继承信息的类被称为父类(超类、基类);获得继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了必定的延续性,同时继承也是封装程序中可变因素的重要手段(若是不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。
- 封装:一般认为封装是把数据和操做数据的方法绑定起来,对数据的访问只能经过已定义的接口。面向对象的本质就是将现实世界描绘成一系列彻底自治、封闭的对象。咱们在类中编写的方法就是对实现细节的一种封装;咱们编写一个类就是对数据和数据操做的封装。能够说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程 接口(能够想一想普通洗衣机和全自动洗衣机的差异,明显全自动洗衣机封装更好所以操做起来更简单;咱们如今使用的智能手机也是封装得足够好的,由于几个按键就搞定了全部的事情)。
- 多态性:多态性是指容许不一样子类型的对象对同一消息做出不一样的响应。简单的说就是用一样的对象引用调用一样的方法可是作了不一样的事情。多态性分为编译时的多态性和运行时的多态性。若是将对象的方法视为对象向外界提供的服务,那么运行时的多态性能够解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式, 但一切对A系统来讲都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统能够使用电池供电或者用交流电,甚至还有多是太阳能,A系统只会经过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟经过何种方式得到了动力)。方法重载(overload)实现的是编译时的多态性(也称为前绑 定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态须要作两件事:1). 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样一样的引用调用一样的方法就会根据子类对象的不一样而表现出不一样的 行为)。
3.Java支持的数据类型有哪些?什么是自动拆装箱?
答:Java语言支持的8中基本数据类型是:
• byte
• short
• int
• long
• float
• double
• boolean
• char
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间作的一个转化。好比:把int转化成Integer,double转化成double,等等。反之就是自动拆箱。
4.Overload和Override的区别。Overloaded的方法是否能够改变返回值的类型?
答:方法的重写Overriding和重载Overloading是Java多态性的不一样表现。重写Overriding是父类与子类之间多态性的一种表现,重载O verloading是一个类中多态性的一种表现。若是在子类中定义某方法与其父类有相同的名称和参数,咱们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被”屏蔽”了。若是在一个类中定义了多个同名的方法,它们或有不一样的参数个数或有不一样的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是能够改变返回值的类型。
5.接口和抽象类的区别是什么?
答:Java提供和支持建立抽象类和接口。它们的实现有共同点,不一样点在于:
• 接口中全部的方法隐含的都是抽象的。而抽象类则能够同时包含抽象和非抽象的方法。
• 类能够实现不少个接口,可是只能继承一个抽象类
• 类若是要实现一个接口,它必需要实现接口声明的全部方法。可是,类能够不实现抽象类声明的全部方法,固然,在这种状况下,类也必须得声明成是抽象的。
• 抽象类能够在不提供接口方法实现的状况下实现接口。
• Java接口中声明的变量默认都是final的。抽象类能够包含非final的变量。
• Java接口中的成员函数默认是public的。抽象类的成员函数能够是private,protected或者是public。
• 接口是绝对抽象的,不能够被实例化。抽象类也不能够被实例化,可是,若是它包含main方法的话是能够被调用的。
也能够参考JDK8中抽象类和接口的区别
6.什么是值传递和引用传递?
答:对象被值传递,意味着传递了对象的一个副本。所以,就算是改变了对象副本,也不会影响源对象的值。
对象被引用传递,意味着传递的并非实际的对象,而是对象的引用。所以,外部对引用对象所作的改变会反映到全部的对象上。web
7.建立线程有几种不一样的方式?面试
• 继承Thread类算法
重写run()
• 实现Runnable接口spring
重写run()方法
• 实现Callable接口sql
该接口中的call方法能够在线程执行结束时产生一个返回值docker
8.启动一个线程是调用run()仍是start()方法?
答:启动一个线程是调用start()方法,使线程所表明的虚拟处理机处于可运行状态,这意味着它能够由JVM 调度并执行,这并不意味着线程就会当即运行。run()方法是线程启动后要进行回调(callback)的方法。
9.synchronized关键字的用法?
答:synchronized关键字能够将对象或者方法标记为同步,以实现对对象和方法的互斥访问,能够用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized做为方法的修饰符。
10.简述synchronized和java.util.concurrent.locks.Lock的异同?
答:主要相同点:Lock能完成synchronized所实现的全部功能
主要不一样点:Lock有比synchronized更精确的线程语义和更好的性能。
Lock是一个类,synchronized是一个关键字
synchronized会自动释放锁,而Lock必定要求程序员手工释放,而且必须在finally从句中释放。
11.举例说明同步和异步。
答:若是系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据之后可能被另外一个线程读到,或者正在读的数据可能已经被另外一个线程写过了,那么这些数据就必须进行同步存取(数据库操做中的排他锁就是最好的例子)。当应用程序在对象上调用了一个须要花费很长时间来执行的方法,而且不但愿让程序等待方法的返回时,就应该使用异步编程,在不少状况下采用异步途径每每更有效率。事实上,所谓的同步就是指阻塞式操做,而异步就是非阻塞式操做。
12.Thread类的sleep()方法和对象的wait()方法均可以让线程暂停执行,它们有什么区别?
答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其余线程,可是对象的锁依然保持,所以休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法致使当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),若是线程从新得到对象的锁就能够进入就绪状态。
13.同步方法和同步代码块的区别是什么?
答:在Java语言中,每个对象有一把锁。线程能够使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁:这里的锁对象能够是This)或者是代码块级别(细粒度锁:这里的锁对象就是任意对象)。
14.什么是线程池(thread pool)?
答:在面向对象编程中,建立和销毁对象是很费时间的,由于建立一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每个对象,以便可以在对象销毁后进行垃圾回收。因此提升服务程序效率的一个手段就是尽量减小建立和销毁对象的次数,特别是一些很耗资源的对象建立和销毁,这就是”池化资源”技术产生的缘由。线程池顾名思义就是事先建立若干个可执行的线程放入一个池(容器)中,须要的时候从池中获取线程不用自行建立,使用完毕不须要销毁线程而是放回池中,从而减小建立和销毁线程对象的开销。
Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤为是对于线程池的原理不是很清楚的状况下,所以在工具类Executors面提供了一些静态工厂方法,生成一些经常使用的线程池,以下所示:
- newSingleThreadExecutor:建立一个单线程的线程池。这个线程池只有一个线程在工做,也就是至关于单线程串行执行全部任务。若是这个惟一的线程由于异常结束,那么会有一个新的线程来替代它。此线程池保证全部任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:建立固定大小的线程池。每次提交一个任务就建立一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,若是某个线程由于执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:建立一个可缓存的线程池。若是线程池的大小超过了处理任务所须要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增长时,此线程池又能够智能的添加新线程来处理任务。此线程池不会对线程池大小作限制,线程池大小彻底依赖于操做系统(或者说JVM)可以建立的最大线程大小。
- newScheduledThreadPool:建立一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadExecutor:建立一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
15.线程的基本概念、线程的基本状态以及状态之间的关系
答:线程指在程序执行过程当中,可以执行程序代码的一个执行单位,每一个程序至少都有一个线程,也就是程序自己。
Java中的线程有五种状态分别是:新建、就绪、运行、阻塞、结束。
16.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
答:一般咱们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而经过new关键字和构造器建立的对象则放在堆空间,堆是垃圾收集器管理的主要区域,因为如今的垃圾收集器都采用分代收集算法,因此堆空间还能够细分为新生代和老生代,再具体一点能够分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操做起来最快可是栈很小,一般大量的对象都是放在堆空间,栈和堆的大小均可以经过JVM的启动参数来进行调整,栈空间用光了会引起StackOverflowError,而堆和常量池空间不足则会引起OutOfMemoryError。
String str = new String("hello");
上面的语句中变量str放在栈上,用new建立出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区的。
17.当一个对象被看成参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里究竟是值传递仍是引用传递?
答:是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例做为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性能够在被调用过程当中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中能够经过传引用或传输出参数来改变传入的参数的值。在C#中能够编写以下所示的代码,可是在Java中却作不到。
18.String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们能够储存和操做字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象能够直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法彻底相同,区别在于它是在单线程环境下使用的,由于它的全部方面都没有被synchronized修饰,所以它的效率也比StringBuffer要高。
19.描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。
因为Java的跨平台性,通过编译的Java源程序并非一个可执行程序,而是一个或多个类文件。当Java程序须要使用某个类时,JVM会确保这个类已经被加载、链接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,一般是建立一个字节数组读入.class文件,而后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,因此此时的类还不可用。当类被加载后就进入链接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)若是类存在直接的父类而且这个类尚未被初始化,那么就先初始化父类;2)若是类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java 2(JDK 1.2)开始,类加载过程采起了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其余的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:
20,简述深克隆与浅克隆
浅拷贝是指在拷贝对象时,对于基本数据类型的变量会从新复制一份,而对于引用类型的变量只是对引用进行拷贝,
没有对引用指向的对象进行拷贝。
而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
区别就在因而否对 对象中的引用变量所指向的对象进行拷贝。
21.GC是什么?为何要有GC?
答:GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会致使程序或系统的不稳定甚至崩溃,Java提供的GC功能能够自动监测对象是否超过做用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操做方法。Java程序员不用担忧内存管理,由于垃圾收集器会自动进行管理。要请求垃圾收集,能够调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM能够屏蔽掉显示的垃圾回收调用。
垃圾回收能够有效的防止内存泄露,有效的使用能够使用的内存。垃圾回收器一般是做为一个单独的低优先级的线程运行,不可预知的状况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或全部对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,由于服务器端的编程须要有效的防止内存泄露问题,然而时过境迁,现在Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户一般以为iOS的系统比Android系统有更好的用户体验,其中一个深层次的缘由就在于Android系统中垃圾回收的不可预知性。
22.列出一些你常见的运行时异常?
答:
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- IndexOutOfBoundsException (下标越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
23.线程如何同步和通信。
线程同步
什么是线程同步?
当使用多个线程来访问同一个数据时,很是容易出现线程安全问题(好比多个线程都在操做同一数据致使数据不一致),因此咱们用同步机制来解决这些问题。
实现同步机制有两个方法:
1。同步代码块:
synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。
2。同步方法:
public synchronized 数据返回类型 方法名(){}
就是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this 也就是该对象的自己(这里指的对象自己有点含糊,其实就是调用该同步方法的对象)经过使用同步方法,可很是方便的将某类变成线程安全的类
24.为何要使用线程通信?
当使用synchronized 来修饰某个共享资源时(分同步代码块和同步方法两种状况),当某个线程得到共享资源的锁后就能够执行相应的代码段,直到该线程运行完该代码段后才释放对该 共享资源的锁,让其余线程有机会执行对该共享资源的修改。当某个线程占有某个共享资源的锁时,若是另一个线程也想得到这把锁运行就须要使用wait() 和notify()/notifyAll()方法来进行线程通信了。
25.用Java写一个单例类。
//饿汉式
public class Singleton { private Singleton(){} private static Singleton instance = new Singleton(); public static Singleton getInstance() {
return instance; } }
//懒汉式 public class Singleton { private static Singleton instance = null; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } }
26.Java中的四种引用及其应用场景是什么?
new
操做符建立一个对象时所返回的引用即为强引用Bitmap
Bitmap
再也不使用就会被回收-----------------------------------------------------------------------------集合-------------------------------------------------------------------------------------
23.List、Set、Map是否继承自Collection接口?
答:List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不容许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。
24.阐述ArrayList、Vector、LinkedList的存储性能和特性。
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,它们都容许直接按序号索引元素,可是插入元素要涉及数组元素移动等内存操做,因此索引数据快而插入数据慢,Vector中的方法因为添加了synchronized修饰,所以Vector是线程安全的容器,但性能上较ArrayList差,所以已是Java中的遗留容器。LinkedList使用双向链表实现存储(将内存中零散的内存单元经过附加的引用关联起来,造成一个能够按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据须要进行前向或后向遍历,可是插入数据时只须要记录本项的先后项便可,因此插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此以外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,可是因为ArrayList和LinkedListed都是非线程安全的,若是遇到多个线程操做同一个容器的场景,则能够经过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另外一个类的构造器中建立新的对象来加强实现)。
25.List、Map、Set三个接口存取元素时,各有什么特色?
答:List以特定索引来存取元素,能够有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系能够是一对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实如今插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
26.Java中的HashMap的工做原理是什么?
Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap须要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,而后把键值对存储在集合中合适的索引上。若是key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
27.HashMap和Hashtable有什么区别?
• HashMap和Hashtable都实现了Map接口,所以不少特性很是类似。可是,他们有如下不一样点:
• HashMap容许键和值是null,而Hashtable不容许键或者值是null。
• Hashtable是同步的,而HashMap不是。所以,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
• HashMap提供了可供应用迭代的键的集合,所以,HashMap是快速失败的。另外一方面,Hashtable提供了对键的列举(Enumeration)。
通常认为Hashtable是一个遗留的类。
28.数组(Array)和列表(ArrayList)有什么区别?何时应该使用Array而不是ArrayList?
下面列出了Array和ArrayList的不一样点:
• Array能够包含基本类型和对象类型,ArrayList只能包含对象类型。
• Array大小是固定的,ArrayList的大小是动态变化的。
• ArrayList提供了更多的方法和特性,好比:addAll(),removeAll(),iterator()等等。
• 对于基本类型数据,集合使用自动装箱来减小编码工做量。可是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
29.ArrayList和LinkedList有什么区别?
ArrayList和LinkedList都实现了List接口,他们有如下的不一样点:
• ArrayList是基于索引的数据接口**,它的底层是数组**。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素链表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
• 相对于ArrayList,LinkedList的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
• LinkedList比ArrayList更占内存,由于LinkedList为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
也能够参考ArrayList vs. LinkedList。
30.HashSet和TreeSet有什么区别?
HashSet是由一个hash表来实现的,所以,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。
另外一方面,**TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。**所以,add(),remove(),contains()方法的时间复杂度是O(logn)。
31.说出ArrayList,Vector, LinkedList的存储性能和特性
list的子类特色 ArrayList: 底层数据结构是数组,查询快,增删慢 线程不安全,效率高
Vector: 底层数据结构是数组,查询快,增删慢 线程安全,效率低
LinkedList: 底层数据结构是链表,查询慢,增删快 线程不安全,效率高
32.Collection 和 Collections的区别
Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各类集合的搜索、排序、线程安全化等操做。
-----------------------------------------------------------------------------IO-------------------------------------------------------------------------------------
1.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
字节流和字符流。字节流继承于InputStream、OutputStream,字符流继承于Reader、Writer。在java.io 包中还有许多其余的流,主要是为了提升性能和使用方便。关于Java的I/O须要注意的有两点:一是两种对称性(输入和输出的对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)
2.什么是java序列化,如何实现java序列化?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。能够对流化后的对象进行读写操做,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操做时所引起的问题。
序列化的实现:将须要被序列化的类实现Serializable接口,该接口没有须要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,而后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就能够将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
3.什么是同步?什么是异步?
同步和异步的概念出来已经好久了,网上有关同步和异步的说法也有不少。如下是我我的的理解:
同步就是:若是有多个任务或者事件要发生,这些任务或者事件必须逐个地进行,一个事件或者任务的执行会致使整个流程的暂时等待,这些事件没有办法并发地执行;异步就是:若是有多个任务或者事件发生,这些事件能够并发地执行,一个事件或者任务的执行不会致使整个流程的暂时等待。这就是同步和异步。举个简单的例子,假若有一个任务包括两个子任务A和B,对于同步来讲,当A在执行的过程当中,B只有等待,直至A执行完毕,B才能执行;而对于异步就是A和B能够并发地执行,B没必要等待A执行完毕以后再执行,这样就不会因为A的执行致使整个任务的暂时等待。
4.什么是阻塞?什么是非阻塞?
在前面介绍了同步和异步的区别,这一节来看一下阻塞和非阻塞的区别。
阻塞就是:当某个事件或者任务在执行过程当中,它发出一个请求操做,可是因为该请求操做须要的条件不知足,那么就会一直在那等待,直至条件知足;
非阻塞就是:当某个事件或者任务在执行过程当中,它发出一个请求操做,若是该请求操做须要的条件不知足,会当即返回一个标志信息告知条件不知足,不会一直在那等待。
这就是阻塞和非阻塞的区别。也就是说阻塞和非阻塞的区别关键在于当发出请求一个操做时,若是条件不知足,是会一直等待仍是返回一个标志信息。
5.什么是阻塞IO?什么是非阻塞IO?
在了解阻塞IO和非阻塞IO以前,先看下一个具体的IO操做过程是怎么进行的。
一般来讲,IO操做包括:对硬盘的读写、对socket的读写以及外设的读写。
当用户线程发起一个IO请求操做(本文以读请求操做为例),内核会去查看要读取的数据是否就绪,对于阻塞IO来讲,若是数据没有就绪,则会一直在那等待,直到数据就绪;对于非阻塞IO来讲,若是数据没有就绪,则会返回一个标志信息告知用户线程当前要读的数据没有就绪。当数据就绪以后,便将数据拷贝到用户线程,这样才完成了一个完整的IO读请求操做,也就是说一个完整的IO读请求操做包括两个阶段:
1)查看数据是否就绪;
2)进行数据拷贝(内核将数据拷贝到用户线程)。
那么阻塞(blocking IO)和非阻塞(non-blocking IO)的区别就在于第一个阶段,若是数据没有就绪,在查看数据是否就绪的过程当中是一直等待,仍是直接返回一个标志信息。
Java中传统的IO都是阻塞IO,好比经过socket来读数据,调用read()方法以后,若是数据没有就绪,当前线程就会一直阻塞在read方法调用那里,直到有数据才返回;而若是是非阻塞IO的话,当数据没有就绪,read()方法应该返回一个标志信息,告知当前线程数据没有就绪,而不是一直在那里等待。
6.什么是同步IO?什么是异步IO?
咱们先来看一下同步IO和异步IO的定义,在《Unix网络编程》一书中对同步IO和异步IO的定义是这样的:
A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes.
An asynchronous I/O operation does not cause the requesting process to be blocked.
从字面的意思能够看出:同步IO即 若是一个线程请求进行IO操做,在IO操做完成以前,该线程会被阻塞;而异步IO为 若是一个线程请求进行IO操做,IO操做不会致使请求线程被阻塞。
事实上,同步IO和异步IO模型是针对用户线程和内核的交互来讲的:
对于同步IO:当用户发出IO请求操做以后,若是数据没有就绪,须要经过用户线程或者内核不断地去轮询数据是否就绪,当数据就绪时,再将数据从内核拷贝到用户线程;
而异步IO:只有IO请求操做的发出是由用户线程来进行的,IO操做的两个阶段都是由内核自动完成,而后发送通知告知用户线程IO操做已经完成。也就是说在异步IO中,不会对用户线程产生任何阻塞。
这是同步IO和异步IO关键区别所在,同步IO和异步IO的关键区别反映在数据拷贝阶段是由用户线程完成仍是内核完成。因此说异步IO必需要有操做系统的底层支持。
注意同步IO和异步IO与阻塞IO和非阻塞IO是不一样的两组概念。
阻塞IO和非阻塞IO是反映在当用户请求IO操做时,若是数据没有就绪,是用户线程一直等待数据就绪,仍是会收到一个标志信息这一点上面的。也就是说,阻塞IO和非阻塞IO是反映在IO操做的第一个阶段,在查看数据是否就绪时是如何处理的。
7.NIO与IO的区别
NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的做用和目的,但实现方式不一样,NIO主要用到的是块,因此NIO的效率要比IO高不少。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另外一套就是网络编程NIO。
1.面向流与面向缓冲
Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。
2.阻塞与非阻塞IO
Java IO的各类流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,可是它仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,因此直至数据变的能够读取以前,该线程能够继续作其余的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不须要等待它彻底写入,这个线程同时能够去作别的事情。 线程一般将非阻塞IO的空闲时间用于在其它通道上执行IO操做,因此一个单独的线程如今能够管理多个输入和输出通道(channel)。
3.选择器(Selectors)
Java NIO的选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
4.总结
NIO可以让您只使用一个(或几个)单线程管理多个通道(网络链接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。
若是须要管理同时打开的成千上万个链接,这些链接每次只是发送少许的数据,例如聊天服务器,实现NIO的服务器多是一个优点。一样,若是你须要维持许多打开的链接到其余计算机上,如P2P网络中,使用一个单独的线程来管理你全部出站链接,多是一个优点。
若是你有少许的链接使用很是高的带宽,一次发送大量的数据,也许典型的IO服务器实现可能很是契合。
-----------------------------------------------------------------------------数据库-------------------------------------------------------------------------------------
数据库优化方面
1).尽量使用更小的整数类型.(mediumint就比int更合适).
2).尽量的定义字段为not null,除非这个字段须要null.
3).若是没有用到变长字段的话好比varchar,那就采用固定大小的纪录格式好比char.
4).表的主索引应该尽量的短.这样的话每条纪录都有名字标志且更高效.
5).只建立确实须要的索引。索引有利于检索记录,可是不利于快速保存记录。若是老是要在表的组合字段上作搜索,那么就在这些字段上建立索引。索引的第一部分必须是最常使用的字段.若是老是须要用到不少字段,首先就应该多复制这些字段,使索引更好的压缩。
6).全部数据都得在保存到数据库前进行处理。
7).全部字段都得有默认值。
8).在某些状况下,把一个频繁扫描的表分红两个速度会快好多。在对动态格式表扫描以取得相关记录时,它可能使用更小的静态格式表的状况下更是如此。
2.系统的用途
1).尽可能使用长链接.
2).explain 复杂的SQL语句。
3).若是两个关联表要作比较话,作比较的字段必须类型和长度都一致.
4).LIMIT语句尽可能要跟order by或者 distinct.这样能够避免作一次full table scan.
5).若是想要清空表的全部纪录,建议用truncate table tablename而不是delete from tablename.
6).能使用STORE PROCEDURE 或者 USER FUNCTION的时候.
7).在一条insert语句中采用多重纪录插入格式.并且使用load data infile来导入大量数据,这比单纯的indert快好多.
8).常常OPTIMIZE TABLE 来整理碎片.
9).还有就是date 类型的数据若是频繁要作比较的话尽可能保存在unsigned int 类型比较快。
3.系统的瓶颈
1).磁盘搜索.
并行搜索,把数据分开存放到多个磁盘中,这样能加快搜索时间.
2).磁盘读写(IO)
能够从多个媒介中并行的读取数据。
3).CPU周期
数据存放在主内存中.这样就得增长CPU的个数来处理这些数据。
4).内存带宽
当CPU要将更多的数据存放到CPU的缓存中来的话,内存的带宽就成了瓶颈.
-----------------------------------------------------------------------------分布式-------------------------------------------------------------------------------------
CAP原理:分布式计算系统不可能同时确保如下三个特性:一致性(Consistency)、可用性(Availability)和分区容忍性(Partition),设计中每每须要弱化对某个特性的保证。
这里,一致性、可用性和分区容忍性的含义以下:
一致性:任何操做应该都是原子的,发生在后面的事件能看到前面事件发生致使的结果,注意这里指的是强一致性;
可用性:在有限时间内,任何非失败节点都能应答请求;
分区容忍性:网络可能发生分区,即节点之间的通讯不可保障。
比较直观地理解以下,当网络可能出现分区的时候,系统是没法同时保证一致性和可用性的。要么,节点收到请求后由于没有获得其余节点的确认而不该答(牺牲可用性),要么节点只能应答非一致的结果(牺牲一致性)。
因为大多数时候网络被认为是可靠的,所以系统能够提供一致可靠的服务;当网络不可靠时,系统要么牺牲掉一致性(多数场景下),要么牺牲掉可用性。
注意:网络分区是可能存在的,出现分区状况后极可能会致使发生“脑裂”,多个新出现的主节点可能会尝试关闭其余主节点。
zookeeper:
ZooKeeper是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操做。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序能够基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。
Zookeeper保证了以下分布式一致性特性:
客户端的读请求能够被集群中的任意一台机器处理,若是读请求在节点上注册了监听器,这个监听器也是由所链接的zookeeper机器来处理。对于写请求,这些请求会同时发给其余zookeeper机器而且达成一致后,请求才会返回成功。所以,随着zookeeper的集群机器增多,读请求的吞吐会提升可是写请求的吞吐会降低。
有序性是zookeeper中很是重要的一个特性,全部的更新都是全局有序的,每一个更新都有一个惟一的时间戳,这个时间戳称为zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper最新的zxid。
zookeeper负载均衡和nginx负载均衡区别
zk的负载均衡是能够调控,nginx只是能调权重,其余须要可控的都须要本身写插件;可是nginx的吞吐量比zk大不少,应该说按业务选择用哪一种方式。
Nginx
请解释一下什么是Nginx?
Nginx是一个web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。
(1)RR轮询,默认的反向代理模式,用以平衡各服务器的负载,若某个服务器宕机,会自动从轮询中剃掉。同时,咱们能够手动指定某台服务器脱离轮询,用于离线检查或升级
(2)weight权重,针对服务器性能不通,用来控制服务器被访问的比例。调节各服务器负载
(3)ip hash,ip_hash主要记录了客户端IP访问的目标主机,以实现老用户访问时的快速调度。
四、Nginx是如何处理一个请求的呢?
首先,nginx在启动时,会解析配置文件,获得须要监听的端口与ip地址,而后在nginx的master进程里面
先初始化好这个监控的socket,再进行listen
而后再fork出多个子进程出来, 子进程会竞争accept新的链接。
此时,客户端就能够向nginx发起链接了。当客户端与nginx进行三次握手,与nginx创建好一个链接后,此时,某一个子进程会accept成功,而后建立nginx对链接的封装,即ngx_connection_t结构体接着,根据事件调用相应的事件处理模块,如http模块与客户端进行数据的交换,最后,nginx或客户端来主动关掉链接,到此,一个链接就寿终正寝了
docker
Docker 是一个开源的应用容器引擎,让开发者能够打包他们的应用以及依赖包到一个可移植的容器中,而后发布到任何流行的Linux机器上,也能够实现虚拟化,容器是彻底使用沙箱机制,相互之间不会有任何接口。
dubbo
-----------------------------------------------------------------------------spring boot +mybatis-------------------------------------------------------------------------------------
Spring Boot
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各类启动器,开发者能快速上手。
springboot自动配置的原理
在spring程序main方法中 添加@SpringBootApplication或者@EnableAutoConfiguration
会自动去maven中读取每一个starter中的spring.factories文件 该文件里配置了全部须要被建立spring容器中的bean
springboot读取配置文件的方式
springboot默认读取配置文件为application.properties或者是application.yml
什么是微服务
之前的模式是 全部的代码在同一个工程中 部署在同一个服务器中 同一个项目的不一样模块不一样功能互相抢占资源
微服务 将工程根据不一样的业务规则拆分红微服务 微服务部署在不一样的机器上 服务之间进行相互调用
Java微服务的框架有 dubbo(只能用来作微服务),spring cloud(提供了服务的发现,断路器等)
springcloud如何实现服务的注册和发现
服务在发布时 指定对应的服务名(服务名包括了IP地址和端口) 将服务注册到注册中心(eureka或者zookeeper)
这一过程是springcloud自动实现 只须要在main方法添加@EnableDisscoveryClient 同一个服务修改端口就能够启动多个实例
调用方法:传递服务名称经过注册中心获取全部的可用实例 经过负载均衡策略调用(ribbon和feign)对应的服务
ribbon和feign区别
Ribbon添加maven依赖 spring-starter-ribbon 使用@RibbonClient(value="服务名称") 使用RestTemplate调用远程服务对应的方法
feign添加maven依赖 spring-starter-feign 服务提供方提供对外接口 调用方使用 在接口上使用@FeignClient("指定服务名")
Ribbon和Feign的区别:
Ribbon和Feign都是用于调用其余服务的,不过方式不一样。
1.启动类使用的注解不一样,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。
2.服务的指定位置不一样,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
3.调用方式不一样,Ribbon须要本身构建http请求,模拟http请求而后使用RestTemplate发送给其余服务,步骤至关繁琐。
Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将须要调用的其余服务的方法定义成抽象方法便可,
不须要本身构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法彻底一致。
springcloud断路器的做用
当一个服务调用另外一个服务因为网络缘由或者自身缘由出现问题时 调用者就会等待被调用者的响应 当更多的服务请求到这些资源时
致使更多的请求等待 这样就会发生连锁效应(雪崩效应) 断路器就是解决这一问题
断路器有彻底打开状态
必定时间内 达到必定的次数没法调用 而且屡次检测没有恢复的迹象 断路器彻底打开,那么下次请求就不会请求到该服务
半开
短期内 有恢复迹象 断路器会将部分请求发给该服务 当能正常调用时 断路器关闭
关闭
当服务一直处于正常状态 能正常调用 断路器关闭
----------------------------------------------------------------------------通讯--------------------------------------------------------------------------------------------------------
1.概念解释:SOAP、WSDL、UDDI。
答:
- SOAP:简单对象访问协议(Simple Object Access Protocol),是Web Service中交换数据的一种协议规范。
- WSDL:Web服务描述语言(Web Service Description Language),它描述了Web服务的公共接口。这是一个基于XML的关于如何与Web服务通信和使用的服务描述;也就是描述与目录中列出的Web服务进行交互时须要绑定的协议和信息格式。一般采用抽象语言描述该服务支持的操做和信息,使用的时候再将实际的网络协议和信息格式绑定给该服务。
- UDDI:统一描述、发现和集成(Universal Description, Discovery and Integration),它是一个基于XML的跨平台的描述规范,能够使世界范围内的企业在互联网上发布本身所提供的服务。简单的说,UDDI是访问各类WSDL的一个门面(能够参考设计模式中的门面模式)。
WebService是一种跨编程语言和跨操做系统平台的远程调用技术。
2.WSDL是什么,有什么做用?
WSDL是web service definition language的缩写,即web service的定义(描述)语言。
怎样向别人介绍你的 web service 有什么功能,以及每一个函数调用时的参数呢?你可能会本身写一套文档,你甚至可能会口头上告诉须要使用你的web service的人。这些非正式的方法至少都有一个严重的问题:当程序员坐到电脑前,想要使用你的web service的时候,他们的工具(如Visual Studio)没法给他们提供任何帮助,由于这些工具根本就不了解你的web service。解决方法是:用机器能阅读的方式提供一个正式的描述文档。web service描述语言(WSDL)就是这样一个基于XML的语言,用于描述web service及其函数、参数和返回值。由于是基于XML的,因此WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的web service生成WSDL文档,又能导入WSDL文档,生成调用相应web service的代码。
Webservice服务发布以后,经过浏览器访问发布的+?wsdl便可得到wsdl文档。
三.WSDL文档主要有那几部分组成,分别有什么做用?
一个WSDL文档的根元素是definitions元素,WSDL文档包含7个重要的元素:types, import, message, portType, operations, binding和service元素。
一、 definitions元素中通常包括若干个XML命名空间;
二、 Types元素用做一个容器,定义了自定义的特殊数据类型,在声明消息部分(有效负载)的时候,messages定义使用了types元素中定义的数据类型与元素;
三、 Import元素可让当前的文档使用其余WSDL文档中指定命名空间中的定义;
四、 Message元素描述了Web服务的有效负载。至关于函数调用中的参数和返回值;
五、 PortType元素定义了Web服务的抽象接口,它能够由一个或者多个operation元素,每一个operation元素定义了一个RPC样式或者文档样式的Web服务方法;
六、 Operation元素要用一个或者多个messages消息来定义它的输入、输出以及错误;
七、 Binding元素将一个抽象的portType映射到一组具体的协议(SOAP或者HTTP)、消息传递样式(RPC或者document)以及编码样式(literal或者SOAP encoding);
八、 Service元素包含一个或者多个Port元素
每个Port元素对应一个不一样的Web服务,port将一个URL赋予一个特定的binding,经过location实现。
能够使两个或者多个port元素将不一样的URL赋给相同的binding。
四.SOAP是什么?
SOAP是simple object access protocal的缩写,即简单对象访问协议。 是基于XML和HTTP的一种通讯协议。是webservice所使用的一种传输协议,webservice之因此可以作到跨语言和跨平台,主要是由于XML和HTTP都是独立于语言和平台的。Soap的消息分为请求消息和响应消息,一条SOAP消息就是一个普通的XML文档,包含下列元素:
一、 必需的 Envelope 元素,可把此XML文档标识为一条SOAP消息
二、 可选的 Header 元素,包含头部信息
三、 必需的 Body 元素,包含全部的调用和响应信息
四、 可选的 Fault 元素,提供有关在处理此消息所发生错误的信息
Soap请求消息
Soap响应消息
五.怎么理解UDDI?
UDDI是Universal Description Discovery and Integration的缩写,即统一描述、发现和整合规范。用来注册和查找服务,把web services收集和存储起来,这样当别人访问这些信息的时候就从UDDI中查找,看有没有这个信息存在。
五.Webservice的SEI指什么?
WebService EndPoint Interface(webservice终端[Server端]接口)
六.说说你知道的webservice框架,他们都有什么特色?
Webservice经常使用框架有JWS、Axis2、XFire以及CXF。
下面分别介绍一个这几种Web Service框架的基本概念
1、JWS是Java语言对WebService服务的一种实现,用来开发和发布服务。而从服务自己的角度来看JWS服务是没有语言界限的。可是Java语言为Java开发者提供便捷发布和调用WebService服务的一种途径。
2、Axis2是Apache下的一个重量级WebService框架,准确说它是一个Web Services / SOAP / WSDL 的引擎,是WebService框架的集大成者,它能不但能制做和发布WebService,并且能够生成Java和其余语言版WebService客户端和服务端代码。这是它的优点所在。可是,这也不可避免的致使了Axis2的复杂性,使用过的开发者都知道,它所依赖的包数量和大小都是很惊人的,打包部署发布都比较麻烦,不能很好的与现有应用整合为一体。可是若是你要开发Java以外别的语言客户端,Axis2提供的丰富工具将是你不二的选择。
3、XFire是一个高性能的WebService框架,在Java6以前,它的知名度甚至超过了Apache的Axis2,XFire的优势是开发方便,与现有的Web整合很好,能够融为一体,而且开发也很方便。可是对Java以外的语言,没有提供相关的代码工具。XFire后来被Apache收购了,缘由是它太优秀了,收购后,随着Java6 JWS的兴起,开源的WebService引擎已经再也不被看好,渐渐的都败落了。
4、CXF是Apache旗下一个重磅的SOA简易框架,它实现了ESB(企业服务总线)。CXF来自于XFire项目,通过改造后造成的,就像目前的Struts2来自WebWork同样。能够看出XFire的命运会和WebWork的命运同样,最终会淡出人们的视线。CXF不可是一个优秀的Web Services / SOAP / WSDL 引擎,也是一个不错的ESB总线,为SOA的实施提供了一种选择方案,固然他不是最好的,它仅仅实现了SOA架构的一部分。
注:对于Axis2与CXF之间的关系,一个是Axis2出现的时间较早,而CXF的追赶速度快。
如何抉择:
1、若是应用程序须要多语言的支持,Axis2应当是首选了;
2、若是应用程序是遵循 spring哲学路线的话,Apache CXF是一种更好的选择,特别对嵌入式的Web Services来讲;
3、若是应用程序没有新的特性须要的话,就还是用原来项目所用的框架,好比 Axis1,XFire,Celtrix或BEA等等厂家本身的Web Services实现,就别劳民伤财了。
MQ
为何使用消息队列?消息队列的优势和缺点?kafka、activemq、rabbitmq、rocketmq都有什么优缺点?
消息队列的常见使用场景有不少可是核心的有三个:解耦、异步、削峰
消息队列的有点和缺点?
优势:特殊场景下解耦、异步、削峰。
系统可用性下降:系统引入的外部依赖越多,越容易挂掉,原本你就是A系统调用BCD三个系统的接口就行了,人ABCD四个系统好好的没什么问题,你偏加个MQ进来,万一MQ挂了怎么办,整套系统崩溃了,就完蛋了
系统复杂性提升:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的状况?怎么保证消息传递的顺序性?
一致性问题:系统A处理完了直接返回成功了,人家都认为你这个请求成功了;但问题是,要是BCD三个系统哪里BD系统成功了,结果C系统写库失败了,咋整?数据就不一致了,
kafka、activemq、rabbitmq、rocketmq都有什么优缺点?
特性
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
单机吞吐量 |
万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是能够支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优势,就是吞吐量高。
通常配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic数量对吞吐量的影响 | topic能够达到几百,几千个的级别,吞吐量会有较小幅度的降低
这是RocketMQ的一大优点,在同等机器下,能够支撑大量的topic |
topic从几十个到几百个的时候,吞吐量会大幅度降低
因此在同等机器下,kafka尽可能保证topic数量不要过多。若是要支撑大规模topic,须要增长更多的机器资源 |
||
时效性 | ms级 | 微秒级,这是rabbitmq的一大特色,延迟是最低的 | ms级 | 延迟在ms级之内 |
可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 很是高,分布式架构 | 很是高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会致使不可用 |
消息可靠性 | 有较低的几率丢失数据 | 通过参数优化配置,能够作到0丢失 | 通过参数优化配置,消息能够作到0丢失 | |
功能支持 | MQ领域的功能极其完备 | 基于erlang开发,因此并发能力很强,性能极其好,延时很低 | MQ功能较为完善,仍是分布式的,扩展性好 |
功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志 采集被大规模使用,是事实上的标准 |
优劣势总结 | 很是成熟,功能强大,在业内大量的公司以及项目中都有应用 偶尔会有较低几率丢失消息 并且如今社区以及国内应用都愈来愈少,官方社区如今对ActiveMQ 5.x维护愈来愈少,几个月才发布一个版本 并且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用 |
erlang语言开发,性能极其好,延时很低; ,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。 |
接口简单易用,并且毕竟在阿里大规模应用过,有阿里品牌保障 可靠性和可用性都是ok的,还能够支撑大规模的topic数量,支持复杂MQ业务场景 块不是按照标准JMS规范走的有些系统要迁移须要修改大量代码 用RocketMQ挺好的 |
kafka的特色其实很明显,就是仅仅提供较少的核心功能,可是提供超高 的吞吐量,ms级的延迟,极高的可用性以及可靠性,并且分布式能够任意扩展 影响,在大数据领域中以及日志采集中,这点轻微影响能够忽略 这个特性自然适合大数据实时计算以及日志收集 |
1. 引入消息队列以后如何保证其高可用性?
(1)RabbitMQ的高可用性
RabbitMQ是比较有表明性的,由于是基于主从作高可用性的,咱们就以他为例子讲解第一种MQ的高可用性怎么实现。
rabbitmq有三种模式:单机模式,普通集群模式,镜像集群模式
(1.1) 单机模式
就是demo级别的,通常就是你本地启动了玩玩儿的,没人生产用单机模式
(1.2)普通集群模式
意思就是在多台机器上启动多个rabbitmq实例,每一个机器启动一个。可是你建立的queue,只会放在一个rabbtimq实例上,可是每一个实例都同步queue的元数据。完了你消费的时候,实际上若是链接到了另一个实例,那么那个实例会从queue所在实例上拉取数据过来。
这种方式确实很麻烦,也不怎么好,没作到所谓的分布式,就是个普通集群。由于这致使你要么消费者每次随机链接一个实例而后拉取数据,要么固定链接那个queue所在实例消费数据,前者有数据拉取的开销,后者致使单实例性能瓶颈。
并且若是那个放queue的实例宕机了,会致使接下来其余实例就没法从那个实例拉取,若是你开启了消息持久化,让rabbitmq落地存储消息的话,消息不必定会丢,得等这个实例恢复了,而后才能够继续从这个queue拉取数据。
因此这个事儿就比较尴尬了,这就没有什么所谓的高可用性可言了,这方案主要是提升吞吐量的,就是说让集群中多个节点来服务某个queue的读写操做。
1.3)镜像集群模式
这种模式,才是所谓的rabbitmq的高可用模式,跟普通集群模式不同的是,你建立的queue,不管元数据仍是queue里的消息都会存在于多个实例上,而后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
这样的话,好处在于,你任何一个机器宕机了,没事儿,别的机器均可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步全部机器,致使网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,若是某个queue负载很重,你加机器,新增的机器也包含了这个queue的全部数据,并无办法线性扩展你的queue
那么怎么开启这个镜像集群模式呢?我这里简单说一下,避免面试人家问你你不知道,其实很简单rabbitmq有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候能够要求数据同步到全部节点的,也能够要求就同步到指定数量的节点,而后你再次建立queue的时候,应用这个策略,就会自动将数据同步到其余的节点上去了。
(2)kafka
1)消费端弄丢了数据
惟一可能致使消费者弄丢数据的状况,就是说,你那个消费到了这个消息,而后消费者那边自动提交了offset,让kafka觉得你已经消费好了这个消息,其实你刚准备处理这个消息,你还没处理,你本身就挂了,此时这条消息就丢咯。
这不是同样么,你们都知道kafka会自动提交offset,那么只要关闭自动提交offset,在处理完以后本身手动提交offset,就能够保证数据不会丢。可是此时确实仍是会重复消费,好比你刚处理完,还没提交offset,结果本身挂了,此时确定会重复消费一次,本身保证幂等性就行了。
生产环境碰到的一个问题,就是说咱们的kafka消费者消费到了数据以后是写到一个内存的queue里先缓冲一下,结果有的时候,你刚把消息写入内存queue,而后消费者会自动提交offset。
而后此时咱们重启了系统,就会致使内存queue里还没来得及处理的数据就丢失了
2)kafka弄丢了数据
这块比较常见的一个场景,就是kafka某个broker宕机,而后从新选举partiton的leader时。你们想一想,要是此时其余的follower恰好还有些数据没有同步,结果此时leader挂了,而后选举某个follower成leader以后,他不就少了一些数据?这就丢了一些数据啊。
生产环境也遇到过,咱们也是,以前kafka的leader机器宕机了,将follower切换为leader以后,就会发现说这个数据就丢了
因此此时通常是要求起码设置以下4个参数:
给这个topic设置replication.factor参数:这个值必须大于1,要求每一个partition必须有至少2个副本
在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟本身保持联系,没掉队,这样才能确保leader挂了还有一个follower吧
在producer端设置acks=all:这个是要求每条数据,必须是写入全部replica以后,才能认为是写成功了
在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了
咱们生产环境就是按照上述要求配置的,这样配置以后,至少在kafka broker端就能够保证在leader所在broker发生故障,进行leader切换时,数据不会丢失
3)生产者会不会弄丢数据
若是按照上述的思路设置了ack=all,必定不会丢,要求是,你的leader接收到消息,全部的follower都同步到了消息以后,才认为本次写成功了。若是没知足这个条件,生产者会自动不断的重试,重试无限次。
1. 如何保证消息的顺序性?
其实这个也是用MQ的时候必问的话题,第一看看你了解不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的?这个生产系统中常见的问题。
我举个例子,咱们之前作过一个mysql binlog同步的系统,压力仍是很是大的,日同步数据要达到上亿。mysql -> mysql,常见的一点在于说大数据team,就须要同步一个mysql库过来,对公司的业务系统的数据作各类复杂的操做。
你在mysql里增删改一条数据,对应出来了增删改3条binlog,接着这三条binlog发送到MQ里面,到消费出来依次执行,起码得保证人家是按照顺序来的吧?否则原本是:增长、修改、删除;你楞是换了顺序给执行成删除、修改、增长,不全错了么。
原本这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。
先看看顺序会错乱的俩场景
(1)rabbitmq:一个queue,多个consumer,这不明显乱了
(2)kafka:一个topic,一个partition,一个consumer,内部多线程,这不也明显乱了
那如何保证消息的顺序性呢?简单简单
(1)rabbitmq:拆分多个queue,每一个queue一个consumer,就是多一些queue而已,确实是麻烦点;或者就一个queue可是对应一个consumer,而后这个consumer内部用内存队列作排队,而后分发给底层不一样的worker来处理
(2)kafka:一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,而后N个线程分别消费一个内存queue便可
1. 如何解决消息队列的延时以及过时失效问题?消息队列满了之后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
你看这问法,其实本质针对的场景,都是说,可能你的消费端出了问题,不消费了,或者消费的极其极其慢。接着就坑爹了,可能你的消息队列集群的磁盘都快写满了,都没人消费,这个时候怎么办?或者是整个这就积压了几个小时,你这个时候怎么办?或者是你积压的时间太长了,致使好比rabbitmq设置了消息过时时间后就没了怎么办?
因此就这事儿,其实线上挺常见的,通常不出,一出就是大case,通常常见于,举个例子,消费端每次消费以后要写mysql,结果mysql挂了,消费端hang那儿了,不动了。或者是消费端出了个什么叉子,致使消费速度极其慢。
关于这个事儿,咱们一个一个来梳理吧,先假设一个场景,咱们如今消费端出故障了,而后大量消息在mq里积压,如今事故了,慌了
(1)大量消息在mq里积压了几个小时了还没解决
几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多
这个是咱们真实遇到过的一个场景,确实是线上故障了,这个时候要否则就是修复consumer的问题,让他恢复消费速度,而后傻傻的等待几个小时消费完毕。这个确定不能在面试的时候说吧。
一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条
因此若是你积压了几百万到上千万的数据,即便消费者恢复了,也须要大概1小时的时间才能恢复过来
通常这个时候,只能操做临时紧急扩容了,具体操做步骤和思路以下:
1)先修复consumer的问题,确保其恢复消费速度,而后将现有cnosumer都停掉
2)新建一个topic,partition是原来的10倍,临时创建好原先10倍或者20倍的queue数量
3)而后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费以后不作耗时的处理,直接均匀轮询写入临时创建好的10倍数量的queue
4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据
5)这种作法至关因而临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据
6)等快速消费完积压数据以后,得恢复原先部署架构,从新用原先的consumer机器来消费消息
(2)这里咱们假设再来第二个坑
假设你用的是rabbitmq,rabbitmq是能够设置过时时间的,就是TTL,若是消息在queue中积压超过必定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。
这个状况下,就不是说要增长consumer消费积压的消息,由于实际上没啥积压,而是丢了大量的消息。咱们能够采起一个方案,就是批量重导,这个咱们以前线上也有相似的场景干过。就是大量积压的时候,咱们当时就直接丢弃数据了,而后等过了高峰期之后,好比你们一块儿喝咖啡熬夜到晚上12点之后,用户都睡觉了。
这个时候咱们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,而后从新灌入mq里面去,把白天丢的数据给他补回来。也只能是这样了。
假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个订单给查出来,手动发到mq里去再补一次
(3)而后咱们再来假设第三个坑
若是走的方式是消息积压在mq里,那么若是你很长时间都没处理掉,此时致使mq都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉全部的消息。而后走第二个方案,到了晚上再补数据吧。
1. 若是让你写一个消息队列,该如何进行架构设计啊?说一下你的思路
其实聊到这个问题,通常面试官要考察两块:
(1)你有没有对某一个消息队列作过较为深刻的原理的了解,或者从总体了解把握住一个mq的架构原理
(2)看看你的设计能力,给你一个常见的系统,就是消息队列系统,看看你能不能从全局把握一下总体架构设计,给出一些关键点出来
其实回答这类问题,说白了,起码不求你看过那技术的源码,起码你大概知道那个技术的基本原理,核心组成部分,基本架构构成,而后参照一些开源的技术把一个系统设计出来的思路说一下就好
好比说这个消息队列系统,咱们来从如下几个角度来考虑一下
说实话,我通常面相似问题的时候,大部分人基本都会蒙,由于平时历来没有思考过相似的问题,大多数人就是平时埋头用,历来不去思考背后的一些东西。相似的问题,我常常问的还有,若是让你来设计一个spring框架你会怎么作?若是让你来设计一个dubbo框架你会怎么作?若是让你来设计一个mybatis框架你会怎么作?
其实回答这类问题,说白了,起码不求你看过那技术的源码,起码你大概知道那个技术的基本原理,核心组成部分,基本架构构成,而后参照一些开源的技术把一个系统设计出来的思路说一下就好
好比说这个消息队列系统,咱们来从如下几个角度来考虑一下
(1)首先这个mq得支持可伸缩性吧,就是须要的时候快速扩容,就能够增长吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker -> topic -> partition,每一个partition放一个机器,就存一部分数据。若是如今资源不够了,简单啊,给topic增长partition,而后作数据迁移,增长机器,不就能够存放更多数据,提供更高的吞吐量了?
(2)其次你得考虑一下这个mq的数据要不要落地磁盘吧?那确定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是kafka的思路。
其次你考虑一下你的mq的可用性啊?这个事儿,具体参考咱们以前可用性那个环节讲解的kafka的高可用保障机制。多副本 -> leader & follower -> broker挂了从新选举leader便可对外服务。
(4)能不能支持数据0丢失啊?能够的,参考咱们以前说的那个kafka数据零丢失方案
restful
谈谈你对restful 规范的理解? - restful其实就是一套编写接口的协议,协议规定如何编写以及如何设置返回值、状态码等信息。 - 最显著的特色: restful: 给用户一个url,根据method不一样在后端作不一样的处理,好比:post 建立数据、get获取数据、put和patch修改数据、delete删除数据。
restful与webservice的区别
首先须要了解:REST是一种架构风格,其核心是面向资源;而webService底层SOAP协议,主要核心是面向活动;
SOAP
什么是SOAP,我想不用多说,google一把满眼都是。其实SOAP最先是针对RPC的一种解决方案,简单对象访问协议,很轻量,同时做为应用协议能够基于多种传输协议来传递消息(Http,SMTP等)。可是随着SOAP做为WebService的普遍应用,不断地增长附加的内容,使得如今开发人员以为SOAP很重,使用门槛很高。在SOAP后续的发展过程当中,WS-*一系列协议的制定,增长了SOAP的成熟度,也给SOAP增长了负担。
REST
REST其实并非什么协议也不是什么标准,而是将Http协议的设计初衷做了诠释,在Http协议被普遍利用的今天,愈来愈多的是将其做为传输协议,而非原先设计者所考虑的应用协议。SOAP类型的WebService就是最好的例子,SOAP消息彻底就是将Http协议做为消息承载,以致于对于Http协议中的各类参数(例如编码,错误码等)都置之不顾。其实,最轻量级的应用协议就是Http协议。Http协议所抽象的get,post,put,delete就比如数据库中最基本的增删改查,而互联网上的各类资源就比如数据库中的记录,对于各类资源的操做最后老是能抽象成为这四种基本操做,在定义了定位资源的规则之后,对于资源的操做经过标准的Http协议就能够实现,开发者也会受益于这种轻量级的协议。
REST专门针对网络应用设计和开发方式,以下降开发的复杂性,提升系统的可伸缩性。REST提出设计概念和准则为:
1. 网络上的全部事物均可以被抽象为资源(resource)
2. 每个资源都有惟一的资源标识(resource identifier),对资源的操做不会改变这些标识
3. 全部的操做都是无状态的
REST简化开发,其架构遵循CRUD原则,该原则告诉咱们对于资源(包括网络资源)只须要四种行为:建立,获取,更新和删除就能够完成相关的操做和处理。咱们能够经过统一资源标识符(Universal Resource Identifier,URI)来识别和定位资源,而且针对这些资源而执行的操做是经过 HTTP 规范定义的。其核心操做只有GET,PUT,POST,DELETE。因为REST强制全部的操做都必须是stateless的,这就没有上下文的约束,若是作分布式,集群都不须要考虑上下文和会话保持的问题。极大的提升系统的可伸缩性。
SOAP webService有严格的规范和标准,包括安全,事务等各个方面的内容,同时SOAP强调操做方法和操做对象的分离,有WSDL文件规范和XSD文件分别对其定义。
若是从这个意义上讲,是否使用REST就须要考虑资源自己的抽象和识别是否困难,若是自己就是简单的相似增删改查的业务操做,那么抽象资源就比较容易,而对于复杂的业务活动抽象资源并非一个简单的事情。好比校验用户等级,转帐,事务处理等,这些每每并不容易简单的抽象为资源。
其次若是有严格的规范和标准定义要求,并且前期规范标准须要指导多个业务系统集成和开发的时候,SOAP风格因为有清晰的规范标准定义是明显有优点的。咱们能够在开始和实现以前就严格定义相关的接口方法和接口传输数据。(不少状况下是为了兼容之前项目且前台调用逻辑代码都不能动的前提下,更改底层应用,通常就须要使用webService模式开发,由于老代码中已经有了明确的方法定义以及参数类型、个数等申明)
简单数据操做,无事务处理,开发和调用简单这些是使用REST架构风格的优点。而对于较为复杂的面向活动的服务,若是咱们仍是使用REST,不少时候都是仍然是传统的面向活动的思想经过转换工具再转换获得REST服务,这种使用方式是没有意义的。