总结

设计模式

一.六大设计原则

1.开闭原则:针对扩展开放,修改关闭;

2.里氏替换原则:任何父类出现的地方都可由其子类进行替换;

3.依赖倒置原则:依赖于抽象而不是具体,面向接口编程;

4.单一职能原则:使用多个隔离的接口,比使用单个接口要好;

5.迪米特法则:一个类应该尽可能少与其他类发生相互作用,保持系统模块的相对独立;

6.合成复用原则:尽可能使用合成/聚合模式,而非继承。

二、模式分类

1.创建型模式:单例模式、工厂方法、抽象工厂、建造者模式以及原型(clone)模式;

2.结构型模式:适配器模式、装饰模式、代理模式、桥接模式、外观模式、组合模式、享元模式

3.行为型模式:策略模式,模板方法模式,责任链模式,观察者模式,迭代器模式,访问者模式,中介器模式,解释器模式,命令模式,备忘录模式以及状态模式。

三、详细模式分解

1.工厂方法:对于实现相同接口的一系列类的实例化操作,包括普通工厂(传递工厂值类型字符串),多工厂方法(工厂类中提供多个工厂方法)以及静态工厂方法(工厂类中方法均为静态方法),缺点是类的创建依赖于工厂类,扩展需要修改工厂类,违背闭包原则。

2.抽象工厂:解决工厂方法的弊端,程序的扩展只需要新增新的工厂类,而不需要修改之前的逻辑。

3.单例模式:保障JVM中只有一个对象的实例存在,省去创建的繁琐,降低系统内存使用频率,减轻GC压力,保障核心业务不紊乱。(1)构造方法私有;(2)懒加载,延迟加载考虑性能,安全需要做synchronize同步处理;(3)饿汉模式,简单安全,性能有影响。

4.建造者模式(Builder):将各个产品几种起来管理,创建复合对象,而工厂是创建单个产品。

5.原型模式(Prototype):将一个对象作为原型进行克隆,复制产生一个与原对象类似的新对象,clone(),深拷贝、浅拷贝。

6.适配器模式:将某个已有类的接口转换为客户期望的另一个接口表示,消除接口不匹配造成的类兼容问题,分为:

(1)类的适配器模式:创建新类,继承原类,实现新接口;

(2)对象的适配器模式:创建wrapper类,持有原类,在Warpper类中调用原类实例的方法执行;

(3)接口的适配器模式:创建抽象类Wrapper,实现所有接口方法,再写别的类时继承抽象类。

7.装饰模式:动态给被装饰的对象添加额外的功能,通过持有被装饰对象,重写相同接口方法,在方法内部调用被装饰对象同名方法。

8.代理模式:多一个对象出来,替原有对象进行一系列操作,中介、律师。

9.外观模式:提供一致的统一操作界面,如电脑开机(CPU,内存,磁盘Startup方法),Spring的类关系配置。

10.桥接模式:把视图与其具体实现分离,抽象化与实现化解耦,二者可以独立变化,如JDBC数据库驱动程序,数据库的切换不需要修改代码,通过JDBC桥接即可。

11.组合模式:部分-整体树形结构问题处理,将多个对象组合一起进行操作。

12.享元模式:实现对象的共享,即池化,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。sqlsession链接。

13.策略模式:定义一系列的算法,每个算法封装起来,可以相互替换,且算法本身的变化不会影响到使用算法的客户。计算器,商城打折。

14.模板方法模式:使用一个抽象类,定义算法的骨架流程,而将差别细节留给具体的实现类实现。

15.观察者模式:订阅发布模式,一个对象发生变化时,其他依赖改对象的所有对象都会受到通知,并随之发生改变。

16.迭代器模式:顺序访问结合中的所有元素。

17.责任链模式:每个对象持有下一个对象的引用,请求在这条链上进行传递,直到某一对象决定处理改请求。

18.命令模式:命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开,熟悉Struts的同学应该知道,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想。数据库事务机制的底层实现。

19.备忘录模式:副本,保存一个对象的状态,以便在适当的时候恢复对象,游戏副本。

20.状态模式:当对象的状态改变时,同时改变其行为。

21.访问者模式:访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。

22.中介者模式:中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。

23.解释器模式:

JVM

1.什么时候发生栈内存溢出

· 栈是线程私有的,每个方法的调用都会产生一个栈帧,用来存储局部变量表,操作数栈,动态链接以及方法返回值等。

· 当线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError异常(方法递归)

· 如果Java虚拟机栈尝试动态扩展失败,无法申请足够的内存完成扩展或者新建立线程时没有足够的内存去创建对应的虚拟机栈,将抛出OutOfMemory异常(线程启动过多)

· 参数-Xss调整JVM栈的大小

2.JVM 内存模型

[图片上传失败...(image-553b8b-1609729780955)]

image.png

· PC:线程执行的字节码的行号指示器,线程私有;

· Java虚拟栈:栈帧结构,存放基本数据类型,对象引用,方法出口等,线程私有;

· Native方法栈:与Java虚拟机栈相似,服务于Native方法,线程私有;

· Java堆:内存最大的一块,所有对象实例,数组存放区域,GC回收的地方,线程共享;

· 方法区: 存放已经被加载的类信息,常量,静态变量,JIT热点代码。回收目标主要是常量池的回收和类型的卸载,线程共享。

3.JVM新生代、老年代、永久带,新生代为什么分为Eden和Survivor

· 共享内存=堆+永久带

· 永久带=方法区+其他

· java堆=老年代+新生带(2:1通过-XX:NewRatio设置)

· 新生带=Eden+S0+S1(8:1:1,通过-XX:SurvivorRatio设置,Survivor中对象被复制次数为15,-XX:MaxTenuringThreshold设置)

如果没有Survivor区,Eden每进行一次Minor GC,存活对象被送到老年代,老年代很快填满,触发Major GC。老年代空间大于新生代,进行Full GC消耗比Minor GC大,所以区分Eden与Survivor。只有经历过16次Minor GC还存活的对象才被送到老年代。两个Survivor区的设置是为了解决内存碎片化。

4. Full GC****流程,对象晋升老年代

· 大对象直接进入老年代;

· 对象在Eden出生并存活了年龄次数(16),则进入老年带;

· 当老年代满了无法容纳更多的对象,触发Full GC,清理整个内存堆,包括年轻代与老年代;

· Major GC是在老年代的GC,清理老年代,至少伴随一次Minor GC ,比Minor GC慢10倍以上。 空间分配担保:在触发minor GC时,虚拟机检查老年代最大可用连续空间是否大于新生代所有对象空间,如果条件成立,Minor GC确保是安全的,若老年代判断到剩余空间不够,进行一次Full GC。

5. 垃圾回收器,各自优缺点,重点讲下CMS和G1,包括原理,流程,优缺点。

1.几种垃圾回收器

· Serial收集器:单线程收集器,发生STW,使用复制算法;

· Parnew收集器:Serial收集器的多线程版本,发生STW,复制算法;

· Parallel Scavenge收集器:新生代收集器,复制算法,并发的多线程收集器,目标是达到一个可控的吞吐量。

· Serial Old收集器:Serial收集器的老年代版本,单线程收集器,使用标记整理算法;

· Parallel Old收集器:Parallel Scavenge收集器的老年代版本,使用多线程,标记整理算法;

· CMS收集器:获取最短回收停顿时间为目标的收集器,标记清除算法。运作过程: (1)初始标记:只标记GC Root直接关联的对象,发生STW,速度快; (2)并发标记:从GC Roots找到它能引用的所有其他对象的过程,可能产生浮动垃圾; (3)重新标记:修正并发标记期间因用户线程继续执行导致标记发生变动的那部分对象的标记记录,发生STW,比初始标记耗时,低于并发标记; (4)并发清理:

· G1收集器:标记整理算法,运作流程:初始标记-》并发标记-》最终标记-》筛选标记,采用标记整理算法不产生内存空间碎片。

CMS vs G1

· CMS 只收集老年代,配合新生代的Serial与ParNew使用,G1收集新生代与老年代,不需要结合其他收集器

· CMS以最小停顿时间为目标,G1可预测垃圾回收的停顿时间;

· CMS采用标记清除算法回收垃圾,容易产生内存碎片,G1使用标记整理算法,空间整合无内存碎片。

6.JVM****内存模型,重排序,内存屏障,happen-before

· 内存模型:所有变量存储于主内存,每条线程有独立的工作线程,线程的工作内存中保存了主内存的副本,线程对变量的操作必须在工作内存中进行,不能直接操作主内存,不同线程之间无法直接访问对方工作内存中的变量,线程间变量的传递需要工作内存与主内存间进行数据同步。

· 指令重排序:通过乱序执行的技术,处理器大大提高执行效率,这就是指令重排。

· 内存屏障:也叫内存栅栏,是一种CPU指令,控制特定条件下的重排序与内存可见性问题。LoadLoad、LoadStore,StoreStore,StoreLoad屏障。

7.****类加载器

类加载器就是根据指定的全限定名称将class文件加载到JVM内存,转为class对象,分为启动类加载器,扩展类加载器,应用程序类加载器,其他类加载器。

· 启动类加载器:Native方法。将JAVA_HOME\lib目录或-Xbootclasspath指定的路径中的类加载到内存;

