程序运行需要将我们的java代码编译成对应的字节码文件,但是各种操作系统(Windows、Linux)的指令并不完全一致,要实现一份字节码文件能在多种系统上都能正常运行,就需要在各平台安装对应系统的JVM(Java Virtual Machine),这是由sun公司提供的,只要按照统一的API接口开发,部署到不同的操作平台时,JVM会屏蔽掉各平台之间的差异,实现同一份代码跨平台运行
整数类型: 默认类型 int | 字节数 | 位数 | 取值范围 |
---|---|---|---|
byte | 1字节 | 8bit | [-128,127] -2^7 ~ 2^7-1 |
short | 2字节 | 16bit | -2^15 ~ 2^15-1 |
int | 4字节 | 32bit | -2^31 ~ 2^31-1 |
long | 8字节 | 64bit | -2^63 2^63-1 |
小数类型:默认类型 double | – | – | – |
float | 4字节 | 32bit | - |
double | 8字节 | 64bit | - |
布尔类型:true/false | – | – | – |
boolean | - | 1bit | - |
字符类型 | – | – | – |
char | 2字节 | 16bit | [0,65535] |
每个基本数据类型都对对应一个包装类,例如:int—Integer;包装类型是一个引用类型,可以存放null,扩展性更强
二者之间的转换通过装箱和拆箱完成;
Integer i = 1,会通过Integer.valueOf(1)方法自动装箱,将基本数据类型转换成对应的包装类型
int j = i,就会通过intValue(i)方法自动拆箱,将包装类型转换成对应的基本类型
这三个类都是用来声明和操作字符串
String底层是一个长度不可变final修饰的char字符数组,一旦声明长度就固定,常用来表示长度固定的内容;
StringBuffer和StringBuilder底层都是长度可变的字符数组,创建时默认长度都为16,用来声明需要经常增删改的字符串,通过append()方法进行字符串拼接,效率很高。而这二者的区别是StringBuffer是线程安全,效率相对于线程不安全的StringBuilder相对低一些。
StringBuffer和StringBuilder扩容机制:当要添加的字符串大于 > 当前字符数组的长度的时候扩容,扩容是: 原来长度*2+2 的方式扩容
HashMap和HashTable都是Map接口下的的,Map接口是一个独立体系,与Collection毫无关系
HashMap:基于HashCode,底层是Entry键值对数组,默认长度是16,可以存放null值和null键,线程不安全,效率高;
HashMap在通过put方法保存元素时,会用添加的key对应的HashCode去模数组的长度,来获取下标,然后进行保存。当键值对元素获取到的下标和原有的元素相等时,就会发生Hash冲突(Hash碰撞),这时候就会在对key进行equals比较,当结果为true时,就会用新添加的键值对覆盖掉原有的,而结果为false时,就会在该下标出原有的键值对后面形成一个链表,当链表长度达到8个时,就会形成红黑树,当键值对的数量删除到小于等于6个时,又会将红黑树退化为链表;
HashMap扩容机制:创建是默认长度为16,扩容因子0.75,存放临界值为长度*扩容因子,当存放达到临界值(大于等于)的时候底层通过resize()方法进行扩容,长度扩容为原来的2倍;
HashTable:是一个线程安全的HashMap,效率低,不能存放null值和null键;例如Properties配置文件(.xml、.yml、.properties等)就是HashTable的一个子类,只是键值都是String类型;在JDK8及以后,如果既要保证线程安全又要提高效率,使用ConcurrentHashMap来代替HashTable,理论上效率是HashTable的16倍;
线程的使用:
方式1:写一个线程类继承Thread类,重新run()方法,通过线程对象.start()启动线程;
方式2:写一个业务类实现Runnable接口,重写run()方法,测试先创建业务类对象,将其作为线程对象的构造方法参数创建线程对象,再.start()启动线程;
注意:run()方法只是执行方法,而线程的启动必须是通过start();
线程的状态
1、new 新建;2、Runnable 就绪,可执行状态;3、Running 运行状态;4、Blocked 阻塞状态;5、死亡Dead;
wait和sleep的区别:
wait:Object方法,使线程等待,直到被notify或者notifyall唤醒,会放弃锁资源;
sleep:Thread的方法,强制使线程睡眠,直到时间结束继续运行,不会放弃锁资源;
线程同步:
1. 同步代码块(悲观锁)
语法:
Synchronized(同步对象){//必须是几个线程对象锁共享的 => static
有线程安全问题的代码
}
同步对象可以是:static修饰的对象、字节码、this(实现Runnable接口)
2. 同步方法(悲观锁)
用Synchronized关键字修饰方法即可,在修饰符位置,返回值前面
如果方法是static修饰的:同步的是: 当前类.class
如果方法是非static修饰的:同步的是: this
3. 锁机制(乐观锁)
通过Lock接口的实现类ReentrantLock对象进行上锁lock()和释放锁unlock()
乐观锁(锁机制)效率高,功能强大,悲观锁在读锁和写锁的时候都会进行上锁,就容易堵塞。而乐观锁只有在写锁的时候才会上锁,效率大大提高;
作用:
1、管理多个线程,限定线程个数,防止线程过多导致系统缓慢或崩溃;
2、线程复用,节约资源,提高响应速度,不用每运行一个程序都去创建和销毁线程;
线程池核心参数:
最大线程数,最小线程数,闲置时间,线程工厂,阻塞策略,拒绝策略
1、分类
字节流:包括InputStream输入流和OutputStream输出流,用来处理除文本文字相关的所有流;如果用字节流处理文本相关的东西会遇到乱码问题,因为不同的字符集转换成字节流对应的字节数是不一致的,GBK:中文2字节,英文和数字1字节;UTF-8:中文3~6字节,英文和数字1字节;如果要解决则需要用到转换流,设置字符集,将字节流进行包装;
字符流:包括Reader输入流和Writer输出流,只用于处理文本文字相关的东西;
2、字节流特点
字节流在读取的时候可以将字节数组存储到缓冲区,所以拷贝文件使用字节流;
设计模式就是将前人写代码的优秀经验总结下来应用到开发中,增强代码可读性,使代码更加优美高效;
分类:
***Spring底层使用的就是简单工厂模式+Map缓存的单例+反射实现的
一个类只能创建一个对象
1、饿汉模式
设计步骤:1)私有化构造方法
2)类中创建一个对象,并且用private、static、final进行修饰(private为了避免在外部直接访问当前对象;static是为了在静态方法中可以返回当前类中的对象;final:可加可不加,加了可以保证不可修改,且提高获取效率)
3)提供一个public获取实例的方法,以供外部使用对象
public class Singleton {
// 1. 私有化构造方
private Singleton(){
}
/**
* 2. 在类中创建一个对象,并且用private、static、final修饰
* private为了避免在外部直接访问当前对象
* static是为了在静态方法中可以返回当前类中的对象
* final:可加可不加,加了可以保证不可修改,且提供获取效率
*/
private static final Singleton instance = new Singleton();
/**
* 3. 提供一个public修饰的静态方法getInstance给外部返回一个对象
* @return
*/
public static Singleton getInstance() {
//返回创建好的对象的
return instance;
}
}
2、懒汉模式
需要使用时再创建对象
2.1 简单加锁
public class Singleton {
private Singleton(){
}
private static Singleton INSTANCE= null;
public static synchronized Singleton getInstance() {
if (INSTANCE==null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
2.2 双重判断加锁
public class Singleton {
private Singleton(){
}
private static Singleton INSTANCE= null;
public static Singleton getInstance() {
if (INSTANCE==null){
synchronized (Singleton.class){
if (INSTANCE==null)
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
3、枚举
public enum Singleton{
INSTANCE;
}
客户端不直接调用实际对象,而是通过一个代理对象来间接调用;可以在代理对象调用之前和之后加入自己的逻辑;
分为静态代理和动态代理,常用动态代理,因为它可以根据代理的对象动态创建代理类;
Spring中AOP就是通过动态代理实现的:如果代理的类有接口,就使用JDK自带的动态代理模式,如果没有接口就使用CGLIB的动态代理模式;
1、@Target 用来限制被修饰注解的使用范围,即注解可以在类的哪些成员上使用;
2、@Retention 限制注解的作用时间;注解的生命周期和三个阶段有关:源文件有效SOURCE、源文件和字节码文件中有效CLASS、运行时(包括源文件和字节码)有效RUNTIME;
3、@Documented 文档注解,可以使用javadoc生成文档注释
4、@Inherited 从该类的子类上可以继承父类的注解
TCP是传输层协议,用来传输数据,HTTP是应用层协议,用来包装数据
M:Model模型层,包括Service业务层和Dao层,Service业务层专门进行业务判断,满足条件后调用Dao层来操作数据库
V:View视图层,页面展示
C:Controller控制层,获取前端数据,调用对应的Service处理数据,完成业务
MVC流程图如下:
—| 根目录:一般命名为webapps或webcontent,下面存放公开可直接访问的资源文件和一个名为WEB-INF的文件夹;
—| WEB-INF下有:
—| 1、lib文件夹,放项目要用到的jar包;
—| 2、classes文件夹,存放项目编译后的字节码文件;
—| 3、不能被直接访问的其他资源,例如html和jsp等;
Get请求会将参数保存到地址栏,多个参数用&连接,大小上限为2KB,不安全;
Post请求会生成数据包在请求体中,没有大小限制且安全;表单提交必须用Post
在第一次调用构造方法和init方法时自动创建对象(单例模式),然后一直存在,创建后再次调用service方法都不需要再次创建对象,且正常关闭服务器的时候,该对象依然能调用从HttpServlet继承的destroy方法,只是调用方法,并未销毁该对象
1、匹配请求,获取请求中的参数;
2、调用service业务代码处理请求;
3、响应请求;
重定向是两次或多次请求,地址栏会发生变化,不能共享一次请求中的数据,可以定向到外部应用;
转发是一次请求,所以地址栏不会发生变化,可以共享一次请求中的数据,不能定向到外部应用;
JSP相当于一个Servlet,Servlet是用来编写后台代码,JSP主要用来展示数据,JSP运行的时候,Tomcat会自动将其转换为一个Servlet,将其中的数据以流的形式进行输出
输入输出对象: response; out;
四大作用域对象:pageContext(仅当前页面有效); request(也是输入对象,一次请求有效);session(一次会话有效,可包含多次请求); application(全局对象);
servlet对象:config; page;
异常对象:exception
注意:四大作用域对象使用同一API,都不能跨域取值,都有setAtrribute(“name”, “value”); getAtrribute(“name”); removeAtrribute(“name”, “value”)三个方法;
动态包含标签:
静态包含:<%@ include file=“error.jsp” %>,先包含后编译,只有一个.java文件,包含静态页面html的时候使用;
session和cookie都是会话跟踪技术,cookie是通过客户端记录的信息确认用户身份,而session是通过服务端记录的信息来确认用户身份,但是session的实现是依赖于cookie和sessionId,区别如下:
Mybatis(iBatis)是一个ORM的数据库持久化框架,Mybatis的底层就是一个别人写好的JDBC代码,但是相对于JDBC,它有以下几大优势:
ORM即Object Relational Mapping 对象关系映射,可以解决面向对象和面向关系之间的不匹配问题,使我们在查询数据库的的时候,数据库中的数据会自动转换成java对象,可以直接用面向对象的思维进行数据库操作;
ORM映射方式分为完整映射和半映射:
完整映射:不用写sql语句,例如JPA、Hibernate
半映射/sql映射:需要自己写sql语句,例如Mybatis,但是功能更齐全,灵活性高
#在sql映射执行的时候会自动转换成一个占位符,通过PreparedStatement执行,避免了sql注入问题;
$在映射执行的时候会进行拼接,是Statement方式执行,会有sql注入的问题,基本只在order by + limit的时候使用;
关系型数据库:Mysql、Oracle、SqlServer等
非关系型数据库:Redis、Memcached、MongoDB等
1、在数据库连接池中有一定数量的连接对象,每次需要访问数据库就从中取一个,使用完毕后重新返回给连接池,不需要每次访问数据库都去创建和销毁连接对象,提高响应速度;
2、限制了同一时间连接数据库的数量,防止高并发导致系统缓慢或崩溃;
Redis是一种数据结构化存储方法的集合,数据以Key-Value形式保存在内存中,不遵循sql标准;理论上一个Redis中可以存放2^32个key,约2亿五千万个keys;
优势:
1、数据存储在内存中,存取速度快,并发能力强;
2、value支持多种数据类型,如:String、List、Set、Map等;(Memcached的value只支持String类型)
3、支持多种客户端语言;
4、还支持持久化和存储过期;(Memcached不支持持久化)
5、支持集群,解决高并发;
常用:
1、volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
2、volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
3、allkeys-lru:从所有数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
不建议:
4、volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
5、allkeys-random:从所有数据集(server.db[i].dict)中任意选择数据淘汰;
6、no-enviction(驱逐):禁止驱逐数据;
Redis有两种级别的持久化方式:RDB和AOF,通过redis.conf进行配置
RDB:可以在指定的时间间隔内修改次数进行持久化,默认开启;例如# save 900 1;就是在900秒内至少有一次改变进行持久化;
AOF:记录服务器执行的所有操作命令,保存在AOF日志文件中,服务器启动时重新执行这些命令还原数据集,默认关闭;通过appendonly yes开启;
Redis为了考虑效率,保存数据在内存中,并且考虑数据安全性,还做数据持久化,如果满足保存策略,就会把内存的数据保存到数据rdb文件,还来不及保存那部分数据存放到aof更新日志中,在加载时,把两个数据做一个并集;
集群原因:解决单点故障;实现高并发;处理海量数据;
方案思路:
1、twemproxy,类似于集群,设置好其管理的多个Redis实例,通过twemproxy代理身份来接收请求,并且通过一致性Hash算法连接到具体的Redis;这种方式代理单端口压力大,且一致性Hash算法如果Redis节点数量改变,导致计算值改变,数据无法自动迁移到新的节点;
2、codis,目前使用最广泛,和twemproxy类似,但是支持节点改变后旧数据自动迁移到新节点;
3、Redis cluster3.0自带集群,使用的是Hash槽而不是一致性Hash算法,自身支持节点设置从节点;
4、业务层代码实现,多个无关联的Redis实例,通过在代码中对key进行Hash计算然后确定连接具体的redis实例进行操作;
集群支持的最大节点数:16384个;集群后数据库都默认在下标为0的数据库中,且目前无法选择使用哪个数据库;
Redis非常轻量,单个实例只使用1M内存,为了便于以后扩容,最好是在一开始就使用分布式;虽然前期分布式操作起来相对麻烦一点,但是长远看来,一旦数据量增加到一定程度需要更多Redis服务器时优势就体现出来了,这是只需要将一半Redis实例迁移到另一台服务器就可以,而不需要考虑重新分区的问题;
包括:Redisson、Jedis、Lettuce,官方推荐Redisson;
一级缓存(本地缓存):默认开启,只针对同一个SqlSession有效,即使用同一个SqlSession对象调用同一个mapper方法,SQL语句执行一次后,Mybatis会将其放在缓存中,再次查询如果未申明需要刷新且缓存未过期,会直接获取缓存的数据而不会去数据库查询;
生命周期:
A、Mybatis开启数据库会话 => 创建SqlSession对象(此对象包含一个新的Executor对象,而Executor对象又持有新的PrepetualCache对象) => 会话结束 => 释放SqlSession及其内部的Executor和PrepetualCache对象;
B、如果SqlSession调用close()方法,会释放一级缓存的PrepetualCache对象,缓存不可用;
C、如果SqlSession调用clearCache()方法,会清空PrepetualCache对象的数据,但该对象仍然可用;
D、SqlSession对象执行任何增删改的方法,都会清空PrepetualCache对象的数据,但该对象仍然可用;
如何判断两次是完全相同的查询
四个条件必须全相同:传入的statementId;结果集的范围;查询要传给JDBC的SQL语句;参数;
二级缓存(全局缓存):默认关闭,Application级别;开启要求实现二级缓存的POJO(简单Java对象)必须实现Serializable序列化接口,然后在xml映射文件中配置开启;
优化原因:
**针对数据库优化,优先选择垂直分库分表,还不能满足就进行集群、缓存等优化技术,水平分表分库是最后的选择;
设计时首先满足1NF,然后满足2NF,最后满足3NF;
1NF:原子性,表中的列不可分割;
2NF:唯一性,表中信息不可重复,一般通过主键约束;
3NF:表中字段能通过其他表推导出来就不设计冗余字段,一般通过外键关联实现;
反3NF:在一些场合为提高查询效率,可以违反第3NF,在表中添加冗余字段;例如订单、订单明细;
触发器:create trigger创建触发器,满足条件就能执行;
存储引擎:mysql中常见的有MyIsam、Innodb、memory;
MyIsam与Innodb的区别:
MyIsam:不支持事务,支持全文索引,查询添加速度快,不支持外键约束,锁机制是表锁;对事务要求不高使用;
Innodb:支持事务,不支持全文索引,查询添加速度慢,支持外键约束,锁机制是行锁;对事务要求高使用;
把全文查找变为索引查找,mysql存储引擎的支持的索引类型是B+Tree,通过索引算法减少查询次数而提高效率,但实际是以空间换时间;
Mysql索引分类:
创建索引会有B+Tree结构维护的文件,占用磁盘空间;对DML操作有影响,因为要维护索引;
设置索引的字段条件:1) 查询频繁的字段(where或order by后面的);2) 修改少;3) 唯一性高;
DDL批量插入:1) 变多次索引维护为一次索引维护;2) 变多次唯一校验为一次;3) 变多次事务提交为一次或固定多少数量提交;
DML批量插入:变多次事务提交为一次或固定多少数量提交;
DQL:order by通过索引排序;or两边字段都要能使用索引才能使用,都做索引判断;
SQL优化方式有很多,简单列举一个常用的:1) 避免全表扫描,使用索引;2) 在where中尽量避免用or连接条件;3) 能用between就不要用in;4) 尽量同varchar代替char;5) 不要用select *等······
Consistency一致性:写操作后可以读取最新数据,主节点写入后会锁定节点,知道从节点同步完成,同步失败会返回错误信息,且一定不会返回旧数据,性能较低;
Avalability可用性:任何操作都可以响应结果,同步时不会锁定节点,未同步完毕返回旧数据;
Partition tolerance分区容忍性:添加多个节点,异步操作取代同步,避免网络故障及单点故障;
分布式系统CAP智能满足其中两种进行组合:
CA组合:单个Mysql服务器;
CP组合:满足一致性和分区容忍性,例如Redis、Zookeeper、Consol等;
AP组合:并发可能读写不同步(常用),如Mysql主从同步、Eureka;
Basecalliy Available基本可用、Soft state软状态、Eventually consistent最终一致性;一般为保证分布式系统的可用性,我们会使用AP组合,牺牲了强一致性,所以扩展BASE理论:确保分布式系统出现故障允许损失部分功能而保证核心功能可用;允许系统存在中间状态(如支付中···);实现最终一致性,在经过一段时间后所有节点数据达到一致;
在分布式系统中,要实现高可用及避免单点故障,我们通常对数据库进行集群,分为master和slave,主从同步+读写分离;
主从同步使用Mysql就能实现,读写分离需要使用其他组件,如Sharding-jdbc、MysqlProxy、Mycat,主负责写操作,从负责读操作;
要实现水平分表分库,需要使用到中间组件,一般选择Sharding-sphere中的Sharding-jdbc(只支持Java),以jar包形式依赖即可使用;id可使用雪花算法,引入了时间戳,永远不会重复且保持了升序;
框架是指为解决开放性问题而设计的具有一定约束性的支撑结构;
Spring是一个轻量级开源的IOC和AOP容器框架;它可以用来帮我们管理对象(创建、保存、初始化和销毁),管理对象的前提是必须有无参构造;
IOC:Inversion Of Controll,控制反转,将对象的创建和依赖关系的维护的权力交给Spring,所有对象都是由Spring来帮助我们管理,不需要我们自己来控制,就是控制反转;其中最常用的就是DI (Dependency Injection)依赖注入;
AOP:Aspect Oreinted Programming,面向切面编程,将通用的逻辑抽取出来集中管理,提高代码复用,比如登录拦截,日志管理和事务管理等都可以通过AOP来做;
DI (Dependency Injection)依赖注入:IOC是一种思想,在系统运行中动态的向某个对象提供它所需要的其他的对象,而DI就是来实现这种动态赋值思想的;可以通过xml或者注解两种注入方式实现;
SpringMVC是一个优秀的web层或表现层框架,底层对Servlet进行了封装,可以用Mybatis取代原生的JDBC,且同样具有实现Servlet三大职责的能力:匹配请求并接收请求参数;调用业务层处理请求;响应处理结果;
Spring随着功能的增加变得越来越复杂,如果要启动一个新的Spring项目,我们就必须添加构建路径或者Maven依赖关系,配置应用程序服务器,添加Spring配置,从头做一系列的样板代码和配置;而Spring Boot就是基于Spring框架,使用Spring启动,解决了这些问题;
具有以下优点:
SpringBoot有两大核心配置文件:bootstrap和application;这两个都是SpringBoot的配置文件,bootstrap的加载顺序是在application之前;
application配置文件通常用于项目自动化配置;
而bootstrap一般在使用Spring Cloud Config配置中心统一管理application配置文件的时候,在bootstrap配置文件中添加链接到配置中心的属性来加载Spring Cloud Config中存放的配置文件;一些固定不能被覆盖的属性;加解密场景;
配置文件有两种格式:
spring:
application:
name: xxx
SpringBoot核心注解是@SpringBootApplication,加载项目启动的主配置类上,它包含了以下三个注解及功能:
概念:应用在做了集群之后,需要有一个组件来统一将客户端的请求相对平均的分发到这些应用节点上,这种功能就是负载均衡,比如Nginx就是一个负载均衡器;
负载均衡算法:
1、轮询(round robin):将请求一个个一次分发给各个后台服务器,即所谓的雨露均沾,默认方式;
2、权重(weight):给每台服务器设置一个权重值,根据权重来分发请求,权重越大分配的比例越高;
3、IP_HASH:根据hash算法计算ip地址,同一个ip的请求会分配到同一台服务器;
4、url_hash :计算请求url的hash值来分配服务器;
5、Fair:根据每台服务器响应时间分发请求,响应速度越快分发的请求越多;
概念:
微服务架构类似于SOA架构,是一个分布式系统,一个项目会有多个服务,将这些服务拆分开,各自独立,一个服务只做自己的特有业务且有独立的数据库,各服务直接使用HTTP通信协同工作来实现整个系统的所有功能;
优点:
1、单个业务逻辑简单,便于维护;
2、各服务之间相互独立,无耦合,升级维护互不影响;
3、使用轻量级HTTP通信,不同服务可以使用不同的编程语言;
4、扩展能力强,业务量大的业务可以集群或拆分服务,提高系统负载能力;
5、开发效率高,敏捷开发;
缺点:
1、分布式事务,服务之间进行通信的机制增加了事务的复杂性;
2、服务器多,部署麻烦;技术架构复杂,技术成本高;
3、服务之间通信会对性能有一定损耗;
概念:SpringCloud是基于SpringBoot实现的微服务治理工具包,用来管理和协调微服务;
常用组件:
RPC:Remote ProcedureCall,远程调用协议,自定义数据格式,基于TCP通信,Dubbo框架使用的RPC通信;
HTTP:基于TCP的网络传输协议,规定了数据格式,消息臃肿,SpringCloud使用的就是HTTP协议;
Dubbo是阿里巴巴提供的一个服务治理和调用的框架,现在是Apache的顶级项目,只是一个RPC框架,相比于SpringCloud缺少很多功能模块,例如网关、链路追踪等,使用时通常结合zookeeper作为注册中心;
与SpringCloud对比如下:
微服务会有很多的服务,服务之间要进行网络通信,就需要通信地址,通过Eureka进行统一管理,拥有服务注册与发现的功能;
组成:EurekaServer服务端(注册中心);EurekaClient客户端;
EurekaServer是一个独立的服务,所有的EurekaClient需要将自己的通信地址注册到服务端(使用ip、端口等),在EurekaServer就会形成一个微服务的通信地址列表
服务发现: 注册到EurekaServer的客户端会定期(RegistryFetchIntervalSeconds:默认30s)从服务端拉取一份微服务通信地址列表缓存到本地,当需要调用其他服务时就会在本地缓存列表中通过服务名找到通信地址发起HTTP请求;
服务续约: 正常运行的Eureka客户端会定期(LeaseRenewalIntervalInSeconds:默认30s)向EurekaServer服务端发送一次“心跳”请求进行续约,证明自己正常,不要 将自己从通信地址列表中剔除;默认情况EurekaServer接收到的服务续约“心跳”失败比例在15分钟内低于85%就不会剔除服务;
***为避免单个EurekaServer服务器负担过大及解决单点故障问题,可以进行EurekaServer集群;
微服务中为了解决服务单点故障的问题,往往会对微服务进行集群,而集群之同一个服务就会有多个通信地址,此时就需要用到负载均衡器来帮助我们自动分发请求到服务器中;在微服务项目中可以用Ribbon或者Feign来完成负载均衡的工作;
在微服务项目中,很多需要登录和权限才能访问,但不可能每个服务都去写一套登录认证授权的逻辑,代码重复量太大,故使用Zuul作为微服务的大门,所有请求都要经过Zuul,通过Zuul来将请求分发到其他微服务中,只需要在Zuul中做一次认证授权就行了;
Zuul是netflix开源的一个API Gateway服务器,本质上是一个web servlet(filter)引用;它是一个独立的服务,集成了Ribbon和Hystrix,实现负载均衡和服务熔断,同样Zuul也需要注册到EurekaServer中才能获取到正常运行的下游服务通信地址,发起调用;
Zuul底层是通过各种的Filter来实现的,按照执行顺序依次为:"pre"前置,"routing"路由,"post"后置,"error"异常;一般自定义Filter都是前置过滤器;
正常执行流程:请求到前置过滤器(可能还有自定义的前置Filter),然后到路由过滤器,路由中去服务提供这执行业务,正常执行后再到后置过滤器,最终返回结果;
异常执行流程:
RBAC:Role-Based Access Control,角色访问控制,将权限与角色进行关联,不同的角色对应多个不同的权限,再将角色赋予给用户,从而使用户拥有对应的权限,简化了系统中对用户权限的统一管理;
实质上就是Who、What、How的关系,即Who操作What,如何操作How;Who:权限的主体,用户;What:要操作的资源;How:权限;
传统的认证流程:
使用SpringSecurity的认证流程:
Web授权:在Security的配置类中通过HttpSecurity.authorizeRequests()对资源指定访问权限;常用的API如下:
anyRequest(): 任何请求;
antMatchers(“/path”) :匹配某个资源路径;
authenticationed() : 保护URL需要登录访问;
permitAll(): 指定url无需保护(放行)一般用户静态资源;
hasAuthority(String authority): 某个资源需要用户拥有什么样的权限才能访问;
hasAnyAuthority(String … authorities): 某个资源拥有指定权限中的一个就能访问;
----------在项目中有权限表,每个访问资源对应所需要的权限,可以全部查询直接添加到授权中;
方法授权:通过方法上注解来标记需要什么样的权限才能访问;使用时需要在Security配置类中@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled= true)开启方法授权的支持,通常在方法上使用注解@PreAuthorize(“hasAuthority(‘…’)”) 在进入方法之前进行权限验证;(@PostAuthorize是方法执行后验证,基本不会用)
OAuth协议是一个用户资源授权的安全开放简单的标准,使用该协议进行授权不会触及到第三方账号的用户名和密码等账号信息;
OAuth2授权模式:
Lucene是Apache的一个开源的全文检索引擎工具包,方便开发人员在系统中实现全文检索的功能;Lucene适用于小型项目,它仅仅是一个库,要想适用就必须集成到项目中;Lucene的两大核心是索引创建和索引搜索;
索引创建:将结构化或非结构化的数据信息提取,创建索引;(分词、规则排序、合并相同单词)
索引搜索:搜索时通过关键词进行检索,获取到对应的索引,最终是通过索引直接获取到数据取的结果;
Lucene作为目前性能最好、功能最全最先进的搜索引擎库,但由于它只是一个库,要使用就必须用Java作为开发语言并集成到项目中,而且配置复杂,就诞生了在Lucene基础上的ElasticSearch;
ElasticSearch解决了原生Lucene的不足并优化调用方式的同时,还实现了高可用的分布式集群的搜索方案;
ES除了实现全文检索功能外,还具有以下特点:
标签 | 作用 |
---|---|
match | 匹配 要分词处理使用 |
term | 精确匹配 不分词 |
range | 范围查询 |
bool | 过滤,用于需要多个过滤条件组合查询,必须包含must、must_not、should中的一个或多个 |
must | 相当于and |
should | 相当于or |
must_not | 相当于not |
ElasticSearch -> 创建索引库Indices -> 类型Types -> 文档Documents -> 字段Fields;
ES集群中可以包含多个索引库(相当于数据库),而每个索引库 又可以包含多种类型(相当于数据库中的表),每个类型包含多个文档(相当于表中的一行),每个字段又可以包含多个字段(相当于表中的列);
前期就要做好规划,设计先行,编码在后,主要可以从以下三个方面进行调优:
Shard分片:单台机器无法存储海量数据,可以将一个索引中的数据切分成多个shard,分布在多台服务器进行存储;
Replica副本:每个服务器都可能故障,造成shard丢失,为每个Shard创建多个replica放在其他服务器备用;建立索引时默认5个primary shard分片(建立索引一次设置),每个分片一个replica(可以随时修改);
ES状态:green(全部shard和replica可用);yellow(主分片正常,至少缺失一个replica);red(至少一个shard及其replica缺失,有数据缺失);
Master Node:主节点,只负责维护集群及索引库的操作;
Data Node:数据节点,只处理CRUD;
Client Node:只负责处理请求的匹配与分发;
默认每个节点都有成为主节点的资格,可以存数据和处理请求,需要设置node.master与node.data状态分门别类;
Linux是一套性能稳定的多用户网络操作系统,由于其具有开源免费、安全稳定、硬件配置要低、性能高的优点,当应用开发完成之后要部署到硬件服务器上,就选择Linux作为硬件服务器的操作系统;
Linux常见版本有:
命令 | 作用 |
---|---|
systemctl stop firewalld.service | stop关闭防火墙(status是查看防火墙状态;start开启防火墙) |
ifconfig | 查看本机ip地址 |
su - 用户名 | 切换登录用户及环境,不加-只切换用户不切换环境 |
ls - l | 列表查看当前文件夹内容 |
cd /usr/local | 进入目录 |
cd …/ | 返回上一级,多级可以加 |
cd ~ | 返回根目录 |
mkdir 目录名 | 创建文件夹 |
rm -rf 目录名 | 递归删除文件或文件夹 |
mv 目录1 目录2 | 目录2存在将目录1移动到2;目录2不存在则将目录1改名为目录2 |
touch 文件名 | 创建一个文件 |
vim 文件名 | 编辑文件文本,按i后开始编辑,编辑完后Esc,然后:wq保存并退出编辑 |
cat 文件名 | 查看文件文本内容 |
tar -zxvf xxx.tar | 解压tar包 |
unzip xxx.zip | 解压zip包 |
ps -ef丨grep xxx | 查看xxx进程,例如Tomcat |
kill -9 进程号 | 杀进程 |
tail -f xxx.log | 查看实时日志 |
在前后端分离的项目中,前端项目也需要进行部署,但是如果npm dun build拷贝到Tomcat的部署的话,静态页面处理的效率低下,而Nginx作为一个专门的Http服务器,处理效率更高,同时拥有负载均衡的功能;
部署npm管理的vuecli项目,npm run build打包到Nginx进行部署;对于传统的css+div项目直接拷贝部署即可;
Docker是一个开源的应用容器引擎,开发者可以打包应用及依赖包到一个可移植的镜像中,然后发布到Linux或windows机器上,也可以实现虚拟化;Docker可以帮我们构建后部署容器,只需要把打包好的程序放进容器即可,而容器又是基于镜像启动的,容器启动后就可以登录到容器安装自己需要的软件和服务;
命令 | 作用 |
---|---|
systemctl start docker | 启动Docker(停止stop、重启restart) |
docker images | 查看docker下的所有镜像 |
docker pull centos:7 | 拉取centos:7镜像 |
docker rmi $IMAGE_ID | 删除指定镜像 |
docker ps | 查看正在运行的容器(后面加 -a查看所有容器) |
docker run -it --name=mycentos centos:7 /bin/bash | 创建交互式容器,退出后容器就停止 |
docker run -di --name=mycentos2 centos:7 | 创建守护式容器,退出后容器保持运行 |
docker exec -it container_name (或者 container_id) /bin/bash | 登录守护式容器 |
docker cp 需要拷贝的文件或目录 容器名称:容器目录 | 将文件拷贝到docker容器中 |
Docker Compos是Docker官方编排项目之一,用来快速部署分布式应用,避免了自己一个一个创建和启动容器;
Kubernetes,是一个管理云平台多个主机上的容器化的开源的应用,即跨主机容器化编排工具,主要功能:基于容器的应用部署、维护和滚动升级;自我修复;负载均衡和服务发现;跨机器和跨地区的集群调度等;
k8s架构是基于Cluster集群,由Master和Node组成;Master负责提供、管理、调度k8s的资源对象以及对其进行持久化;而Node负责pod对应的容器创建与启动等,与Master密切协作,与k8s Service通信等;
Pod:在k8s中的最小部署单元,可以包含一个或多个容器,每个Pod有独立IP,且可以指定一组共享存储卷volumes;
管理Pod的控制器:ReplicaSet(副本集)、StatefulSet(有状态副本集)等···;
ReplicaSet(副本集)一般不直接使用,通常用Deployment声明式控制器来管理;
k8s中所有内容都被抽象为资源,资源实例化之后为对象;
资源分类:
Service与Pod是通过Label串联起来,同一个Service下的所有Pod通过kube-proxy实现负载均衡,每个Service都会有全局唯一的虚拟IP;
现在的Service都使用的ipvs的代理模式,客户端请求到达内核空间时,会直接根据ipvs规则分发到各个Pod上,效率远远高于以往的iptables和userspace两种代理模式;
通过Service将服务暴露到外部网络有以下四种方式:
Ingress是HTTP(S)负载均衡器,应用层调度,支持多种后端健康状态检查而Service是TCP负载均衡器,传输层调度,不支持健康检查;
Ingress是一组基于DNS域名或URL路径将请求转发到指定Service资源的规则,简单的可以理解为Service的Service,来统一管理集群内的所有服务;
可以使用Ingress来代替SpringCloud微服务中的Zuul网关;
分类:
MQ(Message Queue)消息队列,是用erlang语言开发,基于AMQP协议实现的消息队列,主要有以下特点:
RabbitMQ中交换机和队列默认都是非持久化的,在服务器宕机或重启之后都不会存在,所以要确保消息不丢失,首先要通过交换机和队列的durable属性进行持久化,然后对消息也要进行持久化;(当然持久化的消息会被写入磁盘,比写入内存慢很多,要进行需求和性能之间的平衡考虑)
Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue;
Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑;
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方;
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ;
Consumer:消息消费者,即消费方客户端,接收MQ转发的消息;
数据结构是一种用来装数据及数据之间关系的一种集合;
算法是解决问题步骤的有限集合,通常用某一种计算机语言进行伪码描述;
算法复杂度分为时间复杂度(执行算法所需时间长短)和空间复杂度(所需要的存储空间大小);
通常理论上以时间复杂度判别算法优劣:时间频度,即一个算法中语句执行的次数,算法所需时间与之成正比;
常见算法:递归、排序(冒泡排序)、散列算法(hash算法)、查找(二分查找、hash查找···)等;
Red-Black Tree,是一种自平衡的二叉树,每个节点都有颜色(红或黑);
根节点及所有的叶子节点都是黑色;
如果一个节点是红色,则其子节点必然为黑色;
从根节点到每个叶子节点NIL所经过的黑色节点个数是一样的(接近平衡);
通过左旋、右旋及变色保证自平衡;
FastDFS是一个开源的分布式文件系统,用来对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。我们在maven工程中使用是导入了fastdfs-client-java包,并在fdfs_client.conf文件中配置了tracker-server服务器,由于多个地方会使用到fastDfs,专门抽取了一个文件上传下载的工具类,需要使用的时候直接调用相应方法即可。需要注意的是由于可以集群,在配置时每个tracker要进行正确配置。
核心线程、非核心线程、等待队列、拒绝策略
理论上最大线程数:(N为CPU核数)CPU密集型:N+1;IO密集型:2N+1;
为什么优化:根据硬件及场景,合理配置JVM的参数,使JVM运行程序效率最佳;保证在高并发情况下程序能平稳顺畅运行;
JVM组成:
分为4种:
双亲委派机制:
我们的程序是使用应用程序类加载器,但不会第一时间就使用它,而是先委托给它的父类扩展类加载器,再由父类委托给启动类加载器,所以是最优先使用启动类加载器加载,加载不到再由父类扩展类加载器加载,当父类也加载不到最终才会使用应用程序类加载器进行加载,如果都加载不到类就会报ClassNotFoundException;过程中一旦有一级加载到就不会继续加载;
为何使用双亲委派机制:1) 不让我们轻易覆盖系统功能;2) 使我们也能扩展功能;
新生代收集器:ParNew、Serial(淘汰) 、Parallel Scavenge(JDK1.8使用);
老生代收集器:CMS、Serial Old(淘汰) 、Parallel Old(JDK1.8使用);
堆内存收集器:G1(JDK1.9默认使用)
调优工具:jVisualvm、jConsole;
主要就是调整JVM的内存及GC等相关参数到最适合当前硬件状态;
在同一时间,一个代码块同时只能由一个线程可以执行,要实现就需要在某个地方做标记让使线程同步;
单体项目中,属于单进程可以有多线程,只需要用synchronize或Lock就能实现同步;
分布式系统中,是多个进程,要实现某个方法在同一时间只能被某个进程的某一线程执行,就需要使用到分布式锁;
分布式锁需要满足的特点:1) 是可重入锁,避免死锁;2) 高可用的获取锁和释放锁;3) 最好是阻塞锁;4) 最好是公平锁;
常见的分布式锁:基于数据库;基于缓存;基于Zookeeper;数据库的基本不用,另外两种根据实际情况选择
Innodb行锁
S Lock共享锁:允许事务读取一行数据,Mysql查询默认使用,即同时可以多个来查询,要改成排他锁在SQL语句后加for update即可;
X Lock排他锁:删除或更新,即一次只能一个操作改行,要获取锁需要等占用的事务释放锁;
数据库分布式锁可以使用X锁,但是性能低下,查询都要上锁;也可以不上锁,使用乐观版本控制,每次提交时才检测数据是否冲突;还可以通过唯一性(主键或唯一索引),使多个请求同时提交到数据库只有一个可以操作成功;
使用Redis中的Redisson自己集成的分布式锁,只需要导包获取即可,RedissionUtils.getInstance().getRLock(redisson, “goods” + goodsId);
当然也可以通过redis命令及redis lua支持的原子性自己实现分布式;
Zookeeper会维护一个有层级的树状数据结构,树状结构的节点分为:持久无序节点、持久顺序节点、临时无序节点、临时有序节点;
要实现分布式锁,主要使用临时有序节点+watch来完成:多某一个方法实现分布式锁,创建一个该方法对应的持久节点,每个线程进来先判断是否有前面节点,没有就创建一个临时有序的节点并直接获取锁,如果有前面节点存在,同样创建一个临时有序的节点但同时监听最末的一个节点,持续获取锁资源,当监听节点执行完释放后就能拿到锁执行方法;
本地事务:一个数据源,只操作一个数据库;
分布式事务:多个数据源,同时操作多个数据库;比如微服务系统中,一个服务中的某个事务需要调用其他服务的事务;
常用解决方案:2PC、TCC、可靠消息最终一致性、最大努力通知···
和Java中一样,只有true和false两种取值,但是在JS语法中,表示false的有6个值:0; NaN; undefined; 空字符串; false; null;
Asynchronous Javascript And XML异步JS和XML,是网页开发中改善用户体验的异步刷新技术;
传统的浏览器与服务器交互是同步请求,即使只修改页面中一小部分的数据,也要销毁整个页面,浪费网络传输;
而使用Ajax交互是异步请求,不刷新页面,只提交和更新局部数据,在发送请求的同时还可以继续操作页面,互不干扰,节约网络资源,减少不必要的数据传输;
jQuery是一个开源的js框架,封装了js的属性和代码,优化了使用;它的核心特点是:支持链式编程;功能强大,支持更多类型的选择器;兼容各种主流浏览器;
常用的选择器:
id选择器:#id;
类选择器:.class属性;
层级选择器:儿子选择器 > 获取下面的子元素; 后代选择器 空格 获取下面后代;
标签选择器:标签名;
属性选择器:[attribute] 或[attribute=value] ;
window.onload是指页面加载完毕,包括所有的图片、视频等资源都加载完毕后才会执行;
$(function(){})是 $(document).ready(function(){})方法的简写,在页面结构加载完毕后就就会执行,执行时间在window.onload之前;
Bootstrap是一个移动设备优先的前端框架,封装了很多现成的js和css,是web开发更加迅速简单,支持响应式布局;常用的有表单、表格、模态框、布局、栅格系统等;
Vue是一套用于构建用户界面的渐进式框架,可以自底向上逐层应用,只关注视图层;
主要有以下特性:
每个Vue实例需要通过el进行挂载到标签,可以通过id或class进行挂载,使用模板及常用属性例如:
<div id="myApp">
<h1>{
{
message}}</h1>
</div>
<script type="text/javascript">
//创建vue是需要给他传递一个对象
new Vue({
el:"#myApp", //id在hmtl模板中要对应
data:{
//数据模型
},
methods:{
//挂载标签中的方法;无缓存,需要事件才能调用;
},
watch:{
//监听Vue数据变化,从而做出反应
},
computed:{
//专门用来做计算的属性,必须有返回结果,有缓存,数据未发生变化则不会调用
},
created(){
//钩子函数,在Vue实例构建后执行
},
mounted(){
//钩子函数,在Vue实例构建并挂载完成后执行
}
})
</script>
组件就是一个完成特定功能的HTML标签;
<div id="app">
<mycomponent1></mycomponent1>
</div>
<div id="app1">
<mycomponent1></mycomponent1>
</div>
//定义第一个全局组件,挂载标签中都能使用
Vue.component("mycomponent1",{
template : "这是第一个全局组件
"
})
var app = new Vue({
el: "#app",
data: {
}
});
var app1 = new Vue({
el: "#app1",
data: {
}
});
<div id="app">
<mycomponent></mycomponent>
</div>
var app = new Vue({
el: "#app", //只能在挂载的app标签中使用
data: {
},
components : {
"局部组件的名字1" : {
组件的配置对象},
"局部组件的名字2" : {
组件的配置对象}
}
});
路由就是负责将进入浏览器的请求映射到特定组件中,指定由哪个组件来响应请求,简单来说就是url地址和对应资源之间的映射;