赞
踩
面向对象和面向过程是两中不同的问题的处理角度。
面向过程给更注重的是步骤和顺序,面向对象更注重的是参与者
面向过程比较高效,面向对象更易复用,维护,扩展
面向对象有三大特性:
封装:就是隐藏对象的内部实现细节对外暴漏一个接口,外部调用时无需知道内部是如何实现的
继承:基于基类的方法,并作出自己的改变或扩展
多态:多态的前提就是要有继承,方法重写,父类引用指向子类引用,基于对象所属的类不同,外部对同一个方法的调用,实现的是不同的逻辑,但无法调用子类独有的方法(不是对父类进行重写的)
多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。
先把Java代码编译为字节码,也就是将.java文件编译为.class文件
class文件放入Java虚拟机,这个虚拟机指的是Oracle官方自带的Hotspot JVM
Java虚拟机使用类加载器classLoad装载class文件
加载完成后会,会进行字节码校验,字节码校验通过后JVM解释器会把字节码翻译为机器码交由操作系统执行
静态变量前static
实例变量属于某个对象的属性,必须创建实例对象,其中实例变量才会被分配空间,才能够使用这个实例变量。静态变量不属于对象,属于类,通常也叫类变量,只要程序加载了类的字节码,不用进行实例化,静态变量就会被分配空间,就能直接使用。
==基本类型对比比的是值 == 引用类型对比的是地址
而equals方法是Object类中的方法,用于比较两个对象的内容是否相等,即判断两个对象的属性值是否相等。因此,两者的比较方式和比较的内容不同,使用时需要根据具体情况选择合适的方法。
重载是在一个类中方法名称相同 参数列表不同返回值不同权限修饰符不同
重写:是发生在子类和父类之间的,子类继承父类实现父类的方法 方法名相同参数列表相同返回值范围小于等于父类抛出异常的范围要小于等于父类,访问修饰符要大于等于父类;子类为private不能进行重写。
finaly修饰类类不能被继承
finaly修饰方法 方法不能被重写
finaly修饰变量 变量变为常量 不能被修改必须赋值
(1)使用try、catch、finaly捕获异常,finaly中的代码一定会执行,捕获异常后程序会继续执行
(2)使用throws声明该方法可能会抛出的异常类型,出现异常后,程序终止
不属于,基本数据类型包括:int、double、char、short、byte、boolean、long、float
String StringBuffer StringBuilder
String不是可变长字符串 StringBufferStringBUilder是可边长字符串
每次操作都会生成新的String对象,然后指针指向新生成的String对象,而StringBuffer、StringBuilder可以在原有的对象上进行操作,所以在经常改变字符串内容的情况不要使用String
StringBuffer不是线程安全的StringBUilder是线程安全的
StringBuffer效率比StringBuilder高
不一样String str="i"会分配的字符串常量池中,String str=new String("i")会分配到堆内存中
indexOf() :返回指定字符的索引
charAt() :根据索引返回指定字符
raplace() :字符串替换
trim() : 去除空格
split() : 根据指定字符分割字符串
getBytes() :返回字符串的byte类型数组
length() :返回字符串的长度
subString() :截取字符串
equals() :把字符串进行比较
toLowerCase() :将字符串转换为小写
toUpperCase() :将字符串转换为大写
不是 抽象类可以有非抽象的方法
抽象类不能实例化 普通类可以实例化
普通类不能有抽象方法 抽象类可以有抽象方法
不能使用finally要是用了子类就不能继承就没有意义,编译是也会报错。
相似点:
(1)接口和抽象类都不能被实例化
(2)实现接口或继承抽象类的普通子类都必须实现这些抽象方法
不同点:
(1)抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,
(2)抽象类可以有构造方法,而接口没有
(3)抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
输入流输出流 字节流字符流
就是动态的访问类的基本属性和方法
对于任意的一个类,都可以知道他的属性和方法,对于任意一个对象都能调用他的属性和方法,这种动态获取到信息以及动态的调用对象方法的功能被称为Java的反射机制
Collection是集合接口,提供了对集合进行基本操作的通用接口方法在Java类库中有很多具体实现
Collections是一个集合的工具类,提供了一系列的静态方法供集合使用
List 和Set是conllection的子接口,Map不是和collection平级
List 是有序的 Set是无须的 map 也是无序的
List是不可重复的 Set是可重复的 Map的key是不可以重复的 value是可以重复的
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低 hashMap不是线程安全的
(2)HashTable的Key不允许为null HashMap,key不能重复,可以为null
(3)HashTable只对key进行一次hash,HashMap进行了两次Hash
(4)HashTable底层使用的数组加链表 hashMapJdk1.8以后是基于数组+链表+红黑树来实现的
数据结构:数组+链表实现(扩容是扩容到原来的两倍)
Jdk8开始链表高度到8,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树
原理:当我们向HashMap中put元素时,首先根据key的hashcode重新计算出hash值,再根据hash值得到元素在数组中的位置(下标),如果该数组在其位置存放了其他元素,那么这个位置上的元素将以链表的形式存放,如果链表的高度达到8并且数组长度超过64就会将链表替换成红黑树,如果该位置没有存放其他元素,就直接将该元素放到数组的该位置上
通过进行两次哈希,HashMap 能够尽可能地减少哈希冲突,提高键值对的存取效率。它可以在第一次哈希的基础上,再次进行调整,以找到一个合适的位置存储数据,使得键值对分散在整个哈希表中,尽量避免冲突。
.想要线程安全的HashMap怎么办?
(1)使用ConcurrentHashMap
(2)使用HashTable
(3)Collections.synchronizedHashMap()方法
JDK1.7:使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁
JDK1.8:采用CAS+Synchronized保证线程安全,每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全
链表是物理存储单位上,非连续,非顺序的存储单位它是由一个个节点通过指针联系起来的,其中每个节点都包含数据和指针
HashSet是由HashMap实现的,Hash底层使用的HashMap来保存数据,所有实现比较简单
ArrayList底层是数组结构LinkedList底层是链表接口
Array的查询效率比LinkedList的查询效率高
但是LinkedList的增加和删除的效率比ArrayList高,ArrayList是数组,在对其增删操作时,会对数据的下标索引造成影响,需要进行数据移动
数组转换为List:使用数组的asList方法
List转换为数据:使用List的toArray()方法
底层是数组实现的,
他的默认长度为十,检查元素的个数是否达到上线,要是达到上限就会进行扩容,扩容的数组是原来数组的1.5倍
ArrayList不是线程安全的Vecot是线程安全的
ArrayList的性能比Vecot的性能好
Vecot的扩容是每次都扩容已一倍Arratlist的扩容是扩容给50%
Array是能存储进本数据类型和对象ArrayList只能存储对象
Array是指定大小后不可变的,ArrayList是可变的
ArrayhemiArrayList那么多的功能
Vecot HashTable Statck enumeration(比较器)
迭代器是一种设计模式,他是一个对象,它可以遍历并选择序列中的对象,通常称为轻量级对象,因为创建它的代价小
Iterator可以来遍历Set和List集合,IteratorList只能用来遍历List集合
Iterator只能进行前向遍历,IteratorList即可以前向也可以后向遍历
IteratorList继承Iterator,并包含其他的功能
进程:系统运行的基本单位,进程在运行过程中都是相互独立,但是线程之间运行可以相互影响。
线程:独立运行的最小单位,一个进程包含多个线程且它们共享同一进程内的系统资源
进程间通过管道、 共享内存、信号量机制、消息队列通信
当一个线程被剥夺cpu使用权时,切换到另外一个线程执行
并行是两个或多个事件在同一时间进行运行,并发是指两个或多个事件在同一时间间隔进行运行
并行是在不同实体类的多个事件,并发是在同一个实体类的多个事件
继承Thread类
实现Runnable接口
实现Callable接口
在线程池中创建线程
Runnable和Callable 的返回值不同
Runnable的run方法没有返回值
Callable发call方法有返回值,是个泛型
原子性:线程的一次或多次操作在执行期间不被其他线程影响
可见性:当一个线程在工作内存修改了变量,其他线程能立刻知道
有序性:JVM对指令的优化会让指令执行顺序改变,有序性是禁止指令重排
创建线程:创建当前线程还没有执行start方法
就绪状态:调用的线程的start方法,线程进入就绪状态
运行状态:将就绪状态的线程设置为当前线程此状态的线程就为运行状态,调用run方法
堵塞状态:线程正在运行被停止
死亡状态:当前线程执行完run方法并且调用stop方法线程变为死亡状态
Sleep是Thread中的方法 wait是Object的方法
调用Sleep方法不会释放类锁 调用wait方法会释放类锁
wait()要在同步方法或者同步代码块中执行,sleep()没有限制
wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒
Notify可能会产生死锁NotifyAll不会产生死锁
NotifyAll会将线程中的所有的睡眠状态的线程唤醒,Notify只会唤醒一个线程
Notify是Notify的一个优化
Start方法是启动线程的方法 run方法是运行线程任务逻辑的方法 run方法可以调用多次start只能调用一个
Runnable和Thrable主进程不会获取运行线程的结果
Collable主进程可以获取线程的运行结果,x但是不利于控制服务器中的线程资源,会将资源消耗殆尽
线程池创建线程可以获取线程的运行结果,性能稳定,并能捕获异常,但是在复杂的业务中一个异步调用可能会调用另一个异步调用运行的结果
较低能量损耗
线程池重复使用已经创建的线程降低线程的创建和销毁损耗的资源
提高线程的响应速度
线程池中的线程数没有超过线程的最大线程数,有些线程进入等待状态,当有任务来临无需创建新的线程就能运行
提高线程的管理性
线程池会根据当前线程的状态来优化线程,减少创建和销毁的带来的损耗无限的创建线程不仅会耗资源还会较低系统的效率,使用线程池进行统一管理
接受的参数不同
Submint没有返回值exrcute()有返回值
Submit方便异常的处理
体现在三个方面:
原子性:提供互斥访问,同一时刻只能有一个线程对数据操作(atomic,synchronized)
可见性:一个线程对主内存的修改可以被其他线程看到(volatile,synchronized)
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则)
死锁指多个线程在执行过程中,因争夺资源造成的一种相互等待的僵局
线程死锁是指在执行的过程中进行资源的抢夺形成的一种等待的僵局
死锁产生的四个必要条件:
互斥条件:同一资源同时只能由一个线程读取
不可抢占条件:不能强行剥夺其他线程占有的资源
请求和保持条件:请求其他资源的同时对自己手中的资源保持不放
循环等待条件:在相互等待资源的过程中,形成一个闭环
想要预防死锁,只需要破坏其中一个条件即可,比如使用定时锁、尽量让线程用相同的加锁顺序,还可以用银行家算法可以预防死锁
原理是为每个线程创建变量副本,不同线程之间不可见,保证线程安全。每个线程内部都维护了一个Map,key为threadLocal实例,value为要保存的副本。
但是使用ThreadLocal会存在内存泄露问题,因为key为弱引用,而value为强引用,每次gc时key都会回收,而value不会被回收。所以为了解决内存泄漏问题,可以在每次使用完后删除value或者使用static修饰ThreadLocal,可以随时获取value
Synchronized可以保证方法和代码块在运行时在同一时刻只有一个方法或者代码块执行
Synchronize是通过对象头的markwordk来表明监视器的,监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换要从用户态切换为核心态,成本很高,此时这种锁叫重量级锁,在JDK1.6以后引入了偏向锁、轻量级锁、重量级锁
偏向锁:当一段代码没有别的线程访问,此时线程去访问会直接获取偏向锁
轻量级锁:当锁是偏向锁时,有另外一个线程来访问,会升级为轻量级锁。线程会通过CAS方式获取锁,不会阻塞,提高性能,
重量级锁:轻量级锁自旋一段时间后线程还没有获取到锁,会升级为重量级锁,重量级锁时,来竞争锁的所有线程都会阻塞,性能降低
注意,锁只能升级不能降级
AQS是一个抽象类,可以用来构造锁和同步类,如ReentrantLock,Semaphore,CountDownLatch,CyclicBarrier。
AQS的原理是,AQS内部有三个核心组件,一个是state代表加锁状态初始值为0,一个是获取到锁的线程,还有一个阻塞队列。当有线程想获取锁时,会以CAS的形式将state变为1,CAS成功后便将加锁线程设为自己。当其他线程来竞争锁时会判断state是不是0,不是0再判断加锁线程是不是自己,不是的话就把自己放入阻塞队列。这个阻塞队列是用双向链表实现的
可重入锁的原理就是每次加锁时判断一下加锁线程是不是自己,是的话state+1,释放锁的时候就将state-1。当state减到0的时候就去唤醒阻塞队列的第一个线程。
因为有一些线程可能发生中断 ,而发生中断时候就需要在同步阻塞队列中删除掉,这个时候用双向链表方便删除掉中间的节点
AQS分为独占锁和共享锁
ReentrantLock(独占锁):可重入,可中断,可以是公平锁也可以是非公平锁,非公平锁就是会通过两次CAS去抢占锁,公平锁会按队列顺序排队
Semaphore(信号量):设定一个信号量,当调用acquire()时判断是否还有信号,有就获取一个信号量,没有就阻塞等待其他线程释放信号量,当调用release()时释放一个信号量,唤醒阻塞线程。
应用场景:允许多个线程访问某个临界资源时,如上下车,买卖票
CountDownLatch(倒计数器):给计数器设置一个初始值,当调用CountDown()时计数器减一,当调用await() 时判断计数器是否归0,不为0就阻塞,直到计数器为0。
应用场景:启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行
CyclicBarrier(循环栅栏):给计数器设置一个目标值,当调用await() 时会计数+1并判断计数器是否达到目标值,未达到就阻塞,直到计数器达到目标值
应用场景:多线程计算数据,最后合并计算结果的应用场景
数据库就是数据管理的产物。
数据保存在内存
优点:存取速度快
缺点:数据不能永久保存
数据保存在文件
优点:数据永久保存
缺点:读取速度慢,查询数据不方便
数据保存在数据库
数据永久保存
查询方便效率高
管理数据方便
结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言
作用:用于存储数据、查询、更新和管理关系数据库系统
InnoDB有三大特性,分别是事务、外键、行级锁,这些都是MyIsAm不支持的,
另外InnoDB是聚簇索引,MyIAm是非聚簇索引,
InnoDB不支持全文索引,MyIAm支持
InnoDB支持自增和MVCC模式的读写,MyIAm不支持
MyIsAM的访问速度一般比InnoDB快,差异在于innodb的mvcc、行锁会比较消耗性能,还可能有回表的过程(先去辅助索引中查询数据,找到数据对应的key之后,再通过key回表到聚簇索引树查找数据)
第一范式:每个列都不可再拆分
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非组件
Atomicity(原子性):一个事务的中的所有操作,要不全部完成,要不全部不完成,不会出现再中间某个环节结束
Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏
Isolation(隔离性):防止多个事务再并发执行时由于交叉执行而导致的数据不一致
Durability(持久性):事务结束后,对数据的修改就是永久的,即便系统故障页不会丢失
A原子性(atomicity) 由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
C一致性(consistency) 一般由代码层面来保证
I隔离性(isolation) 由MVCC来保证
D持久性(durability) 由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复
MVCC是多版本并发控制,为每次事务生成一个新版本数据,每个事务都由自己的版本,从而不加锁就决绝读写冲突,这种读叫做快照读。只在读已提交和可重复读中生效。
实现原理由四个东西保证,他们是
undolog日志:记录了数据历史版本
readView:事务进行快照读时动态生成产生的视图,记录了当前系统中活跃的事务id,控制哪个历史版本对当前事务可见
隐藏字段DB_TRC_ID: 最近修改记录的事务ID
隐藏字段DB_Roll_PTR: 回滚指针,配合undolog指向数据的上一个版本
- DDL(Data Definition Language):数字定义语句,主要进行定义/改变表的结构、数据类型、表之间的链接等操作。常用语句关键字有create、drop、alter等
- DML(Data Manipulation Language):数据操纵语句,主要对数据进行添加、修改、删除操作,关键字包括inster、delete、update等
- DQL(Data Query Language):数据查询语言,主要对数据库进行查询操作,常用的关键字包括select、from、where等
- DCL(Data Control Language):数据控制语句,主要用来设置/更改数据库用户权限,常用的关键字有grant、remove等
在高并发情况下,并发事务会产生脏读、不可重复读、幻读问题,这时需要用隔离级别来控制
先了解下几个概念:脏读、不可重复读、幻读。
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
不可重复读是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,另一个事务修改了数据并提交了。
幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
读未提交: 允许一个事务读取另一个事务已提交的数据,脏读。
读已提交:一个事务只能看见已经提交事务所做的改变。可避免脏读的发生
可重复读: MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,解决了不可重复读的问题。
可串行化:通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。
(1)抽取实体,如用户信息,商品信息,评论
(2)分析其中属性,如用户信息:姓名、性别...
(3)分析表与表之间的关联关系
然后可以参考三大范式进行设计,设计主键时,主键要尽量小并且定义为自增和不可修改
视图是一种虚拟的表,具有和物理表相同的功能,可以对视图进行增、改、查操作,视图通常是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表,它使得我们获取数据更容易,相比多表查询
内连接是把匹配的关联数据显示出来,左连接是把左边表的数据显示出来,右边的表显示出符合条件的数据,右连接则相反
索引是一种特殊的文件,它们包含着对数据表里所有引用的指针
索引是一种数据结构,数据库索引是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中的数据。
通俗来说:索引就相当于目录,为了方便查找书中的内容,通过对内容建立索引形成目录,索引是个文件是要占据物理空间的。
优点:
大大加快数据的检索时间
使用索引可以在查询的过程中使用优化隐藏器,提高系统性能
缺点:
时间方面:创建索引和维护索引要耗费时间,具体的,当对表中的数据进行增删改时,索引也要动态维护,会降低增删改的执行效率
空间方面:索引需要占物理空间
索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引,默认的索引类型为B+树索引。
哈希索引不支持排序,因为哈希表是无序的。
哈希索引不支持范围查找。
哈希索引不支持模糊查询及多列索引的最左前缀匹配。
因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。
B+数的数据储存是在叶子节点上的,叶子节点均为 索引方便扫库,B树的分支节点也同样存储数据我们要是查找数据还要进行再一次的遍历,就比较麻烦,因为在数据库中基于范围的查找都是比较频繁的所以使用B+树索引,
B+树的节点只存储索引key值,具体信息的地址存在于叶子节点的地址中。这样就可以存放更多的节点。
B+树的查询效率更加稳定,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
主键索引:一张表只能有一个主键索引,主键索引列不能有空值和重复值
唯一索引:唯一索引不能有相同值,但允许为空
普通索引:允许出现重复值
组合索引:对多个字段建立一个联合索引,减少索引开销,遵循最左匹配原则
全文索引:通过建立倒排索引提升检索效率,广泛用于搜索引擎
0
(1)where条件中有or,除非所有查询条件都有索引,否则失效
(2)like查询用%开头,索引失效
(3)索引列参与计算,索引失效
(4)违背最左匹配原则,索引失效
(5)索引字段发生类型转换,索引失效
(6)mysql觉得全表扫描更快时(数据少),索引失效
where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数,having可以
说两个维度:
共享锁(简称S锁)和排他锁(简称X锁)
读锁是共享的,可以通过lock in share mode实现,这时候只能读不能写。
写锁是排他的,它会阻塞其他的写锁和读锁。从颗粒度来区分,可以分为表锁和行锁两种。
表锁和行锁
表锁会锁定整张表并且阻塞其他用户对该表的所有读写操作,比如alter修改表结构的时候会锁表。
行锁又可以分为乐观锁和悲观锁
悲观锁可以通过for update实现
乐观锁则通过版本号实现
数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。
悲观锁:。假设会发生并发冲突,会对操作的数据进行加锁,直到事务提交,才会释放锁,其他的事务才能对数据进行修改
实现方式:使用数据库中的锁机制
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否数据是否被修改过。给表增加version字段,在修改提交之前检查version与原来取到的version值是否相等,若相等,表示数据没有被修改,可以更新,否则,数据为脏数据,不能更新。
实现方式:乐观锁一般使用版本号机制或CAS算法实现。
主从同步使得数据可以从一个数据库服务器复制到其他服务器上,在复制数据时,一个服务器充当主服务器(master),其余的服务器充当从服务器(slave)。
因为复制是异步进行的,所以从服务器不需要一直连接着主服务器,从服务器甚至可以通过拨号断断续续地连接主服务器。通过配置文件,可以指定复制所有的数据库,某个数据库,甚至是某个数据库上的某个表。
读写分离,使数据库能支撑更大的并发。
在主服务器上生成实时数据,而在从服务器上分析这些数据,从而提高主服务器的性能。
数据备份,保证数据的安全。
MySQL主要分为 Server 层和存储引擎层:
Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
存储引擎: 主要负责数据的存储和读取。server 层通过api与存储引擎进行通信。
Server 层基本组件
连接器: 当客户端连接 MySQL 时,server层会对其进行身份认证和权限校验。
查询缓存: 执行查询语句的时候,会先查询缓存,先校验这个 sql 是否执行过,如果有缓存这个 sql,就会直接返回给客户端,如果没有命中,就会执行后续的操作。
分析器: 没有命中缓存的话,SQL 语句就会经过分析器,主要分为两步,词法分析和语法分析,先看 SQL 语句要做什么,再检查 SQL 语句语法是否正确。
优化器: 优化器对查询进行优化,包括重写查询、决定表的读写顺序以及选择合适的索引等,生成执行计划。
执行器: 首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会根据执行计划去调用引擎的接口,返回结果。
建立合适的索引
开启慢查询日志,记录执行速度慢的sql,对慢sql语句进行优化
优化sql语句执行效率的方案:
为搜索字段(where中的查询条件)、排序字段、select查询列,创建合适的索引
尽量建立组合索引并注意组合索引的创建顺序,按照顺序组织查询条件、尽量将筛选力度大的查询条件放到左边
尽量使用覆盖索引
select语句中尽量不要使用*
order by和group by语句尽量使用到索引
where条件中尽量不要使用1=1、not in语句(推荐使用not exists)
不用mysql的内置函数,内置函数不会建立查询缓存
尽量不用count(*) 、尽量使用使用count(主键)
MyBatis是一个半ORM(对象关系映射)框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。他是为简化数据库操作而设计的。MyBatis通过配置文件或者注解即可将数据库数据与pojo实体类联系起来
Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
由于MyBatis专注于SQL本身,灵活度高,所以比较适合对性能的要求很高,或者需求变化较多的项目,如互联网项目。
Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具
(1)优点:
消除jdbc中的重复代码
可以在XML或注解中直接 编写SQL语句,比较灵活,方便对SQL的优化和调整
SQL在XML中与代码解耦,按照对应关系方便管理
XML中提供了动态SQL的标签,方便根据条件拼接SQL
提供了XML、注解与Java对象的映射机制
与spring集成比较方便。
(2)缺点:
字段较多、关联表多时,编写SQL工作量较大
SQL语句依赖了数据库特性,会导致程序的移植性差,切换数据库困难
${}是字符串替换,#{}是预处理;使用#{}可以有效的防止SQL注入,提高系统安全性。
Mybatis在处理${}时,就是把${}直接替换成变量的值。而Mybatis在处理#0{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
使用查询时sql语句定义别名,让字段名的别名和实体类的属性名对应
通过<ResultMap>来映射字段名和实体类属性名一一对应
Mapper 接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象 MappedProxy,代理对象会拦截接口方法,根据类的全限定名+方法名,唯一定位到一个MapperStatement并调用执行器执行所代表的sql,然后将sql执行结果返回。
Mapper接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
备注:在旧版本的Mybatis中,namespace是可选的,不过新版本的namespace已经是必填项了。
Mybatis使用RowBounds(肉棒死)对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect(带莱克特)方言,添加对应的物理分页语句和物理分页参数。
答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor(赛)这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是Invoca(ken) tionHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
编写插件:实现Mybatis的Interceptor(赛)接口并复写intercept()方法,然后再给插件编写注解,指定要拦截哪一个接口的哪些方法即可,最后在配置文件中配置你编写的插件。
Mybatis仅支持association(额扫死en )关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
延迟加载通过在需要访问关联对象时才去加载它们,以提高性能和减少不必要的数据库查询。在访问关联对象之前,会检查是否已经加载,如果未加载,则会通过代理对象发送查询语句进行加载。这样可以避免不必要的关联对象查询,提升系统的效率和性能
当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。
一级缓存:一级缓存是sqlSession级别的,在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession'之间的缓存数据区域(HashMap)是互相不影响的,默认是开启的。
二级缓存:是Mapper级别的,也就是说可以多个sqlSession去操作同一个Mapper的sql语句,多个sqlSessin可以共用二级缓存,默认是关闭的需要在Mapper配置文件中开启配置。
需要注意的是,尽管缓存可以提高查询性能,但在某些情况下,缓存也可能导致数据一致性的问题。因此,在使用缓存时,需要根据具体的业务场景和需求来合理配置和使用缓存。
映射形式:
第一种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。《ResultMap》
第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
MyBatis动态SQL是一种可以根据不同条件生成不同SQL语句的机制,它能够根据业务需求和运行时条件来动态地拼接和生成SQL语句。动态SQL提供了灵活性和可重用性,可以减少代码冗余并简化SQL语句的编写。
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
《ReuestMap》 《Sql》 《selectKey》
<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签 trim | where | set | foreach | if | choose | when | otherwise | bind 等,其中 <sql> 为sql片段标签,通过<include>标签引入sql片段,<selectKey>为不支持自增的主键生成策略标签。
Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
Mapper.xml文件中的namespace即是mapper接口的类路径
1. 读取mybatis配置文件:mybatis-config.xml 为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
2. 加载映射文件。映射文件即sql文件,该文件中配置了操作数据库的SQL语句
3. 构造会话工厂:通过mybatis的环境等配置信息构建会话工厂SqlSessionFactory
4. 创建会话对象:由会话工厂创建SqlSession 对象,该对象中包含了执行sql语句的所有方法
5. Executor执行器:mybatis底层定义了一个Executor接口来操作数据库,他将根据SqlSession 传递的参数动态的生成执行的sql语句,同时负责查询缓存的维护。
6. MappedStatement 对象:在Executor接口的执行方法中有一个MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的sql语句的id、参数信息等。
7. 输入参数映射
8. 输出结果映射
Spring是一个框架也是一个容器也是也种生态
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,提高开发人员的开发效率以及系统的可维护性。
IOC(Inversion Of Controll,控制反转)是一种设计思想,指将对象的控制权转移给Spring框架,由 Spring 来负责控制对象的生命周期(比如创建、销毁)和对象间的依赖关系。
以前需要我们自己new对象,对象始终会和其他接口或类耦合起来,使用Spring IOC之后就就不需要我们自己去new对象,spring会给我们创建好对象,这就叫控制反转
DI—Dependency Injection(依赖注入):
依赖注入实现了控制反转的思想。
Spring通过依赖注入的方式来完成Bean管理的。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
Spring 的 IoC 的实现原理就是工厂模式加反射机制
AOP(Aspect-Oriented Programming,面向切面编程)
将与核心业务无关的代码独立的抽取出来,形成一个独立的切面,然后以横向交叉的方式应用到业务流程当中的过程被称为AOP。
减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
Spring的AOP使用的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接口,会默认使用JDK动态代理,如果是代理对象并且没有实现接口的类,就会切换使用CGLIB。当然,你也可以强制通过一些配置让Spring只使用CGLIB。。
利用AOP对业务逻辑的各个部分进行隔离,降低业务逻辑的耦合性,提高程序的可重用性和开发效率
常见的通知:
前置通知:方法执行前调用 对应注解@Before
后置通知:方法执行后会回调 对应注解@After
返回通知:方法返回后回调 对应注解@AfterReturning
异常通知:方法出现异常时调用 对应注解@AfterThrowing
环绕通知:动态代理、手动推荐方法运行 对应注解@Around
先创建一个切面,将切面交给Spring管理
在切面中添加切点和通知(通知并通过切入点表达式来表示要对哪些方法进行日志打印)
这样既可以实现日志的打印
(1)spring属于低侵入式设计,代码的污染极低;
(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
(3)Spring提供了AOP技术,可以方便地实现对程序进行权限拦截和运行监控等功能
(4)spring对于主流的应用框架提供了集成支持。
BeanFactory用了工厂模式,
AOP用了动态代理模式,
RestTemplate用来模板方法模式,
SpringMVC中handlerAdaper用来适配器模式,
Spring里的监听器用了观察者模式
循环依赖就是在创建 A 实例的时候里面包含着 B 实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。
spring通过三级缓存来解决循环依赖:
一级缓存:缓存经过完整的生命周期的Bean
二级缓存 :缓存未经过完整的生命周期的Bean
三级缓存:缓存的是ObjectFactory,其中存储了一个生成代理类的拉姆达表达式
我们在创建 A 的过程中,先将 A 放入三级缓存 ,这时要创建B,B要创建A就直接去三级缓存中查找,并且判断需不需要进行 AOP 处理,如果需要就执行拉姆达表达式得到代理对象,不需要就取出原始对象。然后将取出的对象放入二级缓存中,因为这个时候 A 还未经 过完整的生命周期所以不能放入一级缓存。这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B创建完成,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了
BeanFactory是spring的原始接口,针对原始结构的实现类功能比较单一,BeanFactory接口实现的容器,特点是在每次获取对象时才会创建对象。
ApplicationContext继承了BeanFactory接口,拥有BeanFactory的所有功能,并且扩展了很多高级特性,每次容器启动时就会创建所有对象。
结论:早期电脑的性能低,内存小,所以spring的容量不足,不能将所有的对象全部创建好放入容器,,所以使用的是BeanFactory,需要某个对象时,再进行创建,随着电脑硬件的发展,内存越来越大,所以spring框架就引入了ApplicationContext,将所有的对象都创建好放入容器,需要使用哪个对象时,去容器中取。
BeanFactory
Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。
BeanFactory是工厂。
FactoryBean
FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
在Spring中,Bean可以分为两类:
● 第一类:普通Bean
● 第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)
不是,spring框架中的单例bean不是线程安全的
spring中的bean默认是单例模式,spring框架并没有对单例bean进行多线程封装
只有spring正常关闭bean销毁方法才能被调用
注:初始化方法和销毁方法配置完场后要在xml中对其方法进行调用才行,在bean标签中添加属性,调用初始化方法使用init-method,调用销毁方法使用destroy-method,并且调用销毁方法之前要进行手动销毁才能调用(close()方法)
单例:singleton(森钩藤):spring ioc 容器中只存在一个 bean 实例,bean 以单例模式存在,是系统默认值;
原型:prototype(噗肉投胎不):每次请求都会创建一个新的bean实例
请求request:每次 http 请求都会创建一个 bean
会话session:对于同一个 http session 共享一个 bean 实例
全局会话global session(阁楼bou):每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
隐式的bean发现机制和自动装配
在Java代码或者XML进行显示的配置
spring事务有编程式和声明式,我们一般@Transactional进行声明式事务,在某个方法上增加 注解,这个方法中的sql会统一成功或失败。
原理是:
当一个方法加上@Transactional注解,spring会根据这个类生成一个代理对象并将这个代理对象作为bean,当使用这个bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、用来保证数据库操作的一致性
(1)事务方法所在的类没有加载到容器中
(2)事务方法不是public类型
(3)同一类中,一个没有添加事务的方法调用另外以一个添加事务的方法,事务不生效
(4)spring事务默认只回滚运行时异常,可以用rollbackfor属性设置
(5)业务自己捕获了异常,事务会认为程序正常秩序
default:默认级别,使用数据库自定义的隔离级别
其它四种隔离级别与mysql一样
多个事务方法相互调用的情况下
● REQUIRED(瑞快yer的):支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
● SUPPORTS(四炮而肆):支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
● MANDATORY(满的特锐):必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
● REQUIRES_NEW(瑞快yer死new):开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
● NOT_SUPPORTED(Not四炮忒特):以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
● NEVER(乃wer):不使用事务,如果当前事务存在,则抛出异常
● NESTED(奈斯忒特):如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
@Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,通过@Autowired 的使用来消除 set/get 方法
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它的required=false)
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
Spring MVC是基于Spring框架的一种轻量级、灵活的Web框架,用于开发Web应用程序。将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
(1)可以支持各种视图技术,而不仅仅局限于JSP;
(2)方便与Spring框架集成(如IoC容器、AOP等);
(3)清晰的角色分配:前端控制器(dispatcherServlet) ,请求到处理器映射(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)。
(4) 支持各种请求资源的映射策略
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可:
<!-- 配置SpringMvc的拦截器 -->
<mvc:interceptors>
<!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 -->
<bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean>
<!-- 只针对部分请求拦截 -->
<mvc:interceptor>
<mvc:mapping path="/modelMap.do" />
<bean class="com.zwp.action.MyHandlerInterceptorAdapter" />
</mvc:interceptor>
</mvc:interceptors>
因为Spring, SpringMVC 需要使用的大量的配置文件 (xml文件)还需要配置各种对象,把使用的对象放入到spring容器中才能使用对象需要了解其他框架配置规则,SpringBoot减少了配置文件,内置了tomcat,能够独立运行
1. 容易上手,提升开发效率
2. 简化配置
自动配置
快速启动
5. 避免大量的maven导入和各种版本冲突
Spring Boot的自动配置原理是基于条件化配置和约定大于配置的原则。它会根据应用程序的环境、依赖项和属性配置来自动决定是否启用某个功能,并自动配置相应的组件。通过扫描和加载自动配置类,并根据条件判断是否应用该配置。同时,使用属性文件来配置应用程序的行为。使用启动器依赖项可以简化特定功能的引入和配置。这样可以减少开发人员的配置工作,提高开发效率和开发体验。
Spring Boot的启动器(Starters)是一组预配置的依赖项,用于简化应用程序的构建和配置。这些启动器提供了一组相关的依赖项,以支持特定的功能或技术栈,例如Web应用程序、数据访问、安全性等。通过使用启动器,可以轻松地引入和配置所需的依赖项,以快速构建具有所需功能的应用程序。
可以实现接口ApplicationRunner 或者CommandLineRunner这两个接口实现方式一样,都只提供了一个run方法
@RestController :修饰类,该控制器会返回Json数据
@RequestMapping("/path") :修饰类,该控制器的请求路径
@Autowired : 修饰属性,按照类型进行依赖注入
@PathVariable : 修饰参数,将路径值映射到参数上
@ResponseBody :修饰方法,该方法会返回Json数据
@RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中
@Controller@Service@Compont: 将类注册到ioc容器
@Transaction:开启事务
Spring boot actuator(啊可图Ater)是spring启动框架中的重要功能之一。用于监控和管理应用程序的运行时状态。它提供了一组端点(endpoints),可以通过HTTP请求访问这些端点来获取应用程序的各种信息,例如健康状况、运行状态、配置信息、日志等。监视器可以帮助开发人员和运维人员更好地了解应用程序的运行情况,以及进行故障排查和性能调优等操作。同时,监视器还支持自定义的端点,可以根据需求添加额外的端点来暴露更多的应用程序信息。查状态。
使用@ControllerAdvice注解实现一个ControllerAdvice类,可以统一处理控制器类抛出的所有异常。在该类中,使用@ExceptionHandler注解处理具体的异常类型,并返回相应的错误信息。这样可以简化异常处理的代码,使得处理异常变得更加方便和统一。
@Component表明一个类会作为组件类,并告知spring要为这个类创建Bean,@Component通常是通过类路径扫描自动侦测以及自动装配到spring容器中
@Bean注解告诉spring这个方法会返回一个对象,这个对象要注册为spring应用上下文的bean,通常方法体包含了最终产生bean的实例的逻辑,并且方法名就是实例名
**总结:**@Component和@Bean都是用来注册Bean并装配到spring容器中,但Bean比Component的自定义性更强,可以实现一些Component实现不了的自定义加载类
- bootstrap(.yml 或 .properties):由AppicationContext加载,比application优先加载,配置在应用程序上下文的引导阶段生效。一般来说在spring cloud config和Nacos中会用到它,且bootstrap中的属性不能被覆盖。
- application(.yml 或 .properties):由ApplicationContext加载,用于springboot的自动化配置
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
说出主要的组件:
Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架"Spring Boot化"的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。
集中配置管理工具,分布式系统中统一的外部配置管理,默认使用Git来存储配置,可以支持客户端配置的刷新及加密、解密操作。
Netflix OSS 开源组件集成,包括Eureka、Hystrix、Ribbon、Feign、Zuul等核心组件。
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。
Spring Cloud Security提供了一组原语,用于构建安全的应用程序和服务,而且操作简便。可以在外部(或集中)进行大量配置的声明性模型有助于实现大型协作的远程组件系统,通常具有中央身份管理服务。它也非常易于在Cloud Foundry等服务平台中使用。在Spring Boot和Spring Security OAuth2的基础上,可以快速创建实现常见模式的系统,如单点登录,令牌中继和令牌交换。
在微服务中,通常根据业务模块分服务,项目中前端发起一个请求,后端可能跨几个服务调用才能完成这个请求(如下图)。如果系统越来越庞大,服务之间的调用与被调用关系就会变得很复杂,假如一个请求中需要跨几个服务调用,其中一个服务由于网络延迟等原因挂掉了,那么这时候我们需要分析具体哪一个服务出问题了就会显得很困难。Spring Cloud Sleuth服务链路跟踪功能就可以帮助我们快速的发现错误根源以及监控分析每条请求链路上的性能等等。
轻量级事件驱动微服务框架,可以使用简单的声明式模型来发送及接收消息,主要实现为Apache Kafka及RabbitMQ。
Spring Cloud Task的目标是为Spring Boot应用程序提供创建短运行期微服务的功能。在Spring Cloud Task中,我们可以灵活地动态运行任何任务,按需分配资源并在任务完成后检索结果。Tasks是Spring Cloud Data Flow中的一个基础项目,允许用户将几乎任何Spring Boot应用程序作为一个短期任务执行。
Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。要使用Feign,我们可以将调用的服务方法定义成抽象方法保存在本地添加一点点注解就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。
当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka 服务注册和发现可以在这种情况下提供帮助。由于所有服务都在 Eureka 服务器上注册并通过调用 Eureka 服务器完成查找,因此无需处理服务地点的任何更改和处理。
Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过Eureka Service来监控各个微服务是否运行正常。
集群吧,注册多台Eureka,然后把SpringCloud服务互相注册,客户端从Eureka获取信息时,按照Eureka的顺序来访问。
默认情况下,如果Eureka Service在一定时间内没有接收到某个微服务的心跳,Eureka Service会进入自我保护模式,在该模式下Eureka Service会保护服务注册表中的信息,不再删除注册表中的数据,当网络故障恢复后,Eureka Servic 节点会自动退出自我保护模式DiscoveryClient的作用?
可以从注册中心中根据服务别名获取注册的服务器信息。
网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。
统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等
Zuul是对SpringCloud提供的成熟对的路由方案,他会根据请求的路径不同,网关会定位到指定的微服务,并代理请求到不同的微服务接口,他对外隐蔽了微服务的真正接口地址。
网关是对所有服务的请求进行分析过滤,过滤器是对单个服务而言。
Nginx、Zuul、Gateway
Zuul是SpringCloud集成的网关,使用Java语言编写,可以对SpringCloud架构提供更灵活的服务。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法
Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单的说,就是在配置文件中列出后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。(有点类似Nginx)
Nginx是反向代理同时可以实现负载均衡,nginx拦截客户端请求采用负载均衡策略根据upstream配置进行转发,相当于请求通过nginx服务器进行转发。Ribbon是客户端负载均衡,从注册中心读取目标服务器信息,然后客户端采用轮询策略对服务直接访问,全程在客户端操作。
Ribbon使用discoveryClient从注册中心读取目标服务信息,对同一接口请求进行计数,使用%取余算法获取目标服务集群索引,返回获取到的目标服务信息。
开启客户端负载均衡。
当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)
断路器有三种状态
在分布式系统,我们一定会依赖各种服务,那么这些个服务一定会出现失败的情况,就会导致雪崩,Hystrix就是这样的一个工具,防雪崩利器,它具有服务降级,服务熔断,服务隔离,监控等一些防止雪崩的技术。
Hystrix有四种防雪崩方式:
Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易
他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。
· Feign
· RestTemplate
调用方式同:Ribbon需要我们自己构建Http请求,模拟Http请求然后通过RestTemplate发给其他服务,步骤相当繁琐
而Feign则是在Ribbon的基础上进行了一次改进,采用接口的形式,将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。
使用了一个RouteLocatorBuilder的bean去创建路由,除了创建路由RouteLocatorBuilder可以让你添加各种predicates和filters,predicates断言的意
1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
2. 数据结构简单,对数据操作也简单
3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在
首先redis是基于c语言编写的,而且是内存中的数据库,读写速度很快。在项目中也会经常的使用redis,用来做缓存、分布式锁、也可以用来设计消息队列、同时还支持事务、持久化、多种集群方案。
大多数小伙伴都知道,Redis有以下这五种基本类型:
· String(字符串)
· Hash(哈希)
· List(列表)
· Set(集合)
· zset(有序集合)
- 避免了线程切换的资源损耗
- 单线程避免了资源共享与竞争,不用考虑锁的问题
- 基于内存的,内存的读写速度非常快
- 使用非阻塞的IO多路复用机制
- 数据存储进行了压缩优化
- 使用了高性能的数据结构,如hash、跳表等
redis本身就是基于内存的,如果我的数据全部在内存中,单线程操作就是效率最高的。因为多线程的本质就是cpu模拟出来多个线程的情况,这种模拟出来的情况就有个代价,就是上下文切换,对于一个内存系统来说,没有上下文切换的效率是最高的。一次上下文切换大概在1500ns(纳秒)左右,假设在内存中读取1MB的连续数据耗时大约250us(微秒),假设由多个线程读取了1000次,那么就有1000次的上下文切换,就有1500ns*1000=1500us,单线程执行250us,多线程上下文切换就用了1500us,
使之具有高速读写和数据持久化的特征
如果程序直接与磁盘交互,磁盘IO速度会严重影响redis的性能
内存硬件成本低
什么是热Key呢?在Redis中,我们把访问频率高的key,称为热点key。
如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。
而热点Key是怎么产生的呢?主要原因有两个:
· 用户消费的数据远大于生产的数据,如秒杀、热点新闻等读多写少的场景。
· 请求分片集中,超过单Redi服务器的性能,比如固定名称key,Hash落入同一台服务器,瞬间访问量极大,超过机器瓶颈,产生热点Key问题。
如何解决热key问题?
· Redis集群扩容:增加分片副本,均衡读流量;
· 将热key分散到不同的服务器中;
· 使用二级缓存,即JVM本地缓存,减少Redis的读请求。
**缓存穿透:**
请求数据库中根本不存在的数据,数据库中不存在,缓存中就更加不存在,导致每次请求直接怼到数据库,数据库压力过大;
**解决:**
- 缓存空值,并设置过期时间小一点,不然会对数据的准确性造成影响
- 对用户请求进行合法校验
- 布隆过滤器
**缓存击穿:**
请求了很多缓存中不存在但数据库存在的数据,一般是缓存过期导致的,也导致请求直接怼到数据库;
**解决:**
- 对于比较热点的数据,我们可以在缓存中设置永不过期,也可以在访问数据的时候,在缓存中更新这些数据的过期时间,如果是批量入库的缓存项,我们可以为这些缓存项分配合理的过期时间,避免同意时刻失效
- 使用分布式锁,保证每个key同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,其他线程没有获得分布式锁的权限,需要进行等待。不过在高并发的情况下,这种方案对分布式锁的访问压力比较大
**缓存雪崩:**
缓存大面积的失效
**解决:**
- 使用集群来确保服务器实例在任何时间都处于服务状态,(搭建redis集群)
- 让失效的时间点不分布在同一时间点
Redis是基于内存的非关系型K-V数据库,既然它是基于内存的,如果Redis服务器挂了,数据就会丢失。为了避免数据丢失了,Redis提供了持久化,即把数据保存到磁盘。
Redis提供了RDB和AOF两种持久化机制
RDB,就是把内存数据以快照的形式保存到磁盘上。
RDB 的优点:
适合大规模的数据恢复场景,如备份,全量复制等
RDB缺点
没办法做到实时持久化/秒级持久化。
新老版本存在RDB格式兼容问题
AOF(append only file) 持久化,采用日志的形式来记录每个写操作,追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题。默认是不开启的。
AOF的优点
· 数据的一致性和完整性更高
AOF的缺点
· AOF记录的内容越多,文件越大,数据恢复变慢。
(1)主从模式:个master节点,多个slave节点,master节点宕机slave自动变成主节点
(2)哨兵模式:在主从集群基础上添加哨兵节点或哨兵集群,用于监控master节点健康状态,通过投票机制选择slave成为主节点
(3)分片集群:主从模式和哨兵模式解决了并发读的问题,但没有解决并发写的问题,因此有了分片集群。分片集群有多个master节点并且不同master保存不同的数据,master之间通过ping相互监测健康状态。客户端请求任意一个节点都会转发到正确节点,因为每个master都被映射到0-16384个插槽上,集群的key是根据key的hash值与插槽绑定
主从同步第一次是全量同步:slave第一次请求master节点会根据replid判断是否是第一次同步,是的话master会生成RDB发送给slave。
后续为增量同步:在发送RDB期间,会产生一个缓存区间记录发送RDB期间产生的新的命令,slave节点在加载完后,会持续读取缓存区间中的数据
锁未被释放
B锁被A锁释放了
数据库事务超时
锁过期了,业务还没执行完
redis的主从复制问题
分布式锁可能存在锁过期释放,业务没执行完的问题。有些小伙伴认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释
Redis通过MULTI、EXEC、WATCH等一组命令集合,来实现事务机制。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
简言之,Redis事务就是顺序性、一次性、排他性的执行一个队列中的一系列命令。
Redis执行事务的流程如下:
开始事务(MULTI)
命令入队
执行事务(EXEC)、撤销事务(DISCARD
原理是使用setnx+setex命令来实现,但是会有一系列问题:
(1)任务时常超过缓存时间,锁自动释放。可以使用Redision看门狗解决
(2)加锁和释放锁的不是同一线程。可以在Value中存入uuid,删除时进行验证。但是要注意验证锁和删除锁也不是一个原子性操作,可以用lua脚本使之成为原子性操作
(3)不可重入。可以使用Redision解决(实现机制类似AQS,计数)
(4)redis集群下主节点宕机导致锁丢失。使用红锁解决
使用SETNX命令:利用Redis的SETNX命令(SET if Not eXists),可以将一个键设置为锁,只有在键不存在时才能成功设置。多个线程或进程尝试使用SETNX命令来争夺锁,只有一个线程能成功设置键,即获取到了锁。
设置锁的过期时间:在使用SETNX命令成功设置锁后,可以通过设置键的过期时间来防止锁的长时间占用。可以使用Redis的EXPIRE命令为锁设置一个合适的过期时间,确保在一定时间后锁会自动释放。
释放锁:当线程完成了对共享资源的操作后,需要手动释放锁。可以使用Redis的DEL命令将锁的键删除,以释放锁供其他线程使用。
分布式锁不能解决超时的问题,分布式锁有一个是超时时间,程序的执行如果超出了锁的超时时间就会出问题
缩减键值对象:满足业务要求下key越短越好;value值进行适当压缩
共享对象池:
尽可能使用散列表
编码优化控制编码类型
控制key的数量
有哪些应用场景?
Redis 在互联网产品中使用的场景实在是太多太多,这里分别对 Redis 几种数据类型做了整理:
1)String:缓存、限流、分布式锁、计数器、分布式 Session 等。
2)Hash:用户信息、用户主页访问量、组合查询等。
3)List:简单队列、关注列表时间轴。
4)Set:赞、踩、标签等。
5)ZSet:排行榜、好友关系链表。
Nginx是一个 轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用nginx网站用户有很多,例如:新浪、网易、 腾讯等。
跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,
而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
使用Nginx的话还能:
节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
稳定性高:宕机的概率非常小
接收用户请求是异步的
因为他的事件处理机制:异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决
nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址
server { # 第一个Server区块开始,表示一个独立的虚拟主机站点
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
正向代理就是一个人发送一个请求直接就到达了目标的服务器
反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规 则分发给了后端的业务处理服务器进行处理了
反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。
优点:
占内存小,可实现高并发连接,处理响应快
可实现http服务器、虚拟主机、方向代理、负载均衡
Nginx配置简单
可以不暴露正式的服务器IP地址
缺点:
动态处理差:nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,
http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。
worker_processes 1; # worker进程的数量
events { # 事件区块开始
worker_connections 1024; # 每个worker进程支持的最大连接数
} # 事件区块结束
http { # HTTP区块开始
include mime.types; # Nginx支持的媒体类型库文件
default_type application/octet-stream; # 默认的媒体类型
sendfile on; # 开启高效传输模式
keepalive_timeout 65; # 连接超时
server { # 第一个Server区块开始,表示一个独立的虚拟主机站点
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果
error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户
location = /50x.html { # location区块开始,访问50x.html
root html; # 指定对应的站点目录为html
}
}
......
静态资源访问,就是存放在nginx的html页面,我们可以自己编写
如何用Nginx解决前端跨域问题?
使用Nginx转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。
1、基于域名的虚拟主机,通过域名来区分虚拟主机——应用:外部网站
2、基于端口的虚拟主机,通过端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台
3、基于ip的虚拟主机。
需要建立/data/www /data/bbs目录,windows本地hosts添加虚拟机ip地址对应的域名解析;对应域名网站目录下新增index.html文件;
#当客户端访问www.lijie.com,监听端口号为80,直接跳转到data/www目录下文件
server {
listen 80;
server_name www.lijie.com;
location / {
root data/www;
index index.html index.htm;
}
}
#当客户端访问www.lijie.com,监听端口号为80,直接跳转到data/bbs目录下文件
server {
listen 80;
server_name bbs.lijie.com;
location / {
root data/bbs;
index index.html index.htm;
}
}
使用端口来区分,浏览器使用域名或ip地址:端口号 访问
#当客户端访问www.lijie.com,监听端口号为8080,直接跳转到data/www目录下文件
server {
listen 8080;
server_name 8080.lijie.com;
location / {
root data/www;
index index.html index.htm;
}
}
#当客户端访问www.lijie.com,监听端口号为80直接跳转到真实ip服务器地址 127.0.0.1:8080
server {
listen 80;
server_name www.lijie.com;
location / {
proxy_pass http://127.0.0.1:8080;
index index.html index.htm;
}
}
location指令的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作。
location的语法能说出来吗?
注意:~ 代表自己输入的英文字母
匹配符 匹配规则 优先级
= 精确匹配 1
^~ 以某个字符串开头 2
~ 区分大小写的正则匹配 3
~* 不区分大小写的正则匹配 4
!~ 区分大小写不匹配的正则 5
!~* 不区分大小写不匹配的正则 6
/ 通用匹配,任何请求都会匹配到 7
Location正则案例
示例:
#优先级1,精确匹配,根路径
location =/ {
return 400;
}
#优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写
location ^~ /av {
root /data/av/;
}
#优先级3,区分大小写的正则匹配,匹配/media*****路径
location ~ /media {
alias /data/static/;
}
#优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里
location ~* .*\.(jpg|gif|png|js|css)$ {
root /data/av/;
}
#优先7,通用匹配
location / {
return 403;
}
Nginx限流就是限制用户请求速度,防止服务器受不了
限流有3种
正常限制访问频率(正常流量)
突发限制访问频率(突发流量)
限制并发连接数
Nginx的限流都是基于漏桶流算法,底下会说道什么是桶铜流
实现三种限流算法
1、正常限制访问频率(正常流量):
限制一个用户发送的请求,我Nginx多久接收一个请求。
Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,限制的原理实质是基于漏桶算法原理来实现的。在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制单个IP的请求处理频率。
#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;
#绑定限流维度
server{
location/seckill.html{
limit_req zone=zone;
proxy_pass http://lj_seckill;
}
}
1r/s代表1秒一个请求,1r/m一分钟接收一个请求, 如果Nginx这时还有别人的请求没有处理完,Nginx就会拒绝处理该用户请求。
2、突发限制访问频率(突发流量):
限制一个用户发送的请求,我Nginx多久接收一个。
上面的配置一定程度可以限制访问频率,但是也存在着一个问题:如果突发流量超出请求被拒绝处理,无法处理活动时候的突发流量,这时候应该如何进一步处理呢?Nginx提供burst参数结合nodelay参数可以解决流量突发的问题,可以设置能处理的超过设置的请求数外能额外处理的请求数。我们可以将之前的例子添加burst参数以及nodelay参数:
#定义限流维度,一个用户一分钟一个请求进来,多余的全部漏掉
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/m;
#绑定限流维度
server{
location/seckill.html{
limit_req zone=zone burst=5 nodelay;
proxy_pass http://lj_seckill;
}
}
1
为什么就多了一个 burst=5 nodelay; 呢,多了这个可以代表Nginx对于一个用户的请求会立即处理前五个,多余的就慢慢来落,没有其他用户的请求我就处理你的,有其他的请求的话我Nginx就漏掉不接受你的请求
3、 限制并发连接数
Nginx中的ngx_http_limit_conn_module模块提供了限制并发连接数的功能,可以使用limit_conn_zone指令以及limit_conn执行进行配置。接下来我们可以通过一个简单的例子来看下:
http {
limit_conn_zone $binary_remote_addr zone=myip:10m;
limit_conn_zone $server_name zone=myServerName:10m;
}
server {
location / {
limit_conn myip 10;
limit_conn myServerName 100;
rewrite / http://www.lijie.net permanent;
}
}
上面配置了单个IP同时并发连接数最多只能10个连接,并且设置了整个虚拟服务器同时最大并发数最多只能100个链接。当然,只有当请求的header被服务器处理后,虚拟服务器的连接数才会计数。刚才有提到过Nginx是基于漏桶算法原理实现的,实际上限流一般都是基于漏桶算法和令牌桶算法实现的。接下来我们来看看两个算法的介绍:
漏桶流算法和令牌桶算法知道?
漏桶算法是网络世界中流量整形或速率限制时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。也就是我们刚才所讲的情况。漏桶算法提供的机制实际上就是刚才的案例:突发流量会进入到一个漏桶,漏桶会按照我们定义的速率依次处理请求,如果水流过大也就是突发流量过大就会直接溢出,则多余的请求会被拒绝。所以漏桶算法能控制数据的传输速率。
令牌桶算法
令牌桶算法是网络流量整形和速率限制中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。Google开源项目Guava中的RateLimiter使用的就是令牌桶控制算法。令牌桶算法的机制如下:存在一个大小固定的令牌桶,会以恒定的速率源源不断产生令牌。如果令牌消耗速率小于生产令牌的速度,令牌就会一直产生直至装满整个令牌桶。
Nginx是当下最热的Web容器,网站优化的重要点在于静态化网站,网站静态化的关键点则是是动静分离,动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们则根据静态资源的特点将其做缓存操作。
让静态的资源只走静态资源服务器,动态的走动态的服务器
Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术。
对于静态资源比如图片,js,css等文件,我们则在反向代理服务器nginx中进行缓存。这样浏览器在请求一个静态资源时,代理服务器nginx就可以直接处理,无需将请求转发给后端服务器tomcat。
若用户请求的动态文件,比如servlet,jsp则转发给Tomcat服务器处理,从而实现动静分离。这也是反向代理服务器的一个重要的作用。
只需要指定路径对应的目录。location/可以使用正则表达式匹配。并指定对应的硬盘中的目录。如下:(操作都是在Linux上)
location /image/ {
root /usr/local/static/;
autoindex on;
}
创建目录
mkdir /usr/local/static/image
1
进入目录
cd /usr/local/static/image
1
放一张照片上去#
1.jpg
1
重启 nginx
sudo nginx -s reload
1
打开浏览器 输入 server_name/image/1.jpg 就可以访问该静态图片了
为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。
Nginx负载均衡实现的策略有以下五种:
1 轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。
- upstream backserver {
-
- server 192.168.0.12;
-
- server 192.168.0.13;
-
- }
2 权重 weight
weight的值越大分配
到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。
-
- upstream backserver {
-
- server 192.168.0.12 weight=2;
-
- server 192.168.0.13 weight=8;
-
- }
权重越高,在被访问的概率越大,如上例,分别是20%,80%。
3 ip_hash( IP绑定)
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题
- upstream backserver {
-
- ip_hash;
-
- server 192.168.0.12:88;
-
- server 192.168.0.13:80;
-
- }
4 fair(第三方插件)
必须安装upstream_fair模块。
对比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。
- upstream backserver {
-
- server server1;
-
- server server2;
-
- fair;
-
- }
哪个服务器的响应速度快,就将请求分配到那个服务器上。
5、url_hash(第三方插件)
必须安装Nginx的hash软件包
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。
- upstream backserver {
-
- server squid1:3128;
-
- server squid2:3128;
-
- hash $request_uri;
-
- hash_method crc32;
-
- }
当上游服务器(真实访问服务器),一旦出现故障或者是没有及时相应的话,应该直接轮训到下一台服务器,保证服务器的高可用
Nginx配置代码:
-
- server {
-
- listen 80;
-
- server_name www.lijie.com;
-
- location / {
-
- ### 指定上游服务器负载均衡服务器
-
- proxy_pass http://backServer;
-
- ###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
-
- proxy_connect_timeout 1s;
-
- ###nginx发送给上游服务器(真实访问的服务器)超时时间
-
- proxy_send_timeout 1s;
-
- ### nginx接受上游服务器(真实访问的服务器)超时时间
-
- proxy_read_timeout 1s;
-
- index index.html index.htm;
-
- }
-
- }
- # 如果访问的ip地址为192.168.9.115,则返回403
-
- if ($remote_addr = 192.168.9.115) {
-
- return 403;
-
- }
不允许谷歌浏览器访问 如果是谷歌浏览器返回500
- if ($http_user_agent ~ Chrome) {
-
- return 500;
-
- }
变量 含义
$args 这个变量等于请求行中的参数,同$query_string
$content length 请求头中的Content-length字段。
$content_type 请求头中的Content-Type字段。
$document_root 当前请求在root指令中指定的值。
$host 请求主机头字段,否则为服务器名称。
$http_user_agent 客户端agent信息
$http_cookie 客户端cookie信息
$limit_rate 这个变量可以限制连接速率。
$request_method 客户端请求的动作,通常为GET或POST。
$remote_addr 客户端的IP地址。
$remote_port客户端的端口。
$remote_user已经经过Auth Basic Module验证的用户名。
$request_filename当前请求的文件路径,由root或alias指令与URI请求生成。
$schemeHTTP方法(如http,https)。
$server_protocol 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$server_addr 服务器地址,在完成一次系统调用后可以确定这个值。
$server_name服务器名称。
$server_port 请求到达服务器的端口号。
$request_uri 包含请求参数的原始URI,不包含主机名,如”/foo/bar.php?arg=baz”。
$uri不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
$document_uri 与$uri相同。
ifconfig:查看网络接口详情
ping:查看与某主机是否能联通
ps -ef|grep 进程名称:查看进程号
lost -i 端口 :查看端口占用情况
top:查看系统负载情况,包括系统时间、系统所有进程状态、cpu情况
free:查看内存占用情况
kill:正常杀死进程,发出的信号可能会被阻塞
kill -9:强制杀死进程,发送的是exit命令,不会被阻塞
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。