· 扩展类加载器:负责加载JAVA_HOME\lib\ext目录中的所有类;

· 应用程序类加载器:负责加载用户类路径上的指定类库。

双亲委派模型:当一个类加载器收到加载类的请求时,先不去尝试加载,而是把这个请求委派给父类加载器完成, 每个加载器都是如此,只有父类加载器在自己的搜索范围内找不到指定类时,子类加载器才去尝试加载。确保了类唯一性,防止内存出现多份同样的字节码。

打破双亲委派机制:继承ClassLoader类,并重写loadClass和findClass方法。

8.****常用JVM参数

· 堆栈配置相关:-Xmx,-Xms, -Xmn, -Xss,-XX:MaxPermSize, -XX:NewRatio, -XX:SurvivorRatio,-XX:MaxTenuringThresholdd.

· 垃圾收集器相关:-XX:+UserParallelGC,-XX:ParallelGCThreads=20,-XX:+UserConcMarkSweepGC,-XX:CMSFullGCsBeforeCompaction,-XX:+UseCMSCompactAtFullCollection

9.JVM****调优工具

[图片上传失败...(image-567be3-1609729780954)]

Spring,MyBatis

1. IOC

IOC:控制反转,将原本在程序中手动创建对象的控制权交给Spring框架来管理。IOC容器是Spring用来实现IOC的载体,本质就是一个Map,Map中存放各种对象。将对象的依赖关系交给IOC容器管理,由IOC完成对象的注入。

2.AOP:动态代理

面向切面编程:将那些与业务无关,却为业务模块所共同调用的逻辑或责任(如事务处理,日志管理,权限控制)封装起来,减少系统的重复代码,降低模块间的耦合度。

3.Spring****中bean的作用域

· singleton:唯一bean实例,Spring中bean默认都是单例;

· prototype:每次请求创建一个新的bean实例;

· request:每一次HTTP请求创建一个新的bean,仅在当前HTTP request块内有效;

· session:每一次HTTP请求产生一个新的bean, 仅在当前HTTP session内有效;

· global-session:全局session作用域。

4.Bean****的生命周期

[图片上传失败...(image-c62a37-1609729780954)]

image.png

配置文件中定义Bean-》反射实例化bean对象-》设置对象属性-》检查Aware相关接口并设置相关依赖-》BeanPostProcessor前置处理-》检查是否是InitializingBean决定是否调用afterPropertiesSet方法-》检查是否配置有自定义的init-method-》BeanPostProcessor后置处理-》注册必要的Destruction相关回调接口-》使用bean-》是否实现DisposableBean接口-》是否配置有自定义的destory方法。

5.Spring MVC****的工作原理

[图片上传失败...(image-4b2484-1609729780954)]

image.png

(1). 前端浏览器request请求url;
(2). 在处理器映射器(HandlerMapping)中请求查找handler; (3).返回一个执行链; (4).请求适配器执行Handler; (5).Handler适配器调用Handler处理器执行请求方法 (6).返回ModelAndView给处理器适配器;
(7).返回ModelAndView给前端控制器; (8).前端控制器向视图解析器请求进行视图解析; (9).视图解析器返回View给前端控制器 (10).视图渲染,将模型数据填充到request域; (11).response响应浏览器。

6.Spring 框架中使用的设计模式

· 工厂设计模式:Spring使用工厂模式,通过BeanFactory与ApplicationContext创建bean对象;

· 代理设计模式:AOP实现使用动态代理;

· 单例设计模式:Spring的bean默认都是单例的;

· 模板方法模式:Spring中的jdbcTemplate,hibernateTemplate等以Template结尾的对数据库操作的类使用模板方法;

· 观察者模式:Spring事件驱动模型就是观察者模式的经典应用;

· 适配器模式: Spring AOP的增强或者通知Advice使用了适配器模式。Controller适配器查找也使用了。

7.Spring****的事务传播行为

支持事务情况

· PROPAGATION_REQUIRED:如果存在当前事务,则加入该事务;如果没有事务,创建一个;

· PROPAGATION_SUPPORTS:如果存在当前事务,则加入该事务;如果没有事务,以非事务方式继续运行;

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

不支持事务的情况

· PROPAGATION_REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起;
· PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果存在当前事务,则把当前事务挂起;
. PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常;

其他情况

· PROPAGATION_NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务运行;如果没有事务,则该取值等价于PROPAGATION_REQUIRED。

8. Spring****注入方式

Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入。

9. Spring****事务的实现方式

· 使用TransactionTemplate事务模板对象

· 使用事务管理器PlatformTransactionManager对象

· 基于Aspectj AOP开启事务

· 基于注解@Transactional声明式事务管理

WEB

一、转发与重定向

转发是服务器行为,重定向是客户端行为

· 转发在服务器端完成的;重定向是在客户端完成的

· 转发的速度快;重定向速度慢

· 转发的是同一次请求;重定向是两次不同请求

· 转发不会执行转发后的代码;重定向会执行重定向之后的代码

· 转发地址栏没有变化;重定向地址栏有变化

· 转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成

二、GET和POST两种基本请求方法的区别

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

· GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

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

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

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

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

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

· GET****将header和data一并发送,POST分开发送,先发送header得到100响应再发送data部分

Java多线程

1. ThreadLocal简介

ThreadLocal线程变量,ThreadLocal中填充的变量属于当前线程,该变量对于别的线程是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,每个线程访问自己内部的副本变量。

使用场景

· 进行对象跨层传递时候,使用ThreadLocal可以避免多次传递,打破层次间的约束;

· 线程间数据隔离;

· 进行事务处理,用于存储线程事务信息;

· 数据库连接,Session会话管理。

ThreadLocal注意点

  1. Thread中有一个map,就是ThreadLocalMap;

  2. ThreadLocalMap的key就是ThreadLocal,值是我们自己设定的;

  3. ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收;

  4. 当ThreadLocal为null时,需要被垃圾回收,单此时ThreadLocalMap的生命周期与Thread一样,不会回收,就会导致内存泄露,解决方法是使用完ThreadLocal后执行remove操作,避免出现内存溢出情况。

2.Volatile

为了确保多个线程之间对内存写入操作的可见性,用volatile修饰变量,告诉编译器以及运行时该变量是共享的,通过“禁止指令重排序”保证其线程间的可见性。比synchronized更轻量级的同步机制,普通变量会被拷贝到CPU缓存,而volatile修饰的变量设置了内存屏障,保证每次都是从内存中读。

· 保证变量对所有线程的可见性;

· 禁止指令重排序优化。

volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

3.synchronized

解决Java并发问题的常用方法,可重入,避免死锁。底层使用monitor对象完成, MonitorEnter与MonitorExit指令,主要的作用包括:

· 原子性:确保线程互斥的访问同步代码;

· 可见性:保证共享变量的修改能及时可见;

· 有序性:有效解决重排序问题。

用法

· 作用实例方法,监视器锁是对象实例(this);

· 作用静态方法,监视器锁是对象的Class实例,相当于类的全局锁;

· 作用在某一个对象实例,监视器锁是括号括起来的对象实例。

4. 锁优化

增加自适应的CAS自旋锁,锁消除,锁粗化,偏向锁以及轻量级锁等优化策略提升性能,锁的状态有四种:无锁状态01,偏向锁状态0,轻量级锁状态00以及重量级锁状态10。

· 自旋锁: 当一个线程尝试获取某个锁时,如果该锁已经被其他线程占用,就循环检测锁是否释放,而不是进入线程挂起或者睡眠状态。避免了线程切换带来的开销,但是占用了CPU时间,如果长时间未释放锁,自旋的线程就浪费CPU,设置自旋次数优化。

· 适应性自选锁:jdk1.6引入,自旋的次数不固定,由前一次在同一个锁上的自旋时间以及锁的拥有状态来决定。

· 锁消除:为了数据完整性,在操作数据时对这部分操作做同步控制,但是当JVM检测到不可能存在共享数据竞争时,JVM会对这些同步锁进行锁消除。锁消除的依据是逃逸分析的数据支持。

· 锁粗化:将多个连续的加锁,解锁操作连接在一起,扩展成为范围更大的锁。

· 偏向锁:为了在线程交替执行同步块时提高性能,在只有一个线程执行同步块时提升性能,单线程执行代码块时使用的机制,在多线程并发环境下,转换为轻量级锁或者重量级锁。

· 轻量级锁:在没有多线程竞争的前提下,减少传统的重要级锁使用操作系统互斥量产生的性能消耗。

· 重量级锁:依赖于操作系统Mutex Lock所实现的锁称之为重量级锁,实现线程间的切换需要从用户态转换到内核态。这也是synchronized效率低下的原因。

[图片上传失败...(image-ed90f-1609729780952)]

5. 悲观锁与乐观锁

乐观锁:每次操作不加锁,假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功,使用volatile+CAS原语实现; 悲观锁:导致其他需要锁的线程挂起,等待锁的持有线程释放锁,使用synchronized和Lock实现。

6. synchronized与Lock区别

· lock必须在finally释放,否则如果异常,锁可能永远不会释放,而synchronized在发生异常时JVM确保锁自动释放;

· synchronized代码块无法控制进入同步块线程的先后顺序;

· synchronized块必须完整包含在单个函数,而lock对象的lock()与unlock()方法调用可以放在不同方法里。

7. 线程同步的几种方式

· synchronized修饰

· volatile实现同步(无法保证原子性,不安全)

· ThreadLocal

· 原子类:AtomicInteger,AtomicBoolean等

· 使用Lock

· 使用容器类(BlockQueue, ConcurrentHashMap)

8.死锁条件

· 互斥条件:资源是独占且排他使用;

· 不可剥夺条件:进程获得的资源在未使用完毕之前不能被其他进程强行剥夺;

· 请求保持:进程每次申请所需要的一部分资源,在申请新的资源时,继续占有已分配的资源。

· 循环等待:在发生死锁时由依赖环。

9 notify和notifyAll

notify()方法无法唤醒某个具体的线程,所以只适应于单线程等待。而notifyALL()唤醒所有线程并允许他们争夺锁,确保至少有一个线程能继续。

10.wait与sleep差别

· 使用限制:都需要放在synchronized代码块,sleep方法让当前线程休眠,时间到了自动恢复,wait必须调用notify/notifyAll()唤醒;

· 使用场景:sleep用于当前线程休眠,wait多用于多线程间通信;

· 所属类:sleep是Thread的静态类,wait()是Object类本地方法

· 释放锁:sleep不会释放锁,wait释放当前线程对锁的持有;

· 线程切换:sleep会让出CPU执行时间强制切换上下文,wait不会。

11.SynchronizedMap与ConcurrentHashMap区别

SynchronizedMap和HashTable一样,调用map的方法时锁定整个Map,其他线程无法对map再做操作了;而ConcurrentHashMap实现更为精细化的锁定,采用Node+Synchronized+CAS实现桶的精细化控制,一个线程访问Map的某个桶时,其他线程仍然可对map执行其他操作。

12.CopyOnWriteArrayList

读写分离,读时不加锁,写时先拷贝一份,实现新旧版本分离,然后在拷贝的版本上进行修改操作,修改完成后再更新到旧版本中,避免频繁读时锁定整个数据。

13.并发类CountDownLatch、Semaphore和CyclicBarrier

· CountDownLatch:计数器闭锁,阻塞当前线程,一个或多个线程一致等待,直到其他线程执行的操作完成;

· Semaphore:与CountDownLatch类似,不同的是Semaphore的值被获取后是可以释放的,用来限制流量,限制资源最多被N个线程访问,超过N个不允许再有线程访问,使用资源的线程结束后释放才允许新线程进来;

· CyclicBarrier:同步辅助类,允许一组线程相互等待,直到到达某个公共屏障点;

14.CAS

CAS是乐观锁技术,多个线程尝试 使用CAS更新变量时,只有一个线程能更新成功,其余线程都失败,失败的线程不会挂起,而会去重试。其实现原理是通过内存位置(V)、预期原值(A)与新值(B)。如果内存位置的值与预期原值匹配,处理器自动将该位置更新为新值,否则不做处理,返回V值。

· ABA问题:两个线程同时读取A,线程1取出A,线程2取出A后操作变成B,再操作变成A,线程1操作CAS发现仍然是A,操作成功。解决ABA问题,每次比较数据的值与版本号。

15.读写锁ReentrantReadWriteLock

面对多线程环境下,数据读写不一致的问题,采用不同的读写锁机制:读读不互斥,读写互斥,写写互斥,ReentrantReadWriteLock允许多个读线程获取读锁,但只允许一个写线程获取写锁。

16.进程,线程,协程

进程:程序在一个数据集中的一次动态执行过程,是CPU资源分配与调度的独立单位,由程序,数据集,进程控制块组成。 线程:轻量级进程,CPU的基本执行单元,由线程ID,PC,寄存器集合以及堆栈共同组成,一个进程包含多个线程。 协程:用户态的轻量级线程,微线程,协程的调度完全由用户控制。通过yield转移执行权,与多线程对比,其优势在于:(1)协程执行效率高,不需要进行线程切换;(2)不需要多线程锁机制,共享资源不加锁,通过判断状态。

17. 线程转态转移

[图片上传失败...(image-70834c-1609729780949)]

· 新建态:调用start进入就绪态

· 就绪态:获得时间片进入运行态

· 运行态:(1)运行任务结束进入死亡态;(2)调用sleep,join,等待用户输入进入阻塞态;(3)没有获取synchronized锁,进入锁池队列;(4)调用wait方法,释放锁,进入等待队列

· 阻塞态:sleep结束,join完成,或者用户输入完成进入就绪态

· 等待队列:锁对象调用notify/notifyAll通知线程进入锁池队列

· 锁池队列:获得锁,进入就绪状态

· 死亡态:run()方法运行结束

18.线程池中submit()和execute()方法有什么区别

· execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)

· execute() 没有返回值;而 submit() 有返回值

· submit() 的返回值 Future 调用get方法时,可以捕获处理异常

19.****常见线程池

newFixedThreadPool:创建一个固定线程数的线程池 newSingleThreadExecutor:创建只有1个线程的线程池 newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程 数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在60秒 后自动回收。 newScheduledThreadPool: 创建一个可以指定线程的数量的线程池,但是这个线程池还带有 延迟和周期性执行任务的功能,类似定时器。 newSingleScheduledThreadPool: 创建一个单线程的线程池,具备延迟和周期性执行任务的功能。 ForkJoinPool:线程池中每个线程都有自己独立的任务队列,常适合用于递归的场景,例如树的遍历、最优路径搜索等场景.

[图片上传失败...(image-b2e253-1609729780949)]

image.png

20.****线程池常见拒绝策略

· ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

· ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

· ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 .

· ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

RabbitMQ

一、什么是RabbitMQ,有什么优缺点?

RabbitMQ是一款开源的基于AMQP协议的消息中间件,可以用来解耦,异步以及削峰。

优点 1.解耦:系统间通过消息通信,不用关心其他系统的处理 2.削峰:可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求 3.异步:相比于传统的串行、并行方式,提高了系统吞吐量

缺点 1.降低了系统的稳定性,消息系统挂掉会导致系统不可用; 2.增加系统的复杂性:消息队列加入考虑一致性问题,消息重复消费,如何保障消息可靠传输等。

考虑集群以及主从等模式保障RabbitMQ的高可用

二、如何保证RabbitMQ不被重复消费。

正常情况下,消费者在消费消息完后发送一个确认消息给消息队列,消息队列再将消息从队列中删除。但因为网络传输等故障,确认消息没有传输到消息队列,导致消息队列不知道消息已经被消费,再次将消息分发给其他消费者。 针对上述问题,解决思路是保证消息的唯一性,就算多次传输,不要让消息的多次消费带来影响,保证消息等幂性,如在写入消息队列的数据做唯一标识,消费消息时,根据唯一标识来判断消息是否被消费过。

三、如何保证RabbitMQ消息的可靠传输

消息不可靠的情况可能是消息丢失,消息劫持等。丢失又分为生产者丢失消息,消息列表丢失消息,消费者丢失消息。

  1. 生产者丢失消息:RabbitMQ提供了transaction与confirm模式确保生产者不丢消息。

· transaction机制就是消息发送前,开启事务,然后发送消息,如果发送过程中出现异常,回滚事务,如果发送成功就提交事务,这种模式可能会导致吞吐量下降。

· confirm模式用的更多,通过ACK确认+重发消息保障。

  1. 消息队列丢消息:消息持久化。将消息持久化到磁盘,再给生产者发送ACK信号。

  2. 消费者丢失消息:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可。当消息处理成功后,手动回复确认消息。

四、如何保证RabbitMQ消息的顺序性?

单线程消费保证消息的顺序性;对消息进行编号,消费者处理消息是根据编号处理消息;

Redis知识点

一、支持的数据类型5类

redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

. String字符串,底层是SDS(Simple Dynamic String),动态扩容的简单字符串,类似于Java的ArrayList。

· 预分配冗余空间:预分配冗余空间的方式减少内存的频繁分配,小于1MB,加倍扩容,超过1MB时,每次扩容1MB,最大512MB。

· 惰性空间释放:惰性空间释放,当需要缩短SDS保存的字符串时,程序不立即使用内存重分配来回收缩短后多出来的字节,而是用free成员将这些字节记录下来等待将来使用。

. Hash:哈希
. List:列表
. Set:集合
. ZSet:有序集合,支持ziplist以及skiplist两种编码。

· ziplist:满足元素小于128个并且所有member长度小于64字节。使用紧挨在一起的压缩列表节点来保存,第一个节点保存member,第二个保存score,ziplist的集合元素按score排序,实质上是双向链表。

内存结构:

[图片上传失败...(image-927762-1609729780948)]

image.png

(1).zlbytes:存储压缩列表占用字节,重新分配内存时使用,不用遍历列表计算占用内存大小; (2).zltail:表示ziplist表中最后一项在ziplist中的偏移位置,方便定位到尾节点。 (3).zllen:ziplist包含的节点个数,2个字节长度。 (4).entry:menber,存储的数据项 (5).zlend:结束标识,一字节固定值,255.

skiplist(重点考察):字典+跳表,不满足ziplist条件都使用skiplist。查找时间复杂度平均为log(N),最差为O(N),基本等效于平衡树,但比平衡树实现简单。

跳表的特点

(1). 由许多层结构组成; (2).每一层都是一个有序的链表; (3).最底层包含所有的元素; (4).如果一个元素在Level i的列表中出现,则它在Level i之下的链表都会出现; (5).每个节点包含2个指针,一个指向同链表的下一个元素,另一个指向下面一层的元素。

查找过程:

  1. 从链表头部元素开始,遍历链表,直到找到元素大于或等于目标元素的节点,如果当前元素正好等于目标,直接返回它;

  2. 如果当前元素小于目标元素,垂直下降到下一层继续搜索;

  3. 如果当前元素大于目标或已经达到链表尾部,则移动到前一个节点位置,再垂直下降到下一层。

二、Redis持久化

持久化就是把内存的数据写到磁盘,防止服务器宕机内存数据丢失。Redis提供2种持久化方式RDB和AOF。

RDB:

即redis database,功能核心函数rdbSave (生成RDB文件)和rdbLoad(从文件加载到内存)

AOF:

Append-Only file,每次执行服务器定时任务或者函数时flushAppendOnlyFile函数都会被调用,执行2个工作,WRITE:根据条件将aof_buf中的缓存写入到AOF文件;SAVE:根据条件调用fsync函数,将AOF文件保存在磁盘里。使用redis持久化协议RESP格式的命令存储文本。

RDB vs AOF

优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。

缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

  1. aof文件比rdb更新频率高,优先使用aof还原数据。

  2. aof比rdb更安全也更大;

  3. rdb性能比aof好;

  4. 优先加载aof;

  5. 恢复难度aof高。

三、Redis的淘汰策略

· noeviction:不删除策略,达到最大内存限制后,直接返回错误信息;

· allkeys-lru:优先删除最少使用的key;

· volatile-lru:只限于设置了expire的部分,优先删除最近最少使用的key;

· allkeys-random:随机删除一部分key;

· volatile-random:只限于设置了expire的部分,随机删除一部分key;

· volatile-ttl:只限于设置了expire的部分,优先删除剩余时间短的key。

四、Redis的并发竞争

单进程单线程模式,采用队列模式将并行访问变为串行访问。Redis本身没有锁概念,对于多个客户端连接不存在竞争,利用setnx实现锁,并设置expire过期释放锁。

五. Redis单线程为什么这么快

  1. 完全基于内存,纯粹的内存操作非常迅速;

  2. 数据结构简单;

  3. 采用单线程,避免了不必要的上下文切换与竞争条件,不用考虑锁的问题;

  4. 采用多路I/O复用模型,多个网络连接使用一个线程处理。

  5. Redis自己构建了VM机制。

六、Redis缓存被击穿处理机制

使用mutex,当缓存失效时,不立即load db,SETNX去set一个mutex key,当操作返回成功时在进行load db操作并回设缓存,否则重试整个get缓存的方法。

七、Redis缓存穿透,缓存击穿,缓存雪崩

缓存穿透:key对应的数据再数据源并不存在,每次针对此key的请求从缓存中获取不到,请求数据源,就有可能压垮数据源。 有效的解决方法包括:(1)为空值设置缓存;(2)使用布隆过滤器进行过滤. 缓存击穿:key对应的数据存在,但在redis中过期了,若此时大量并发请求过来,发现缓存过期后从后端数据源加载数据并回设缓存,此时大并发的请求可能压垮数据源。 有效的解决办法:使用互斥锁mutex key。 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,会给后端数据源很大的压力。 有效解决办法:(1)用加锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。(2)缓存失效时间分散开,为不同的key设置不同的过期时间。

八、Redis主从模型

典型的分布式读写模型,利用master插入数据,slave提供检索服务,有效减少单个机器的并发访问数量。原则:Master会将数据同步到slave,而slave不会同步到master,Slave启动时会链接master同步数据。 通过增加slave DB数量线性提高读性能;使用MasterDB双机热备避免Master单点故障。 缺陷:所有节点服务器都保存完整的数据,数据量大的情况下,集群的扩展能力受限于单节点存储能力,对于写敏感的应用,读写分离架构不合适。解决,考虑数据分片。

九、Redis架构模式

https://www.cnblogs.com/jasontec/p/9699242.html
1.****单机模式

[图片上传失败...(image-ac47be-1609729780947)]

image.png

特点

· 简单

缺点

· 内存容量有限

· 处理能力有限

· 无高可用

2.****主从复制(读写分离)

允许用户创建多个Redis服务器的副本,Master和Slave,将Master节点数据同步给Slave,Slave扩展使得读性能线性提升。

[图片上传失败...(image-6a5efd-1609729780947)]

image.png

特点

· Master/Slave角色

· Master/Slave数据相同

· 降低从Master读压力,转交给Slave

缺点

· 没有解决master写压力

· 无高可用

3.****哨兵

[图片上传失败...(image-d5988e-1609729780947)]

image.png

Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中包含三个特性:(1)监控:Sentinel不断检查主从服务器是否运作正常;(2)当被监控的Redis服务器出现问题是,Sentinel通过API发送通知;(3)自动故障迁移,当一个主服务器不能正常工作是,Sentinel会开始一次自动故障迁移操作。

特点

· 保证高可用

· 监控各个节点

· 自动故障迁移

缺点

  1. 仍是主从模式,切换需要时间,丢数据

  2. 没有解决master写的压力

4.****集群(proxy)

[图片上传失败...(image-5eb5e1-1609729780947)]

image.png

多主从模式,实现数据分片,通过代理服务器将数据分片到不同的Redis主从集群,每个Redis集群单独有Sentinel哨兵监控。发生故障时,故障的Redis集群自动下线。缺点是:

· failover逻辑需要自己实现,不支持故障自动转移,可扩展性差,进行扩缩容都需要手动干预。

5.****集群(直连)

[图片上传失败...(image-abf9d1-1609729780947)]

image.png

redis3.0之后支持redis-cluster集群,采用无中心结构,每个节点保存数据与整个集群状态,每个节点与其他节点相互连接。

特点

· 无中心架构,少了proxy层

· 数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布

· 可扩展性,支持节点动态添加与删除

· 高可用,部分节点不可用,集群仍可用

· 实现自动故障failover,节点之间通过gossip协议交换状态信息,采用投票机制完成Slave到Master的角色提升。

缺点

· 资源隔离线差,容易出现相互影响的情况;

· 数据通过异步复制,无法保证数据的强一致性。

Socket通信原理

一、Socket是什么?

Socket是应用层与传输层之间的软件抽象层,他是一组接口,使用外观设计模式实现,将复杂的TCP/IP协议族隐藏在Socket接口后面。 Socket可以理解为一种特殊的文件,通过提供的函数进行文件操作(读写IO,打开,关闭)。

连接过程 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

IP****唯一标识网络中的主机,传输中的协议+端口可以标识主机中的应用程序。(IP地址,协议,端口)标识网络进程。

[图片上传失败...(image-5078e5-1609729780946)]

image.png

二、主要函数

1. socket()

socket()对应普通文件的打开操作,普通文件返回文件描述符,socket()返回一个socket描述字,唯一标识socket,后续读写,绑定等都用到。

· domain协议族:决定socket的地址类型,IPV4,IPV6;

· type:指定socket类型,SOCKET_STREAM,SOCKET_DGRAM,SOCKET_RAW等;

· protocol:指定协议,如IPPROTO_TCP,IPPTOTO_UDP传输协议。

2.bind()函数

将一个地址族中的特定地址赋给socket.例如AF_INET就是把一个ipv4地址和端口号组合赋给socket。

3.listen()与connect()

服务器调用socket()与bind()之后调用listen()监听socket,如果客户端调用connect()发出连接请求,服务器就会接收这个请求(三次握手的第一次握手)。

4.accept()

TCP服务器依次调用socket(),bind(),listen()之后,就会监听指定的socket地址。TCP客户端依次调用socket(),connect()之后就向TCP服务器发送一个连接请求。TCP服务器监听到这个请求之后,调用accept()接收请求并建立连接。(处于三次握手的第二次握手)

5.read()/write()函数

调用网络I/O读写,实现网络中不同进程之间的通信。

6.close()函数

完成读写操作后关闭相应的socket描述字,类似普通文件fclose().

三、TCP三次握手与四次挥手

[图片上传失败...(image-38f3cf-1609729780946)]

image.png

[图片上传失败...(image-b2b384-1609729780946)]

image.png

四、epoll多路复用

解决单个RECV只能监视单个的Socket,Select方法效率低下的问题。

· epoll将维护等待列表与阻塞进程拆分;

· 维护就绪列表,采用回调方式避免遍历哪个Socket接收到了数据。

JAVA 基础

一、集合

1.常见的集合有哪些

Map接口和Collection集合是所有集合框架的父接口:

  1. Collection接口的子接口包括List和Set;

  2. Map接口的实现类主要有HashMap,TreeMap,HashTable,ConcurrentHashMap以及Properties;

  3. Set接口实现的类主要有:HashSet,TreeSet以及LinkedHashSet等;

  4. List接口的实现类主要有:ArrayList,LinkedList,Stack以及Vector

2.HashMap与HashTable的区别

· HashMap不考虑同步,是线程不安全的;Hashtable使用synchronized关键字修饰方法,是线程安全的;

· HashMap允许K/V都为null;后者K/V都不允许为null;

· HashMap继承自AbstractMap类;Hashtable继承自Dictionary;

红黑树: 平衡的二叉搜索树,具有如下特点:1.树中的节点要么是红色要么是黑色;2.根节点是黑色;3.叶子节点都是null值得黑节点;4.每个红节点包含两个黑节点;任意节点到其叶子节点路径上的黑节点数目相同。插入和删除可能破坏这些原则,通过变色与旋转自适应。

3.HashMap的put方法的流程

4.HashMap的扩容,resize()函数,根据是初始化还是达到阈值分情况扩容。

5.HashMap如何解决冲突

哈希是把任意长度的输入通过散列算法,变换成固定长度的输出,输出值就是散列值。通常散列值的空间远小于输入的空间,因此不同的输入可能会散列成相同的输出,不能从散列值来唯一的确定输入值。 哈希冲突就是两个不同的输入,通过相同散列函数计算得出相同的散列值的现象,称之为碰撞。

jdk1.8中,将高位参与到hash key的计算中,降低hash碰撞的概率,使得数据分布更加平均,我们把这样的操作称之为扰动,相比jdk1.7的4次位运算,5次异或(9次扰动),jdk1.8中只进行一次位运算和一次异或运算(2次扰动)。

Hashmap****如何解决哈希冲突

  1. 使用链地址法,链接具有相同hash值的数据;

  2. 使用2次扰动函数降低哈希冲突的概率,使得数据分布更加平均;

  3. 引入红黑树进一步降低遍历的时间复杂度。

6. HashMap为什么不直接使用hashCode的哈希值作为table下表?

hashcode()返回的是int类型,范围是-(231)~(231 -1),而HashMap的容量范围在16~2 ^30,导致hashcode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置。

如何解决?

  1. HashMap自己实现的hash()方法通过两次扰动是的自己的哈希值高低位进行异或运算,降低哈希碰撞概率,使数据分布更平均。

  2. 使用hash运算之后的值与运算获取数组下标进行存储相对取模操作更加有效率;只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length;解决了哈希值与数组大小范围不匹配的问题。

7.HashMap在jdk1.7和jdk1.8中的不同

· 存储结构:jdk1.7采用数据+链表;jdk1.8采用数组+链表+红黑树。

· 初始化方法:jdk1.7单独函数inflateTable();jdk1.8直接集成到扩容函数resize()中。

· hash计算方法:jdk1.7扰动处理=9次扰动=4次为运算+5次异或;jdk1.8只有2次扰动次数=1次位运算+1次异或运算。

· 存储数据的规则:jdk1.7,无冲突放数组,冲突时,放链表;jdk1.8,无冲突放数组,冲突&链表长度<8,放链表;冲突&链表长度>8,树化存放红黑树;

· 插入数据方式:jdk1.7头插法,jdk1.8尾插法;

8.为什么HashMap中String、Integer包装类适合做K?

Integer,String等包装类的特性能保证Hash值得不可更改性(final修饰)与计算准确性(重写了equals()与hashcode()方法),有效减少Hash碰撞的几率。

9.ConcurrentHashMap和HashTable的区别

ConcurrentHashMap结合了HashMap与HashTable的优势,HashMap没有考虑同步,而HashTable虽然考虑了同步但效率低下(锁定整个哈希表),ConcurrentHashMap锁是稍微细粒度的。

ConcurrentHashMap****的实现原理 在jdk1.7中,ConcurrenHashMap采用Segment+HashEntry方式实现,结构如下:

[图片上传失败...(image-58f056-1609729780946)]

image.png

  1. 包含两个静态内部类HashEntry和Segment:前者用来封装映射表的键值对,后者充当锁的角色;

  2. Segment使用重入锁ReentrantLock,每个Segment守护一个HashEntry数组,对HashEntry数组的操作必须先获得对应的Segment锁。

在jdk1.8中,放弃了Segment臃肿的设计,取而代之使用Node+CAS+Synchronized保证并发安全进行实现:

[图片上传失败...(image-de0393-1609729780946)]

image.png

二、泛型

1.Java泛型概念

泛型即参数化类型,引入目的包含:

  1. 类型安全

· 泛型的主要目的是提高java程序的类型安全;

· 编译期间可以检查出因java类型不正确导致的ClassCastException异常;

· 符合越早出错代价越小的原则。

  1. 消除强制类型转换:使用时直接得到目标类型,消除强制类型转换,所得即所需。

  2. 潜在收益:支持泛型几乎不需要JVM或类文件更改,所有转换在编译器中完成。

2.通配符super和extend

super 声明类型的下界,表示参数化的类型可能是指定的类型或者是此类型的父类; extends声明类型的上界,表示参数的类型可能是指定的类型或者是此类型的子类; <? super E>用于灵活写入或比较,使得对象可以写入父类型的容器,父类型的比较方法可以应用于子类对象; <? extends E>用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象。 生产者有上限,消费者有下限。

3.泛型擦除

当编译器对带有泛型的java代码进行编译时,它会去执行类型检查和类型推断,然后生成普通的不带泛型的字节码,这种普通的字节码可以被一般的 Java 虚拟机接收并执行,这在就叫做 类型擦除(type erasure)。

三、IO

  1. IO流

[图片上传失败...(image-e51f6a-1609729780945)]

image.png

2.BIO,NIO,AIO

BIO :同步阻塞I/O,一个连接一个线程,客户端有连接请求服务器就启动一个线程处理,线程池优化,适用于连接数目比较小且固定的架构。 NIO:同步非阻塞I/O,一个请求一个线程,客户端发送的连接请求注册到多路复用器上,多路复用器轮询到有I/O请求时才启动一个线程进行处理,适用于连接数目多且连接时间短的架构。 AIO:异步非阻塞I/O,一个有效请求一个线程,客户端的IO请求都是由操作系统完成之后再通知服务器启动线程处理,AIO适用于连接数目多且连接比较长的架构。

对于NIO,它是非阻塞式,核心类:

· Buffer为所有的原始类型提供 (Buffer)缓存支持。

· Charset字符集编码解码解决方案

· Channel一个新的原始 I/O抽象,用于读写Buffer类型,通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。

BIO 与 NIO区别

· BIO面向流,NIO面向缓冲区;

· BIO是阻塞的,NIO是非阻塞的;

· Java NIO允许使用一个单独的线程来监视多个输入通道,可以注册多个通道使用一个选择器,然后用一个单独的线程来选择通道。

四、反射

  1. java反射机制

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。ReflflectASM工具类,通过字节码生成的方式加快反射速度。

优点:反射可以动态执行,运行期间根据业务功能动态执行方法,访问属性,发挥java的灵活性; 缺点:对性能影响,操作慢于执行java代码。解决方法:(1)通过setAccessible(true)关闭JDK的安全检查;(2)多次创建一个类的实例时使用缓存;(3)使用ReflectASM工具类,通过字节码生成方式加快反射速度。

2.序列化

将对象中的数据编码为字节序列的过程叫对象序列化,反之,将对象的编码字节重新反向解码为对象的过程叫反序列化,遵循约定:(1)、实现序列化接口;(2)、添加序列化版本号;(3)、不想序列化的字段用transient修饰。

  1. 动态代理 动态代理是运行时动态生成代理类,动态代理的应用有Spring AOP数据查询,Java注解对象的获取等。实现动态代理包括JDK原生动态代理(基于接口实现)以及cglib动态代理(基于继承当前类的子类实现)

MySQL****知识点
1.为什么用自增列作为主键

  • 如果定义了主键,InNoDB选择主键作为聚集索引,如果没有选择读一个不包含NULL值得唯一索引作为主键索引,如果没有这样的列,选择内置的6字节长的ROWID作为隐含的聚集索引;

  • 数据记录本身被存储于B+树的叶子节点上,同一叶子节点内的数据记录按主键 顺序存放,新记录插入时,MySQL根据主键插入合适的节点和位置,如果达到装载因子(InnoDB默认是15/16),则开辟新的页;

  • 如果使用自增主键,每次插入新的记录,记录会顺序添加到当前索引节点的后续位置;

  • 如果使用非自增主键,每次主键值近似于随机,每次新记录要被插入到现有索引页的中间某个位置,MySQL不得不为了将新记录插入到合适位置而移动数据,目标页面可能已经被回写到磁盘而从缓存中清掉,此时又要从磁盘读回来,增大了开销,频繁的移动、分页也会产生大量的碎片,为了紧凑索引结构,需要OPTIMIZE TABLE重建表并优化填写页面。

2.什么是索引,什么是索引覆盖?为什么索引能提高效率,什么时候索引失效,什么情况下不建立或者少建立索引?

索引是为了提高数据检索效率的一种数据结构,分为聚集索引,非聚集索引以及联合索引。

· 聚集索引(主键索引):数据和索引在一起存储的索引方式叫做聚簇索引,一个表只有一个聚集索引;

· 非聚集索引:非聚簇索引记录的物理顺序与逻辑顺序没有必然的联系,与数据的存储物理结构没有关系;

· 联合索引:多个字段组成的索引。

索引覆盖:一个查询语句的执行只需要从索引中就能取得,不必从数据表中读取,不需要回表操作,减少了I/O。

索引之所以能提高效率是因为:1.数据索引的存储是有序的;2.有序的情况下,通过索引查询无需遍历索引记录;3.极端情况下数据索引的查询效率等效为二分查找效率,趋近于log2(N).

索引失效:

· %在前的模糊查找;

· 索引列进行计算;

· 索引列使用函数

· 索引列使用!=查询。

少建索引时机:

· 表记录太少;

· 经常插入,删除修改的表;

· 数据重复且分布平均的表字段;

· 经常和主字段一块查询,但主字段索引值比较多的表字段。

3.B+****树索引与哈希索引的区别

B+树是平衡多叉树,叶子节点间的高度差不超过1,同级之间的节点间有指针相互连接且有序;

[图片上传失败...(image-f20506-1609729780945)]

哈希索引是采用一定的哈希算法,将键值换算成新的哈希值,O(1)复杂度内搜索,无序。

哈希索引的优势 等值查询,哈希索引具有绝对优势(前提:没有大量重复键) 哈希索引不使用的场景:

· 不支持范围查询;

· 不支持索引完成排序;

· 不支持联合索引的最左前缀匹配规则。

4.B****树与B+树的区别。

B树,每个节点都存储key和data,所有节点组成这颗树,叶子节点指针为null,叶子节点不包含任何关键字信息。 B+树,所有叶子节点中包含了全部关键字信息及指向含有这些关键字记录的指针,叶子节点本身根据关键字大小自小而大顺序链接,非终端节点可以看做是索引部分。

B+****树相比B树优势

1、B+树磁盘读写代价更低,B+树内部节点没有指向关键字具体信息的指针,内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘快能容纳的关键字数量也越多,一次性读入内存中的所需要的关键字也越多,相对来说IO读写次数就降低了。 2、B+树的查询效率更加稳定:每次查询效率相当,都是从根节点到叶子节点的全路径。

5.****分库,分区,分表

表分区:根据一定规则,将数据库中的一张表分解成更多个更小的,容易管理的部分,逻辑上看仍是一张表,但底层却是由多个物理页分区组成。 分表:通过一定的规则将表分解成多张不同的表,如将用户订单数据根据时间分成多个表。 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表

表分区的好处

· 存储更多的数据:将数据分布在不同的物理设备锁,高效利用多个硬件设备,与单个设备相比,可以存储更多数据;

· 优化查询:where语句中包含分区条件时,可以只扫描部分分区提高查询效率,涉及聚合查询时可以并行,再汇总。

· 分区表更容易维护:批量删除或清除整个分区。

· 避免某些特殊的瓶颈,例如InNoDB的单个索引的互斥访问,ext3文件系统的inode锁竞争等。

分区表限制因素

· 一个表最多只能有1024个分区;

· MySQL 5.1中,分区表达式必须为整数,MySQL5.5中提供了非整数表达式分区的支持;

· 分区字段要么不包含主键或者索引列,要么包含全部主键和索引列;

· 分区表中无法使用外键约束;

· MySQL分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区不对表分区,也不能只对表的一部分数据分区。

判断当前MySQL是否支持分区: show variables like '%partition%'

MySQL****支持的分区类型 1.RANGE分区:允许数据划分不同范围; 2.LIST分区:允许系统通过预定义的列表的值来进行分割; 3.HASH****分区:允许对表的一个或多个列进行Hash Key进行计算,通过Hash码值进行分区; 4.KEY****分区:Hash模式延伸,Hash Key由MySQL系统产生。

6.****数据库隔离与数据不一致问题

[图片上传失败...(image-49bda0-1609729780944)]

image.png

7.****并发控制

1.InnoDB实现的是多版本控制协议:MVVC
2.与之相对的是基于锁的并发控制协议:LBCC

在MVVC并发控制中,读操作分为两类: 1.读快照:读取的是记录的可见版本(有可能是历史版本),不用加锁; 2.读当前:读取的是记录的最新版本,并且当前读返回的记录都会上锁,保障其他事务不会再并发修改这条记录。

8. mysql****锁

锁是计算机协调进程或者线程并发访问某一资源的机制。保证数据并发访问的一致性有效性。

共享锁与排他锁

· 共享锁(读锁):其他事务可以读,但不能写。

· 排他锁(写锁) :其他事务不能读取,也不能写。 锁粒度 MyISAM 和 MEMEORY 存储引擎采用的是表级锁,InnoDB存储因此既支持行级锁也支持表级锁,默认行级锁。 行锁优缺点

[图片上传失败...(image-d910d5-1609729780944)]

image.png

9.****慢查询

slow_query_log与slow_query_log_file分析。 1.查看是否查询的数据有冗余,去除不需要的冗余字段; 2.explain分析sql,查看分析索引命中情况; 3.数据量过大,考虑分表;

10.mysql****优化

· 开启查询缓存,优化查询

· explain select查询,分析查询语句或表结构的性能瓶颈,explain还会告诉你你的索引主键是如何利用,数据表是如何搜索和排序的。

· 只需要一条记录时使用limit 1,找到记录后停止扫描;

· 为搜索字段添加合适的索引;

· 使用ENUM而不是VARCHAR,提高查询效率

· 使用预编译语句

· 垂直分表

· 选择正确的存储引擎

11.MySQL****中MyISAM与InNoDB的却别。

区别

  1. InnoDB支持事务,MyISAM不支持;

  2. InNoDB支持外键,MyISAM不支持;

  3. InNoDB是聚集索引,数据文件与索引绑定,必须有主键,通过主键索引效率更高;

  4. InnoDB不保存表行数,统计行需要全表扫描,而MyISAM用一个变量保存了整个表的行数,查询效率高;

  5. InnoDB不支持全文检索,而MyISAM支持全文检索,查询效率高

  6. MyISAM不支持行级锁,只支持表级锁。

常见的Web攻击手段

一、XSS攻击

XSS(Cross Site Scripting跨站脚本): XSS定义的主语是“脚本”,是一种跨站执行的脚本,在网站上注入javascript脚本,执行非法操作。

XSS攻击发生的条件是可以执行javascript脚本,一般在站点中总会有发表文章、留言等信息的表单,这种表单一般是写入到数据库中,然后在某个页面进行展示。我们可以在这些表单中直接编写javascript代码(<script>alert("哈哈哈哈,你被攻击了!");</script>)进行测试,看是否可以执行。如果在信息展示页面js代码可以执行,XSS攻击就成功了。

解决方法 1.错误把数据当做了代码执行,因此,对数据中的特殊字符做转义处理。

二、CSRF攻击

CSRF(Cross-site request forgery跨站请求伪造): CSRF定义的主语是”请求“,是一种跨站的伪造的请求,指的是跨站伪造用户的请求,模拟用户的操作.

CSRF攻击能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于cookie中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有token或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于session之中,然后在每次请求时把token 从 session 中拿出,与请求中的 token 进行比对.

解决方法 1.设置HttpOnly,程序无法读取cookie; 2.请求参数增加token; 3.通过Referer识别。

//www.greatytc.com/p/40a12ad4bb75

三、越权攻击

越权是指一般一个用户只能对自己本身的信息进行增删改查,然而由于服务器的一些漏检,导致信息在进行增删改查时没有对用户进行判断,从而导致用户A可以对其他用户进行增删改查等操作。

1.水平越权和垂直越权。

· 水平越权:访问控制攻击漏洞。web应用程序在接收用户请求时,我们在进行增删改查某条数据时候,没有判断数据所对应的用户或者判断数据的用户时是通过从用户表单参数中获取userid来实现的,因此,可以通过修改userid来实现水平越权。

· 垂直越权:又称为权限提升攻击,具体原因是web应用没有做用户权限控制,或只是在菜单做了权限控制,导致恶意用户只要猜测到其他管理页面的URL,就可以访问或者控制其他角色拥有的数据或页面,达到权限提升的目的。

2.越权防范

1.永远不要相信客户端的输入; 2.执行关键操作时必须验证用户身份,多阶段功能每一步都验证用户身份; 3.对于直接对象引用,加密资源ID,防止攻击者对ID进行枚举; 4.在前端的验证不可靠,权限验证在服务器端。

四、DDos攻击

DDos攻击:分布式拒绝服务攻击,利用合理的客户端请求占用过多的服务器资源,从而使合法用户无法得到服务器响应。DDos攻击者借用公共网络,将数量庞大的计算机联合起来作为攻击平台,对一个或多个目标发动攻击,从而达到瘫痪主机的目的。通常,在攻击开始之前,攻击者会提前控制大量的用户计算机,称之为肉鸡,通过指令使大量肉鸡在同一时刻对某个主机发起访问,从而达到瘫痪目标主机的目的。

DDos常见手段

1.SYN Flood:利用TCP协议三次握手的过程达到攻击的目的。攻击者伪造大量的IP地址向服务器发送SYN报文,由于伪造的IP地址不存在,服务器不可能从客户端得到任何回应,服务端维护一个非常的大半连接列表,并不断对这个列表中的IP进行遍历与重试,占用大量的系统资源,当大量恶意客户端信息占满服务器的等待列表时,服务器不再接收新的SYN请求,用户无法完成三次握手与服务器通信。

2.DNS Query Flood:UDPFlood变形攻击,其原理是通过向被攻击的服务器发送海量的域名解析请求,导致DNS服务器瘫痪。通常,请求解析的域名是随机生成的,大部分根本不存在,通过伪造端口和客户端IP,防止查询请求被ACL过滤。被攻击的DNS服务器在接收到域名解析请求时,会在服务器上查找是否有对应的缓存,由于域名是随机生成的,几乎不可能有相应的缓存,当在服务器上无法直接解析域名时,DNS服务器向上层DNS服务器递归查询域名信息。大量不存在的域名解析请求给服务器带来很大的负载,当解析请求超过一定量时,会造成DNS解析域名超时,从而达到攻击目的。

3.CC 攻击:Challenge Collapsar攻击,基于应用层HTTP协议发起的DDos攻击,也称为HTTP Flood。CC攻击的原理是攻击者通过肉鸡或者从互联网上搜寻的大量知名的HTTP代理,模拟正常用户给网站发起请求直到网站拒绝服务为止。大部分网站通过CDN或者分布式缓存加速服务端响应,提升网站吞吐量,而构造的HTTP请求往往有意避开这些缓存,需要进行多次查询DB或者一次请求返回大量的数据加速系统资源消耗,从而拖垮后端业务处理系统。

五、SQL注入攻击

SQL注入:就是通过把SQL命令伪装成正常的HTTP请求参数,传递到服务端,欺骗服务器最终执行恶意的SQL命令,达到入侵目的。攻击者可以利用SQL注入漏洞,查询非授权信息,修改数据库服务器的数据,改变表结构,甚至是获取服务器root权限。

解决方法 1.使用预编译语句; 2.使用ORM框架,支持相应关键字或者特殊字符的转义

六 劫持

运营商通过某些方式篡改用户正常访问的网页,插入广告或针对一些广告联盟或带推广链接的网站,加入推广尾巴。

DNS劫持:用户上网的DNS服务器是运营商分配的,所以,在这个节点上运营商可以为所欲为。例如,对于用户访问的A网站,正常DNS解析后会返回A网站服务器IP,而DNS劫持后,返回的是运营商的中间服务器IP。访问该服务器会一致性的访问302重定向,让用户浏览器跳转到预处理好的网页,再在这个网页中打开原来用户请求的网页。

HTTP 劫持:在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,则拦截处理。后续做法,一种是类似DNS劫持返回302让用户跳转到另外的地址,另外一种是在服务器返回的HTML数据中插入js或dom节点。

如何防范劫持

1.对外网检测,上报被劫持的情况;

2.针对被iframe加载的情况,找到运营商设置的劫持规律(请求url或者cookie被做了标记);

  1. 针对dom节点注入情况,初始化时做检查,后续dom注入也做检查,如果检查dom中含有白名单外的http连接,判定为http劫持。

  2. (1)终端拦截所有返回包,判断ip来自黑名单则丢弃返回包;(2)终端拦截请求包,拆包发送(只检测TCP连接后第一个包是否是HTTP协议,将包拆细,使其无法被是被为HTTP协议);

  3. 根本解决办法,用HTTPS代替HTTP。

linux进程通信关

1.linux进程通信,RPC,共享内存等基本概念;

进程相互独立,内部变量别的进程不可见,由内核提供一份公共资源让多个进程访问。

· 独立性;

· 通过资源共享相互通信;

· 共享由操作系统内核实现。

1 匿名管道与命名管道:

内核提供一段内存(队列),通过内存借助这段内存,完成进程间通信。然后将这段内存抽象成文件,通过访问文件描述符的形式读写这块内存中的数据。

特点:

· 只适用于具有亲缘关系的进程(匿名管道,pipe函数创建),适用任何进程(命名管道,mkfifo创建);

· 单向通信;

· 面向字节流;

· 内置同步互斥机制;

· 生命周期随内存

2 消息队列

本质是内核中的一个链表,在访问消息队列的时候,按照节点为单元,访问数据的读和写。

特点

· 适用于任何进程;

· 全双工,半通信

· 面向数据报,按照节点为单位,一个一个读,一个一个写。

· 内置同步互斥机制

· 生命周期随内核,进程没了,消息队列还在,IPC rm指令手动删除或者重启。

3.共享内存

同一块物理内存通过页表分表映射到不同进程的虚拟地址空间,从而导致,第一个内存变量的修改,物理内存发生变化,第二个内存再去读取时能感知到变量的改变。结合信号量使用,从而达到进程间的同步与互斥。

特点

· 适用于任何进程;

· 双向通信

· 无同步互斥机制

· 不存在面向字节流与数据报的概念,只是一块内存,可以随意读写数据,随机访问(shmget创建,shmat分配,shmdt共享内存段与当前进程脱离,shmctl控制共享内存);

· 共享内存效率极高:访问共享内存与普通内存没有差别,而要进行管道或者消息队列的话涉及反复把数据从内核拷贝到用户,又从用户拷贝到内存,反复拷贝,开销大。

· 生命周期随内核

4 信号量

信号量本身是计数器,通过自身计数的性质完成进程之间的同步和互斥。

特点

· 适用任何进程

· 生命周期随内核

TCP,UDP

一、基本概念

· 序列号seq:四个字节,标记数据段的顺序,第一个字节本地随机产生;

· 确认号ack:四个字节,期待收到对方下一个报文段的第一个数据字节的序号;

· 确认ACK:1位,ACK=1时,确认还ack才有效,否则ack无效;

· 同步SYN:建立连接时用于同步序号,SYN=1表示一个连接请求,或连接接受报文。

· 终止FIN:用来释放一个连接,FIN=1表示,报文段的发送方数据已发送完毕,等待释放运输连接。 ps:ACK, SYN, FIN都是标志位,其值要么是1,要么是0;ack、seq小写单词表示序号。

客户端状态转移:CLOSED->SYN_SENT->ESTABLISED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED 服务器状态转移:CLOSED->LISTENING->SYN_RCVD->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

二、三次握手过程

[图片上传失败...(image-b3b4d2-1609729780943)]

image.png

第一次握手:建立连接,客户端发送sync包(syn=c)到服务器,并进入SYN_SENT状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),发送完毕后,客户端与服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

三、四次挥手过程

[图片上传失败...(image-ef90b6-1609729780943)]

image.png

1.客户端进程发送FIN连接释放包,并停止发送数据,FIN=1,seq=u,客户端进入FIN_WAIT_1状态;

2.服务器接收客户端传递过来的FIN包,发出确认报文ACK=1,ack=u+1,并带上自己的序列号seq=v,此时,客户端进入FIN_WAIT_2状态,服务器进入CLOSE_WAIT状态;

3.服务端将最后的数据发送完毕后,向客户端发送连接释放报文,FIN=1,ack=u+1,服务器可能再次中间过程又发送了一些数据,此时的序列号为w,发送完成之后服务端进入LAST_ACK状态,等待客户端最后确认;

4.客户端收到服务器发送过来的连接释放报文后,发送确认ACK包,ACK=1,seq=u+1,ack=w+1,客户端进入TIME_WAIT状态,并在等待2**MSL后,当客户端撤销相应的TCB后,进入CLOSE状态;服务器接收到客户端发送出的确认包后,同样撤销TCB后,结束TCP连接,因此服务器结束要比客户端早一些。

四、TCP与UDP区别

[图片上传失败...(image-5e1371-1609729780943)]

五、拥塞控制与流量控制

拥塞控制与流量控制是TCP用来解决传输数据过程中产生的问题而采取的两种优化方法。

· 流量控制:为了解决发送发与接收方速度不同而导致的数据丢失问题,当数据发送过快,接收方来不及接收就会导致数据丢失,流量控制采用滑动窗口的形式解决问题。

· 拥塞控制:为了解决过多数据注入到网络,导致网络奔溃,超过负荷。当发送方发送大量数据注入到网络,如果没有限制,网络就会超负荷变卡,拥塞控制采用拥塞窗口解决问题。

窗口的意义

窗口就是缓存区,用于暂时存储数据等待发送与接收,就是对每一次发送的数据大小进行限制,每个窗口都有大小限制,超过部分不能发送,可以不用每次发送报文等待ACK确认,只需要保证发送的报文在发送窗口内部就行,消除了等待确认的时间,大大提高了效率。

流量控制

流量控制通过滑动窗口来实现,发送窗口的大小取决于接收方ACK提供的大小和发送方的拥塞窗口大小的最小值。发送窗口大小不能超过滑动窗口的大小。发送方接收到数据的确认信息,滑动窗口会根据返回的序号动态的改变窗口的位置。滑动窗口的大小得到重置,同时,滑动窗口会根据网络状况动态变化。

拥塞控制

拥塞控制为了解决过多数据注入到网络,导致网络奔溃,超过负荷,拥塞控制主要包含四个策略。

[图片上传失败...(image-c830b6-1609729780943)]

image.png

· 慢开始:窗口先设置为1,每次传输轮次大小增长一倍,直达达到慢开始的门限值,慢开始阶段结束;

· 拥塞避免:慢开始结束后,就是拥塞避免,这个阶段拥塞窗口每个传输轮次加1,直到触发网络拥塞,窗口大小和门限都变为拥塞时最大值的一半,然后重新开始慢开始阶段。

· 快重传:接收方收到顺序错误的数据时不接收数据,同时重复发起对于之前数据的确认,发动到第三次,发送方得知自己的一部分数据丢失立刻重传,不需要等待下一次发送信息时一起发送过去,且重传时触发与拥塞一样的情况,进入快恢复阶段。

· 快恢复:

  1. 当收到3个重复ACK时,把ssthresh设置为cwnd的一半,把cwnd设置为ssthresh的值加3,然后重传丢失的报文段,加3的原因是因为收到3个重复的ACK,表明有3个“老”的数据包离开了网络。

  2. 再收到重复的ACK时,拥塞窗口增加1。

  3. 当收到新的数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。原因是因为该ACK确认了新的数据,说明从重复ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态。注意,如果在此过程出现超时,则重新进入慢启动阶段。

六、常见面试题

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。 现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

HTTP 1.0 1.1 2.0****区别

同步滚动:关

一、****HTTP****发展历史

· HTTP 1.0 -1996年

· HTTP 1.1 - 1999年

· HTTP 2.0 - 2015年

二、****HTTP****基本优化

影响HTTP网络请求的两个主要因素:带宽与延迟。

· 带宽:现在网络基础建设带宽已得到极大提升,不再是瓶颈。

· 延迟:

  1. 浏览器阻塞:浏览器阻塞请求,浏览器对于同一个域名同时只能有4个链接,超过浏览器最大连接数限制,后续请求就被阻塞;

2.DNS查询:浏览器需要知道目标服务器IP才能建立连接,DNS将域名解析为IP,这个过程可能耗时,利用DNS缓存结果减少这个耗时;

3.建立连接:HTTP基于TCP协议,三次握手后才能捎带HTTP请求报文达到真正的连接,这些连接都无法复用导致请求都经历三次握手与慢启动。三次握手在高延迟的场景下影响很明显,慢启动则对文件类大请求影响较大。

三、****HTTP1.0 与****HTTP1.1****区别

1.缓存处理:HTTP1.0中主要使用header里的If-Modified-Since,Expires作为缓存判断的依据,HTTP1.1引入了更多的缓存控制策略,如Entity tag, If-Unmodified-Since, If-Match, If-None-Match等。

  1. 带宽优化与网络连接使用:HTTP1.0中,存在浪费带宽的现象,例如客户端只需要某个对象的一部分,而服务器将整个对象送来,且不支持断点续传功能;HTTP1.1中则在请求头引入了range头域,其允许只请求资源的某个部分,返回码206。

3.错误通知的管理:HTTP1.1新增了24个错误状态响应码。(409,410等资源访问冲突,资源被永久性删除等)。

4.Host头处理:HTTP1.0中认为每台服务器绑定唯一的IP地址,因此,请求的URL不传递主机名。而虚拟技术的发展,在一台物理服务器上可以存在多个虚拟主机,并且共享一个IP地址。HTTP1.1的请求消息与响应消息都支持Host头域,如果没有Host头域报告一个400错误。

5.长连接:HTTP1.1支持长连接与请求的流水线处理,可以在一个TCP连接上传送多个HTTP请求与响应,减少建立与关闭连接的消耗和延时。

四、****HTTP2.0****扩充

1.支持多路复用:允许同时通过单一的HTTP2.0连接发起多重的请求-响应消息,减少因HTTP连接多而引起的网络拥塞,解决了慢启动针对突发性与短时性的http链接低效的问题。

2.将通信的基本单位缩小为帧

3.首部压缩:支持DEFLATE和HPACK算法的压缩

4.服务器推送: 客户端请求之前发送数据的机制,在HTTP2.0中,服务器可与对客户端的一个请求发送多个响应。

session与cooking

基本概念

cookie是服务端识别客户的唯一标识依据,客户在访问服务端时,服务端为了记住这个客户,在服务端按照它的规则制作一个cookie数据,将这个cookie数据保留在服务端一段时间,同时回传给客户端保留一份,这样就无需每次登录都来认证客户端身份了。

1.无状态的HTTP协议:协议是计算机网络通信中两台计算机之间进行通信必须遵守的规则或约定,HTTP是一种通信协议,它允许将HTML文档从WEB服务器传送到客户端的浏览器。HTTP是无状态的协议,一旦数据交换完毕,客户端与服务端的链接就会关闭,再次交换数据就需要建立新的链接,服务器无法从连接上跟踪会话。

2.会话跟踪:会话是指用户登录网站后的一系列动作,会话跟踪是WEB程序中常用的技术用来跟踪用户的整个会话。 session和cookie是常用的会话跟踪技术,cookie在客户端记录信息确认用户身份,session在服务端记录信息确认。

Cookie

· Cookie的由来?Cookie的工作原理是什么? 由于HTTP是无状态的连接,服务器无法从网络连接上识别客户身份,为了识别客户身份,服务端给每个客户办法一个访问的通行证,服务端从通信证上确认客户的身份,这个通行证就是cookie的工作原理。cookie实际上就是一小段文本,客户请求服务器,如果服务器需要记录用户状态,就用response向客户端浏览器颁发一个Cookie,客户端把cookie存起来,当下次客户端再次请求服务端时,会将请求连同cookie一起提交给服务器,服务器用过检查cookie用来辨识用户的状态。

cookie的主要内容包括:Name,Value,Domain,Path和Expires属性

· Name和Value有程序设定,默认都是空引用。

· Domain 属性默认值是当前URL的域名部分。

· Path默认是跟目录,即“/”,可以由程序设定为一定的路径进一步限制cookie的作用范围。

· Expires属性:设置cookie的过期日期和时间。不设置,cookie生命周期为浏览器会话期间,关闭浏览器,cookie消失。如果设置了过期时间会将cookie保存在硬盘,关闭浏览器再次打开只要没超过设定时间仍可使用。

· secure 是否使用安全传输协议,HTTPS与SSL等,传输数据之前先加密,默认是false。

Cookie具有不可跨域名性特点。

Session

Session是另一种记录用户状态的机制,与cookie不同的是,session是保存在服务端,用户与服务器建立连接的同时,服务器会自动分配一个SessionId.

1.session的重要性:Cookie将Sessionid带到服务器,用户提交表单时,浏览器将用户的SessionId自动附加到HTTP头部信息中,服务器处理表单结束后,将SessionId返回给对应的用户。服务器通过SessionId作为key,读写对应的value,以达到保持会话信息的目的。

2.session的创建:当程序需要为某个客户端的请求创建一个session时,服务器首先检查客户端的请求里是否已包含了sessionId,如果已经包含,说明此前已经创建过,服务器按照sessionId把相应的session检索出来(检索不到吗,new一个),如果不包含,则为此客户端创建一个session并生产一个与此session相关的sessionID,sessionId值不能重复又难以伪造,这个sessionId会被在本次相应中返回给客户端保存。

3.禁用cookie与session共享 禁用cookie:如果客户端禁用cookie,通常有两种方式实现session而不依赖cookie
1)URL重写,把sessionId直接附加在URL路径后面 2)表单隐藏字段。添加一个隐藏字段,在表单提交时候把session id传递给服务器。 session共享:对于多网站(同一父域不同子域)单服务器,我们需要解决的就是来自不同网站之间SessionId的共享。由于域名不同(aaa.test.com和bbb.test.com),而SessionId又分别储存在各自的cookie中,因此服务器会认为对于两个子站的访问,是来自不同的会话。解决的方法是通过修改cookies的域名为父域名达到cookie共享的目的,从而实现SessionId的共享。带来的弊端就是,子站间的cookie信息也同时被共享了。

总结

· cookie在客户端,session在服务端,cookie的产生是在服务端产生的;

· cookie只是一个通行证,但并不是安全的,任何安全的校验必须要在服务端上完成,cookie只是存在客户端上面的一个唯一标识它且由服务端定制的信息,本地可以改,但是不管怎么改,最后还是需要把它拿上发送给服务端进行匹配校验;

· session和cookie的存储都存在时效性,这是很有必要的;

· 单个cookie保存的数据不能超过4kb,很多浏览器都限制了一个站点最多保存20个cookie;

· 不管是cookie还是session,都是建立在安全性的大前提下,session中不仅仅有cookie的信息,同时会有该用户的相关重要且安全的信息存储,所以session是在服务器的,而cookie只是服务器将一些不重要的信息拿出来丢给客户的存在,以备以后快速匹配校验用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容

  • Java知识点1、==和equals的区别基本类型比较==比较内容 equals比较地址值引用类型比较==比较地址...
    压抑的内心阅读 581评论 0 0
  • [TOC] 1 JAVA: String为什么这么设计 在源码中string是用final 进行修饰,它是不可更改...
    寄浮生阅读 801评论 0 0
  • 1. iOS开发中的加密方式 iOS加密相关算法框架:CommonCrypto。 对称加密: DES、3DES、A...
    Dezi阅读 1,216评论 0 7
  • 一、Java基础1、String类为什么是final的,运行速度,或者说是执行速度,在这方面运行速度快慢为:Str...
    取名废同学阅读 839评论 0 0
  • update time 2021年7月14日11点57分 ,阅读时间30分钟, 文章版本:V 1.5。主要收集在面...
    _明川阅读 7,597评论 12 49