Java后端常考面试题梳理
2024-06-22 02:23:56 1 举报
AI智能生成
登录查看完整内容
算是对几个知识脉络的总结,由于免费额度不够了,后续新增的其他模块会往这里添加。 2024年6月22日02:23:44:补充前端知识
作者其他创作
大纲/内容
问题: 早期的网页都是静态的,内容固定,用户与页面的互动非常有限。
解决: HTML(HyperText Markup Language)出现,允许开发者创建和格式化静态网页。它定义了网页的基本结构,如标题、段落、链接、列表等。
HTML
问题: HTML虽然可以创建网页,但控制样式和布局非常困难,代码混乱,难以维护。
解决: CSS(Cascading Style Sheets)被引入,它将内容与样式分离,使开发者能够更容易地控制网页的外观和布局,提高了代码的可维护性和重用性。
CSS
问题: 静态网页无法响应用户操作,缺乏互动性。
解决: JavaScript的出现,使得网页可以在客户端执行代码,实现与用户的互动。DOM(Document Object Model)提供了一种操作HTML和CSS的方式,允许动态更新内容和样式。
JavaScript包含以下实现部分:核心ECMAScript,可以理解为对这一规范的实现。文档对象模型DOM,对HTML文本操作的抽象,呈现树结构。提供与网页进行交互的方法和接口浏览器对象模型BOM,对浏览器窗口操作的抽象。提供与浏览器进行交互的方法和接口
JavaScript的实现
使用<script>元素。
标签中常用的属性:async 表示应该立即开始下载脚本 defer 脚本会被延迟到整个页面都解析完毕后再运行 charset 使用 src 属性指定的代码字符集 src 表示包含要执行的代码的外部文件 type 表示代码块中脚本语言的内容类型
不管包含的是什么代码,浏览器都会按照<script>在页面中出现的顺序加载它们。也因此,现代Web应用程序通常将所有JavaScript引用放在body元素中的页面内容最后面,减少页面的空白时间,让用户感觉加载更快。
<script>元素的位置
JavaScript的使用
区分大小写语句分号结尾
语法
var声明作用域:function中通过var声明的是局部变量,如果不带var,则声明的是全局变量,但是不建议这么做,不易维护。
var声明提升:var 关键字声明的变量会自动提升到函数作用域顶部 。 所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部 。
var重复声明:
var
声明作用域:作用域是块作用域 ,而var是函数作用域
重复声明:不允许重复声明
声明提升:let声明不会被提升。
let
行为基本和let相同,但是它必须声明和初始化一起,并且不支持修改,简而言之,就是用来声明常量。
const
①不使用var,let和const带来了明确的作用域、声明位置和值,垃圾回收的性能也更好。②优先const,其次才是let。
声明风格及最佳实践
变量的声明:var、let、const
变量
null和undefined的区别null是程序级别的,undefined是系统级别的,null是一个特殊的对象,undefined是一种更甚层次的不存在,在用法上两者其实没有太大的区别,不同人的使用习惯也不一样。
原始类型:Undefined、NULL、String、Number、Boolean、Symbol引用类型:Object对象Boolean值不能用0和1区分,只有true和false。Undefined和NULL是一个值得讨论的点。
数据类型
操作符
条件判断、流程控制
函数
a?.b?.c?.d
链式判断
数组解构、对象解构声明初始化反着写
解构赋值
参数默认值
原型、原型链、继承
箭头函数 参数 箭头 方法体 () => {}
函数、闭包、箭头函数
模块化
Promise对象的三种状态:pending、fulfilled,then()处理、rejected catch()处理
Promise对象、async、await
元编程
语言基础
JavaScript
解决: jQuery简化了DOM操作和事件处理,提高了开发效率。内置的浏览器兼容性处理,减少了开发者的负担。
Jquery
问题: 页面每次需要更新内容时都要重新加载整个页面,用户体验不佳。
解决: AJAX技术允许网页在不重新加载整个页面的情况下与服务器进行异步通信,动态更新部分内容,提高了用户体验。JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,简化了客户端与服务器之间的数据传输。
Ajax
问题:● 随着Web应用的复杂性增加,直接操作DOM变得难以管理。● 缺乏组织代码的方式,导致代码难以维护。
解决:● 引入MVC(Model-View-Controller)架构,将应用逻辑和视图分离。● 提供双向数据绑定,简化了数据和视图的同步。● 提供依赖注入(Dependency Injection),提高代码的可测试性和可维护性。
特点:● 模板语法强大,允许在HTML中直接使用JavaScript表达式。● 内置许多常用功能,如表单验证、路由等。
AngularJS
问题:● 双向数据绑定在大型应用中容易导致数据流混乱。● 操作DOM依然是性能瓶颈。
解决:● 引入单向数据流,简化了数据管理。● 提出虚拟DOM概念,提升了DOM操作的性能。● 使用组件化开发模式,提高了代码的重用性和可维护性。
特点:● JSX语法,允许在JavaScript中直接编写类似HTML的模板。● 强调组件化,鼓励将UI拆分成可重用的组件。● 丰富的生态系统,如React Router、Redux等。
React
问题:● AngularJS的复杂性和React的学习曲线较高。● 开发者需要一种更加轻量和易上手的解决方案。
解决:● 提供简单易用的API,降低学习门槛。● 结合了AngularJS的模板语法和React的组件化思想。● 提供渐进式架构,允许开发者根据需要引入功能。
特点:● 轻量化,核心库关注视图层,其他功能通过插件扩展。● 简洁的模板语法,支持双向数据绑定。● 强大的单文件组件(Single File Component),将模板、逻辑和样式集中在一个文件中。
Vue2和Vue3双向数据绑定和单向数据流虚拟DOM选项式API和组合式API如何理解渐进式模板语法ref()组件化props生命周期钩子指令依赖注入Slots SFCVite和Vue CLi客户端路由和服务端路由状态管理服务端渲染
常考
Vue
问题:● AngularJS架构过于复杂,不易于扩展和维护。● 需要提高性能和开发体验。
解决:● 完全重写了框架,采用现代化的开发理念和工具。● 引入基于TypeScript的强类型支持,提高代码的可靠性。● 提供更好的性能和更简洁的API。
特点:● 基于组件的架构,支持依赖注入。● 强大的CLI工具,提升开发体验。● 丰富的官方支持库和工具,如路由、表单处理、HTTP请求等。
Angular(Angular 2+)
问题: 随着项目规模的增加,代码的组织和管理变得越来越复杂,浏览器不支持最新的JavaScript特性。
解决: Webpack等构建工具的出现,使得模块化开发、代码打包和优化变得更加便捷。Babel编译器允许开发者使用最新的JavaScript特性(如ES6),并将其转换为兼容旧版浏览器的代码。
Webpack、Babel、ES6模块
问题: 大型项目中的类型安全、数据管理、服务端渲染等问题需要更强大的解决方案。
解决: TypeScript引入了静态类型检查,提升了代码的可靠性和可维护性。GraphQL提供了一种灵活的查询语言,提高了数据获取的效率。Next.js和Nuxt.js等框架提供了服务器端渲染(SSR)和静态站点生成(SSG)功能,提升了页面加载速度和SEO表现。
TypeScript、GraphQL、Next.js、Nuxt.js
前端知识
uni-app
微信小程序
全端知识
抽象
封装
继承
多态
特点
默认构造函数的提供机制
重载和重写的区别
子主题
面向对象
String
StringBuilder
StringBuffer
Object
System
Runtime
常用类
List接口的特点
Set接口的特点
ArrayList
HashMap
集合
Error
非运行时异常
运行时异常
Exception
try catch
throws
异常处理
throw
手动异常
继承Exception或者RuntimeException类
提供无参和有参构造
自定义 异常
异常
泛型方法
泛型接口
常用的
类型参数化
定义?
extend
上届
super
下界
上下界
泛型
Target
Retention
Inherit
Document
元注解
Retention要是Runtime
自定义注解
注解
Java基础
在程序运行的过程中,动态的获取类的信息、动态的调用对象的属性和方法
反射能做什么
Class
Metho
Field
Constructor
Annotation
核心类
反射
Thread类
Runnable接口
创建、结果传递、结果合并、任务顺序、异常处理
链式操作
优化:8提供的CompletableFuture类
Callable接口+FutureTask
线程的创建
NEW
Runnable
Block
Wait
TimeWaitng
Terminate
六大状态
线程的状态
Synchronize
ReentrantLock
Lock接口的实现类
Volatile修饰
final修饰
原子类
线程的同步
wait
notify
notifyall
Object方法
await
signal
Condition
park
unpark
LockSupport
线程的通信
Lock接口
AQS
CountDownLatch
CyclicBarrier
Semaphore
Phaser
工具类
JUC
减少资源消耗
提高响应速度
提高线程可管理性
线程池的优点
核心线程数
最大空闲线程数
空闲线程存活时间
空闲线程存活时间单位
阻塞队列
线程工厂
拒绝策略
线程池的核心参数
核心线程数没满,就通过核心线程去处理,核心线程满了队列没满,进入等待队列;等待队列满了,判断最大空闲线程数,满了就用拒绝策略处理,没满就开一个线程处理。
任务提交到线程池的处理流程
cache
fix
schedule
JDK提供的线程池
CPU密集型
IO密集型
如何自定义线程池
线程池
多线程并发
参数 箭头 方法体
lambda表达式
获取流
处理流
收集流
三大步骤
StreamAPI
用于检查接口是否满足函数式接口的使用条件
标注的接口只能声明一个未实现的方法
@FunctionalInterface
函数式接口
LocalDate
LocalTime
LocalDateTime
时间API
新特性
Java进阶
连接器:建立连接,管理连接、校验用户身份
查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块
解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型
预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列
优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划
执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端
执行 SQL:执行 SQL 共有三个阶段
执行一条 SQL 查询语句,期间发生了什么?
行锁(重点记忆)
事务支持(重点记忆)
热备份
外键(重点记忆)
MVCC
聚簇索引
特征
最小存储单元是页,一个页的大小是16KB
存储方式
InnoDB
表锁
不支持事务
不支持外键
没有热备份
MyISAM(没用过)
内存中,不能持久化
Memory(没用过)
存储引擎
共享锁
排他锁
范围锁
根据读写
整库备份
全局锁
表共享锁
表独占锁
意向锁
系统自动控制,显示使用
元数据锁
表级锁
记录锁
间隙锁
临建锁
行级锁(通过对索引项加锁实现)
根据粒度
锁的分类
Record lock:记录锁,锁定一个记录上的索引,而不是记录本身
Gap lock:间隙锁,锁定索引之间的间隙,但是不包含索引本身
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。
MVCC 不能完全解决幻读的问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。
Next-key lock:record+gap 临键锁,锁定一个范围,包含记录本身
锁算法
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View(快照读) 来实现的
隐藏主键,有主动设置主键则不会生成
DB_ROW_ID
事务id
DB_TRX_ID
回滚指针,指向undoLog
DB_ROLL_PTR
隐藏字段
MVCC的作用就是在避免加锁的情况下,最大限度的解决并发读写的问题,它可以实现提交度和重复度
串行化则是真的通过锁实现的
MVCC解决隔离性问题
锁
没用过
视图\\存储过程\\触发器
水平分
垂直分
事务
主键唯一性
JOIN
问题
分库分表
属性不可再分,几乎所有数据库都满足
第一
非主键字段依赖于主键字段
第二
在非主键字段依赖于主键字段的前提下,强制不能传递依赖,必须直接依赖
第三
数据库三范式
整型
浮点
数值型
这个性能会比较高
char(定长)
varchar(变长)
二进制、文本
字符型
到9999年,但是没有时区概念
DATETIME(8字节)
到2038年,有时区概念,项目中建议使用这个
TIMESTAMP(4字节)
时间日期型
Json(mysql8)
这个个人不太回去记忆语法的使用。建表语句,实际项目中都是在工具上(PDManer)定义好表结构信息,然后一键生成。至于修改的操作,开发人员(就我前司而言)基本没机会接触,这得找运维执行。
表结构
DDL
这个个人没怎么记忆,项目中根本就没有自己手写过这类SQL,直接调ORM框架封装的方法得了,够用
新增、修改、删除记录
DML
这个写一些复杂SQL的时候会用到,简单的CRUD同样直接调ORM框架封装的方法
没啥好说的
基本查询
没啥好说的,where
条件查询
count
avg
sum
min
max
PS:NULL值不参与聚合运算
聚合函数
Group by ... having...
执行顺序 where>group by >having
where不能用聚合函数,having可以
分组查询(Group by)
默认升序
排序查询(order by)
这里再深入说一下,分页操作不是指查询出a-b之间的数据,而是把a+b条记录查出来,再减去前面的a条,所以遇到深度分页的情况下需要进行特殊的优化(有一说一我们在项目中除了特定的需求需要,一般都会对分页上限做限制的,防止出现深度分页,b站的个人中心里很多分页操作就禁止了深度分页)
分页查询
查询记录
DQL
这个我作为开发也没确实没机会接触
用户权限控制
DCL
分类
SQL
没怎么用过
Not NULL
UNIQUE
PRIMARY KEY
FOREIGN KEY
PS:用的比较多的就这四个,对于外键,其实也没用过在数据库层面上,基本都是在业务层解决,直接在数据库上加外键太耗性能了
约束
没咋见过,大都是一张表字段太多了要拆开来提高性能
一对一
在多的一方建立外键,指向一的一方的主键(这个不是真建,只是想表达这个意思,在业务层解决)
两张表
一对多
三张表,一张关联表,关联两个表的主键
多对多
表关系
取交集
内连接
整个左边
左外连接
整个右边
右外连接
外连接
自连接
会去重
union
不会去重
union all
联合查询(列数、对应的数据类型要一致)(没用过)
能玩的挺花的,不过我没怎么用过,最多用一下单值的
单值
单列
单行
多行多列
子查询
查询分类
多表查询
使用 undo log(回滚日志) 来保证事务的原子性。
原子性保证了事务的多个操作要么都生效要么都不生效,不会存在中间状态
原子性(A)
MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,好像还有服务层的binlog?
持久性保证了一旦事务生效,就不会因为任何原因而导致其修改的内容被撤销或者丢失
持久性(D)
读锁(共享锁)
写锁(排他锁)
范围锁(范围排他锁)
锁分类
MySQL InnoDB 引擎通过 锁机制、MVCC 等手段来保证事务的隔离性
Mysql默认隔离级别为可重复读
不同隔离级别以及幻读、不可重复读、脏读等问题都只是表面现象,是各种锁在不同加锁时间上组合应用所产生的结果,以锁为手段来实现隔离性才是数据库表现出不同隔离级别的根本原因
只加写锁
最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-UNCOMMITTED(读取未提交)
加读锁、写锁,读锁在查询操作执行完立即释放
允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
READ-COMMITTED(读取已提交)
加读锁、写锁
对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
只读、读写事务下可以靠mvcc实现不加锁避免幻读,写写事务下还是无法避免;
细说这个级别下的幻读
REPEATABLE-READ(可重复读)
加读锁、写锁、范围锁
最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读,但是性能最差。
SERIALIZABLE(可串行化)
隔离级别
使用读已提交解决
一个事务读到另外一个事务未提交的数据
脏读
使用可重复读解决
一个事务中对同一数据的多次读取结果不一致
不可重复读
使用串行化解决
一个事务中对表的多次数据查询结果不一致
幻读
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次查询同一条查询语句(DQL)时,记录发现记录增多或减少了
事务并发带来的问题(这个并入隔离性里)
隔离性保证了每个事务各自读、写的数据互相独立,不会彼此影响
隔离性(I)
保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾
一致性(C)
补充:纠正网上对于ACID的说法:ACID这四种特征并不正交,A、I、D是手段,C是目的。前者是因后者是果,弄到一块完全是为了拼凑的单词缩写
ACID
查询快
好处
需要维护,占内存
缺点
索引是一种用于快速查询和检索数据的数据结构
B树
PS:问的比较多的是和B树、红黑树相比,优点在哪里
①数据存储在叶子结点
②叶子结点之间有双向的指针,提高查询效率
③层级少,高度低,查的快
B+树
不支持范围查询
不支持模糊查询
有hash冲突问题
Hash
ES
Innodb有,但是我没用过
全文索引
MyISAM的,用的比较少
R树
索引的数据结构
主键索引
唯一索引
INDEX
普通索引
FULLTEXT
索引类型
.ibd文件
聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引(叶子结点存放的是数据)
非聚集索引即索引结构和数据分开存放的索引(叶子结点存放的是数据的主键,需要通过主键回表查询)
存在主键,主键就是聚集索引
不存在主键,将第一个使用唯一索引的作为聚集索引
没有主键或合适的唯一索引,生成隐藏id作为聚集索引
聚集索引的选择
聚集索引与非聚集索引(二级索引)(聚簇不聚簇不是索引的类型,是数据的存储方式)
从二级索引中获得数据的主键值,再去聚集索引中查找,尽量避免回表查询
回表查询
①遵守最左前缀原则
②不要在索引列上使用函数运算
③范围查询使用>=或者<=
④字符串类型加引号
⑤不要使用头部模糊查询
⑥or条件左右的列都要有索引
从非聚集索引(二级索引)中即可c查询到的记录,不需要进行回表查询操作
覆盖索引是是, ?
走覆盖索引
⑦不要用*号
⑧索引长字符串,建立前缀索引
⑨尽量用联合索引
⑩控制索引数量
索引优化总结
批量插入数据用load
插入数据
非顺序插入会导致页分裂
主键顺序插入(自增)性能高于非顺序插入(UUID)
不通过索引
using filesort
通过索引
using index
通过索引提高效率,遵循最左前缀
Order by优化
Group by 优化
通过覆盖索引+子查询优化
Limit越往后查询效率越低
使用count(1)或者count(*)
COUNT()
条件用索引,防止行锁升级为表锁
update 优化
SQL优化
SQL执行频率
慢查询日志(实际工作不怎么用)
navicat执行完返回的结果分析用的这个
profile
查询序列化,id相同执行顺序从上到下;不同值越大优先级越大,NULL为最后执行
id
简单表,不使用表连接或者子查询
simple
复杂查询中最外层的 select
PRIMARY
UNION 中的第二个或者后面的查询语句
SELECT/WHERE之后包含了子查询
SUBQUERY
包含在 from 子句中的子查询。
derived
select_type
涉及到的表名
table
如果查询的是基于分区的表,该字段显示查询将会访问的分区
partitions
连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、 index、all
MySQL 能在优化阶段分解查询语句,在执行阶段不需访问表或索引
NULL
该表只有一行,是 const 的特例
system
该表最多有一个匹配的行,MySQL 能对查询的某部分进行优化并将其转化成一个常量,因为只有一个匹配的行,所以速度非常快
primary key 或 unique key 索引的所有部分被连接使用,最多只会返回一条符合条件的记录
eq_ref
相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值比较,可能会得到多个结果
ref
范围扫描通常出现在 in,between,>,<,>=等操作中,使用一个索引来检查给定范围的行
range
扫描全索引就能拿到结果,一般是扫描某个二级索引,对二级索引的叶子节点进行遍历和扫描,所以速度较慢,而二级索引一般比较小,所以比 ALL 快
index
全表扫描,扫描聚簇索引的所有叶子节点,通常这种情况需要增加索引进行优化
ALL
type
显示可能应用在这张表上的索引,一个或多个。有时显示的是 NULL 值,是因为 MySQL 判断表中数据不多,不需要使用索引查询,选择全表查询
possible_key
实际使用的索引,如果为NULL,则没有使用索引。如果强制 MySQL 使用或忽视 possible_keys 列中的索引,在查询中使用 force index、ignore index。
key
表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好。
key_len
MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。
rows
表示返回结果的行数占需读取行数的百分比, filtered 的值越大越好。
filtered
Extra
explain(DESC)
SQL性能分析
列
字段
表设计
最左前缀
字符型索引加单引号
不用*号,走覆盖索引
长字符用前缀索引
模糊查询不用再开头
范围查询用>=或者<=总之就是必须能定位到一个具体的值
不要在索引列上进行运算
索引设计
深分页
统计
分组优化
排序优化
SQL编写
可能使用到的索引
使用到的索引
索引长度,越短越好
innodb认为需要查询的行数,不准
返回的行占读取的行的比例,越大越好
explain或者desc命令
SQL分析
SQL调优
索引
可预测读
不知道,数据库自身行为,你知道了也没啥用,你也干预不了它,如果被问到了,建议反问,你很感兴趣,向面试官请教一下
自适应哈希
插入缓冲区
内部优化
Mysql
一套Java与数据库系统进行通信的接口规范,Java提供接口,由各个数据库厂商进行实现。
JDBC是什么?
Class.forName
Driver
Connection
Statement
避免SQL注入,预编译
PrepareStatemant
ORM思想,将查询结果映射到类
ResultSet
Druid
Hikari
数据库连接池
DBUtil工具类封装
JDBC
某种意义上来说,你可以认为它是一种规范,Java提供了对应的接口供我们实现
只执行一次
init()
来一次请求执行一次
service()
destory()
声明周期
Servlet
本质就是一个Servlet
JSP(JavaServer Pages)
一种可重用的 Java 类,通常用于封装数据和业务逻辑
JavaBean
三大核心组件
对请求和响应进行预处理和后处理
Filter
用于监视 Web 应用程序中的事件,并在事件发生时执行相应的操作
Listener
Interceptor 是在 Spring 框架中使用的一种拦截器,它是基于 Spring 框架的 AOP(面向切面编程)机制实现的
Filter 是 Servlet 规范中的一部分,它是基于 Java Servlet API 实现的
Interceptor 主要用于拦截 Spring MVC 控制器的方法调用,对请求进行预处理和后处理
Filter 是通过在 web.xml 文件中配置来指定对特定 URL 或者某些 Servlet 请求进行拦截和处理的
Interceptor 仅作用于 Spring MVC 的控制器方法,不会拦截对静态资源的请求
Filter 在 Servlet 容器中工作,对所有的请求和响应进行拦截,包括静态资源
Filter和Interceptor的区别:
Cookie 存储在客户端
Cookie 的生命周期由客户端控制
Cookie 可以存储少量的数据
Cookie 存储在客户端,可能会被篡改
Cookie
Session 存储在服务器端
Session 的生命周期由服务器控制
Session 可以存储大量的数据
Session 更安全,因为数据存储在服务器端
Session
JavaWeb
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
解决方式:反射
手动对象关系映射
解决方式:连接池?
数据库连接管理?
解决方式:预编译
SQL 注入攻击
xml配置
配置繁琐
结果集获取
ORM框架解决了什么问题:
通过实例化和使用它来创建SqlSessionFactory,创建完成后就可以丢弃它。所以它的最佳作用域是作为局部方法变量来使用。
SqlSessionFactoryBuilder
应用运行期间一直存在。它的最佳实践应该是单例的。最佳作用域是应用级别的作用域。
SqlSessionFactory
每一个线程都应该有自己的SqlSession实例。它是线程不安全的,不能被共享。最佳作用域是会话级别的。
SqlSession
从 SqlSession 中获得。
映射器实例
核心组件
select
insert
delete
update
sql片段复用
sql
自定义缓存
cache-ref
结果集映射
resultmap
XML文件常用的顶级元素
${}直接使用字符串替换,使用的场景也比较固定,动态表名、列名
#{}使用PreparedStatement 进行预编译,效果就是会使用 ? 替代,用户入参使用的比较多,安全行高,可用避免SQL注入。MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。
还有一个#{}和${}的区别
基于内存的分页,查出所有记录再按偏移量和limit取结果
内置分页的缺点
ResultMap一般不用显示配置,默认会找同名的属性的类型进行转换,当然如果结果映射确实比较复杂,还是得显示配置。
ResultMap(强大的)
ResultType
结果映射
sqlSession级别
默认开启
Select语句会被缓存;Insert、Update、Delete语句会被刷新缓存;
一级缓存
Application级别
默认不开启
二级缓存
缓存
MyBatis对会话(Session)级别的一级缓存设计的比较简单,就简单地使用了HashMap来维护,并没有对HashMap的容量和大小进行限制
一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行<cache/>
主要说一下缓存
语法<if test = \" 条件 \">条件通过需要执行的sql部分</if>
if
choose(when、otherwise)类似Java的switch
前面两中在特殊的情况下,会导致SQL格式出现问题,执行失败,这个就是比较智能,会根据不同情况进行处理
trim(where、set)
对集合进行遍历
foreach
动态SQL
常用插件(只需实现 Interceptor 接口,并指定想要拦截的方法签名即)
MyBatis
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MybatisX代码生成
主键自动生成
属性填充
SQL打印
多数据源
分页插件
逻辑删除
用过的
Mybatis Plus
EJB不好用,开发难度高、性能差,而Spring的出现就是为了简化Java EE的开发,Spring是一个轻量级的Java EE开发框架
Spring的起源
非侵入式
DI是具体实现
IOC思想
AOP
组件化
容器化
Spring的优势
提供了一个 JDBC 的样例模板
提供与流行的“对象-关系”映射框架无缝集成的 API
ORM
提供一套 “消息生产者、消息消费者”模板用于更加简单的使用 JMS
JMS
支持编程和声明式事务管理。
Transaction
提供了一个支持 Object /XML 映射的抽象层实现
OXM
DataAccess/Integration
提供了简单的接口,用户只要实现响应的接口就可以快速的搭建 WebSocket Server,从而实现双向通讯。
WebSocket
提供了一个 Spring MVC Web 框架实现
提供了基本的 Web 开发集成特性
Web
Spring WebFlux 是 Spring Framework 5.x中引入的新的响应式web框架
WebFlux
提供了框架的基础部分,包括控制反转和依赖注入
Beans
封装了 Spring 框架的底层部分,包括资源访问、类型转换及一些常用工具类
core
建立在 Core 和 Beans 模块的基础之上,集成 Beans 模块功能并添加资源绑定、数据验证、国际化、Java EE 支持、容器生命周期、事件传播等。ApplicationContext 接口是上下文模块的焦点
context
提供了强大的表达式语言支持
spEl
Core
Spring 支持 Junit 和 TestNG 测试框架
Test
提供了面向切面编程实现
提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架
Aspects
Messaging
Spring核心组件(看一看就好)
控制反转是一种思想,用户管理Bean的行为转变为框架管理Bean,解放双手不用自己手动New对象,向容器“申请”。创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,达到一个解耦的效果
Bean 是由 Spring IoC 容器实例化、组装和管理的对象。
什么是Bean?
如何理解IOC?
XML
声明@ComponentScan,使用注解@Component,@Controller,@Service,@Repository声明要注册的Bean
代码方式:在配置类上添加@Configuration注解,在方法上添加@Bean注解,方法返回对象,本质上也可以认为是一个XML
IOC配置的三种方式
构造方法实例化
静态工厂方法实例化
实例工厂实例化
Bean的实例化方式
能避免循环依赖,项目在启动时如果存在循坏依赖,会抛异常提醒
保证对象的不变性,避免空指针异常
构造方法注入(推荐)
setter方法注入
Spring提供的,基于类型,结合@Qualifier注解可以byname
@Autowire
JSR250规范实现,基于name
@Resource
@Inject
注解注入(不知道对不对,官方文档里其实是只有构造和setter的,ChatGPT的回答是有这项的,但是它的回答是说这是基于字段的方式,通过在字段上添加注解实现)
依赖注入方式
no
byName
byType
constructor
自动装配(依赖注入的实现方式)
IOC容器初始化时创建
singleton单例,默认
获取Bean时创建
prototype多实例
request,web环境下
session,web环境下
application,Servlet上下文下
websocket(不了解)
Bean的作用域
1、Bean对象的创建(调用无参构造函数)
依赖注入
2、为Bean对象设置相关属性
需要实现后置处理器接口,这块逻辑对所有Bean生效
3、Bean后置处理器(初始化前)
不同的Bean可以定制化处理,使用@PostConstruct
4、Bean对象初始化(调用指定的初始化方法)
Subtopic
5、Bean后置处理器(初始化后)
6、Bean对象创建完成,使用
不同的Bean可以定制化处理,使用@PreDestroy
7、Bean对象销毁(执行指定销毁方法)
8、IOC容器关闭
Bean的声明周期
也可以直接用注解@PostConstruct
重写afterPropertiesSet()方法
实现InitializingBean接口
也可以直接用注解@PreDestroy
重写destroy()方法
实现DisposableBean
自定义Bean的生命周期
IOC(控制反转)
BeanFactory: 工厂模式定义了IOC容器的基本功能规范
BeanRegistry: 向IOC容器手工注册 BeanDefinition 对象的方法
两个顶层接口
初始化的入口在容器实现中的 refresh()调用来完成
容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IoC 容器持有 bean 信息的场所,以后对 bean 的操作都是围绕这个HashMap 来实现的.
初始化流程
第一层缓存(singletonObjects):单例对象缓存池,已经实例化并且属性赋值,这里的对象是成熟对象
第二层缓存(earlySingletonObjects):单例对象缓存池,已经实例化但尚未属性赋值,这里的对象是半成品对象;
第三层缓存(singletonFactories): 单例工厂的缓存
Spring只是解决了单例模式下属性依赖的循环问题;Spring为了解决单例的循环依赖问题,使用了三级缓存
Spring提早将对象暴露出来使用,解决循环依赖
@Lazy注解,延迟加载
修改文件名称,改变循环依赖类的加载顺序
@DependsOn注解,指定加载先后关系
生成代理对象产生的循环依赖
通过把bean改成单例的解决
多例循环依赖
使用@Lazy注解解决
构造器循环依赖
单例以外的循环依赖怎么解决?
循环依赖
Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。
bean对象创建(调用无参构造器)
给bean对象设置属性
bean的后置处理器(初始化之前)
bean对象初始化(需在配置bean时指定初始化方法)
bean的后置处理器(初始化之后)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭
生命周期
Bean实例化
IOC源码分析
在哪里干
连接点 Join point
在哪里干的集合
切入点 pointcut
Before advice
前置通知
After returning advice
后置通知
Around advice
环绕通知是最常用的通知类型,从方法参数这中拿到JoinPoint自定义通知逻辑
环绕通知
After throwing advice
异常通知
After (finally) advice
最终通知
干什么
通知 Advice
在哪干和干什么集合
切面 Aspect
引入
对谁干
目标对象
怎么实现的
织入
怎么实现的一种典型方式
AOP代理
概念
AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器
因为JDK动态代理是基于接口实现的,如果被代理的类没有实现任何接口,就无法生成对应的代理类,也就无法实现动态代理
有接口使用
JDK动态代理
非接口使用
CGLIB动态代理
通过动态代理技术(JDK动态代理或者CGLIB动态代理)在运行时动态将要增强的代码织入到目标类
动态织入
Spring AOP
编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类
静态织入
AspectJ
实现
用来定义一个切面。
@Aspect
用于定义切入点表达式。在使用时还需要定义一个包含名字和任意参数的方法签名来表示切入点名称,这个方法签名就是一个返回值为void,且方法体为空的普通方法。
@pointcut
用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接定义切入点表达式)。
@Before
用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut / value和returning属性,其中pointcut / value这两个属性的作用一样,都用于指定切入点表达式。
@AfterReturning
用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
@Around
用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut / value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定-一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。
@After-Throwing
用于定义最终final 通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点。
@After
配置方式
实现org.springframework.core.Ordered 接口,较低的那个有更高的优先级
较低的那个有更高的优先级
用Order注解
增强通知的顺序
总结来说就是 Spring AOP更易用,AspectJ更强大
Spring AOP 还是 AspectJ?
AOP(面向切面编程)
Spring默认在目标类实现接口时是通过JDK代理实现的,只有非接口的是通过Cglib代理实现的。当设置proxy-target-class为true时在目标类不是接口或者代理类时优先使用cglib代理实现
AOP源码分析
可以进行自定义粒度的事务管理
编程式事务
这个用的比较多,好用,方便
声明式事务
管理事务的方式
只读
默认无
超时
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
回滚策略
DEFAULT
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE
事务的隔离级别
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
没有就新建,有就加入
TransactionDefinition.PROPAGATION_REQUIRED
不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起
TransactionDefinition.PROPAGATION_REQUIRES_NEW
有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样
TransactionDefinition.PROPAGATION_NESTED
有就加入,没有就抛异
TransactionDefinition.PROPAGATION_MANDATORY
有就加入,没有就不管了
TransactionDefinition.PROPAGATION_SUPPORTS
不支持事务,存在就挂起
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
不支持事务,存在就抛异常
TransactionDefinition.PROPAGATION_NEVER
事务的传播行为
事务@Transactional属性
事务配置没配好
多线程调用,不同线程获取到的connection不一样,就不在一个事务了
没有commit或者rollback
粗心
方法权限不是pubilc
方法被final修饰导致aop失效
同一个类中的方法调用没触发aop
AOP失效
事务的配置没配好
Bean没被Spring托管
事务失效的场景
事务管理(基于Spring AOP实现)
Spring Framework
Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发
执行流程
更简洁的Web 层的开发
强大的约定大于配置的契约式编程支持
灵活的URL 到页面控制器的映射
非常容易与其他视图技术集成,如 Velocity、FreeMarker 等等
非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API
灵活的本地化、主题等解析
简单的异常处理
静态资源的支持
支持Restful 风格
为我们提供了什么特性?
主要读取web.xml中servlet参数配置,并将交给子类方法initServletBean()继续初始化
initWebApplicationContext用来初始化和刷新WebApplicationContext
initWebApplicationContext()
调用initStrategies(context)方法对DispatcherServlet中的组件进行初始化
onRefresh()
initHandlerXXX
SpringMVC实现原理之DispatcherServlet的初始化过程
Spring MVC
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。
为什么?
位于spring-boot.jar内部的application.properties或application.yml
默认配置
config目录下的application.properties或application.yml文件(file:./config/)
当前目录下的application.properties或application.yml文件(file:./)
类路径根目录下的application.properties或application.yml文件(classpath:/config/)
类路径根目录下的application.properties或application.yml文件(classpath:/)
应用配置文件
操作系统环境变量
命令行参数
配置加载顺序(后加载的配置会覆盖先加载的配置)
application-dev.properties
application-prod.properties
为了支持多环境配置,Spring Boot引入了profile的概念,可以创建多个环境的配置文件,通过spring.profiles.active参数指定
多环境配置
①启动创建ConfigurableEnvironment
②添加默认配置
③ConfigFileApplicationListener查找配置文件,加载到的每个配置文件会被添加到Environment中作为PropertySource,后加载的配置会覆盖先加载的配置
④根据profile.active处理Profile
⑤环境变量和系统属性,ConfigFileApplicationListener会将操作系统环境变量和JVM系统属性添加到Environment中
⑥处理命令行参数,并将其添加到Environment中
配置文件加载原理
大致原理:基于约定大于配置,本身内置了非常多的y依赖项集合,它们基于@Conditionnal注解进行注册,当我们在maven中添加了各种starter依赖后,满足装配条件的Bean会在启动时进行自动装配,实现方式看下面
定义在main方法入口类处,标注这是一个SpringBoot应用,用于启动sping boot应用项目
标注这个类这是一个配置Bean
@Configuration
@SpringBootConfiguration
在src/main/resources的META-INF/spring.factories
@Import(AutoConfigurationImportSelector.class)
让spring boot根据类路径中的jar包依赖对当前项目进行自动配置
@Import(AutoConfigurationPackages.Registrar.class)
@AutoConfigurationPackage
@EnableAutoConfiguration
注解会告知Spring扫描指定的包来初始化Spring Bean
@ComponentScan
@SpringBootApplication
自动装配
①AutoConfiguration类
②在META-INF下创建spring.factory文件,文件里加上AutoConfiguration类的全路径名org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\com.pdai.demospringbootstarter.DemoAutoConfiguration
如何自定义starter?
加载xml配置,一般是放在启动main类上
@ImportResource
application.properties定义属性,直接使用@Value注入即可
@Value
可以新建一个properties文件,ConfigurationProperties的属性prefix指定properties的配置的前缀,通过location指定properties文件的位置
@ConfigurationProperties(prefix=\"xxx\")
用 @EnableConfigurationProperties注解使 @ConfigurationProperties生效,并从IOC容器中获取bean
@EnableConfigurationProperties
组合@Controller和@ResponseBody
@RestController
获取request请求的参数值
@RequestParam
支持将返回值放在response体内,而不是返回一个页面
@ResponseBody
定义在方法上,在容器内初始化一个bean实例类
@Bean
spring容器初始化时,要执行该方法
@PostConstruct
用来获得请求url中的动态参数
@PathVariable
导入Config1配置类里实例化的bean
@Import(Config1.class)
@Order(1),值越小优先级超高,越先运行
@Order
满足某个条件才创建Bean
@ConditionalOnxxxxx
常用注解
@Scope
@Import
@Controller、 @Service、@Repository、@Component
组件注册
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnXxx
条件注解
声明组件的属性和配置文件哪些前缀开始项进行绑定
@ConfigurationProperties
快速注册组件
场景:SpringBoot默认只扫描自己主程序所在的包。如果导入第三方包,即使组件上标注了 @Component、@ConfigurationProperties 注解,也没用。因为组件都扫描不进来,此时使用这个注解就可以快速进行属性绑定并把组件注册进容器(这个还需要被标注的第三方组件类上本身标注了@configurationProperties注解)
● @EnableConfigurationProperties● 1、开启Sheep组件的属性绑定● 2、默认会把这个组件自己放到容器中常用于导入第三方包
属性绑定
常用注解(分类版本)
①创建SpringBoot项目,添加spring-boot-starter依赖
绑定配置的xxxProperties类,通过@ConfigurationProperties(prefix = \"xxx\")和配置文件进行绑定
②编写模块功能,引入模块所有需要的依赖
③编写xxxAutoConfiguration自动配置类,帮其他项目导入这个模块需要的所有组件
④编写配置文件META-INF/spring.factories指定启动需要加载的自动配置
⑤打包,发布到Maven仓库
⑥其他项目引用
个人理解:starter里边写的那些虽然加了各种注解,看上去是被Spring托管了,但是基于SpringBoot的扫描规则,这些类根本就不会被扫描到。因此,为了装载这些类,我们通过创建一个Configuration类,在类中import其他要托管的类,然后把配置类的全类名加到spring.factories文件里,通过另外一种加载途径实现装载
自定义starter
①每一个Starter都有一个spring-boot-autoconfigure依赖
②spring-boot-autoconfigure依赖的包里有一个META-INF/spring.factories文件,里面指定了所有启动要加载的自动配置类
③@EnableAutoConfiguration 会自动的把上面文件里面写的所有自动配置类都导入进来。xxxAutoConfiguration 是有条件注解进行按需加载的,不是无脑加载
④xxxAutoConfiguration给容器中导入一堆组件,组件都是从 xxxProperties中提取属性值
⑤xxxProperties又是和配置文件进行了绑定
自动配置原理
内部就是@Configuration,标注这是一个配置类
把主程序所在的包的所有组件导入进来
@Import({Registrar.class})
加载所有自动配置类:加载starter导入的组件
@Import({AutoConfigurationImportSelector.class})
排除前面已经扫描进来的配置类、和自动配置类
tips:注解的执行顺序的倒叙的
Spring Boot(核心思想:约定大于配置)有重复的,待整理
Nacos
服务注册与发现
配置中心
OpenFeign
LoadBalance
远程服务调用与负载均衡
反向代理
鉴权
流量控制
熔断
日志监控
能干嘛
Route
就是匹配条件
Predicate
请求鉴权
。。。。
直接实现接口即可
全局默认过滤器GlobalFilters
单一内置过滤器GatewayFilters
自定义过滤器
类型
三大核心
Spring Cloud GateWay
服务网关
Sentinel
服务熔断降级
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器「分别位于不同的分布式系统的不同节点之上」。一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操作,「要么全部成功执行,要么全部不执行」。
分布式事务的定义?
简而言之,必须在一致性与可用性之间做出选择
CAP(强一致性)
简而言之,我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
BASE(最终一致性)
分布式理论
seata服务端
TC(事务协调者)
seata客户端,就是sdk
TM(事务管理者)
database
RM(资源管理者)
分布式事务涉及到哪些角色?
类似JDBC,是一套接口规范,各个数据库厂商基于这套接口规范进行了实现,通用了语言级的API。2PC和3PC衍生于XA协议
XA协议是什么?
分为准备阶段和提交阶段。准备阶段:TC向所有参与者发送准备请求,询问是否可以准备提交事务。每个参与者要么准备好并锁住资源,要么拒绝准备。如果所有参与者都准备好了,TC向所有参与者发送提交请求,否则发送回滚请求。
详情
无侵入,好用
优点
阻塞同步,性能差,不适合长事务
TC单点故障风险
2PC(二段提交)
3PC 是 2PC 的改进版本,增加了一个中间阶段来减少TC和参与者之间的等待时间。准备阶段:与 2PC 类似,TC询问所有参与者是否可以准备提交事务。预提交阶段:如果所有参与者都准备好了,TC者向所有参与者发送预提交请求,参与者预提交并继续等待最终指令。提交阶段:TC发送提交请求,所有参与者正式提交事务。
解决单点问题,以及通过超时机制减少阻塞
协议更复杂,实施成本较高,没有从根本上解决性能和数据一致性的问题
3PC(三段提交)
对于3PC的落地实现
Seta-AT(√)
刚性兑付(强一致性)
2PC改良版,包含尝试(Try)、确认(Confirm)和取消(Cancel)三个步骤。Try 阶段:资源预留或锁定,检查并锁定需要的资源。Confirm 阶段:正式提交,执行实际的事务操作。Cancel 阶段:取消操作,释放预留的资源或撤销尝试阶段的操作。
灵活性高,可以针对具体业务场景优化。TCC 完全不依赖底层数据库,能够实现跨数据库、跨应用资源管理,可以提供给业务方更细粒度的控制。
设计实现复杂,侵入性非常强,需要处理好资源预留和取消的逻辑。
TCC三段补偿
每一个服务的db下额外建立一张业务无关的表,基于对这张表记录的操作实现事务操作
无锁,快
藕和度大,不可复用
本地消息表
将两个事物之间通过消息中间件进行异步解耦,类似本地消息表,但是把表放到MQ里了。
1.主动者发送\"待发送\"消息给消息微服务2.消息微服务收到消息,进行持久化数据库操作并返回(返回就是调主动者提供的一个回查接口,告诉主动者我持久化好消息了)3.主动者收到消息微服务的返回后,执行本地的业务,持久化到数据库4.主动者执行完业务,向消息微服务发送通知5.消息微服务收到通知,将数据库里的待发送状态改为已发送6.以上操作执行都ok,接着消息微服务把消息发到MQ7.被动者监听MQ获取消息内容8.被动者拿到消息内容,进行业务处理9.被动者处理完业务,通知消息微服务把数据库里的消息记录改为已完成或者直接删除PS:细节处理1.保证消息投递100%,定时任务轮询待发送状态的消息,通过主动者提供的状态查询接口确认消息状态,如果业务执行成功,向MQ发送消息并发消息状态改为已发送,如果业务失败,删除消息2.保证消息消费100%,,定时任务轮询已发送状态的消息,已过期的消息重新推到MQ,,被动者在保证幂等的情况下重新执行业务,,被动者执行完业务通知消息服务对消息进行状态修改为已完成或者删除消息
流程
@Translation+先处理本地,然后在通过MQ通知
分布式定时任务定时轮询消息服务,逼着上游向下游进行消息投递
怎么用
解耦,降低业务系统之间的耦合度
消息服务更加灵活,伸缩性强,独立维护,独立部署
相比本地消息表,降低开发成本,消息服务共用
主动者必须实现消息状态回查接口
轮询待发送和已发送两个过期时间的状态进行重试操作
需要分布式定时器实现两个任务
主动者通知被动者需要发两次消息,过于复杂
独立消息微服务+消息队列+分布式定时任务轮询(√)
适合最终一致性要求较低的业务,比如短信通知
确定
最大努力通知
柔性事务(最终一致性)
分布式事务的解决方案?
3PC的实现
AT 模式:基于 Seata 自身实现的两阶段提交,主要针对数据库操作。
AT模式
2PC改良版,不好用
TCC模式
长事务解决方案,不好用,你画内b状态机去吧
Saga模式
XA 模式使用起来与 AT 模式基本一致,用法上的唯一区别在于数据源代理的替换:使用 DataSourceProxyXA 来替代 DataSourceProxy
XA 模式:基于标准的 XA 协议,适用于跨多种资源的分布式事务。
XA模式
模式
Seata
分布式事务
Jaeger(我用过的)
Micrometer+ZipKin
SkyWalking
SpringBoot+MDC实现本地微服务链路追踪,结合feign拦截器实现分布式链路追踪
其他?
服务链路追踪
Spring Cloud Kubernetes
云原生
Spring Cloud
批处理命令的优化措施,类似Redis的原生批命令(多条命令打包执行)
①命令集写到txt文件里
②通过管道符 | ,连接redis-cli 通过添加--pipe命令执行
使用
管道
基于内存读写快
数据结构优化的好,也快
单线程IO多路复用和非阻塞IO
为什么当年说它快
作者认为Reds的主要瓶颈是内存或者带宽,不是CPU
当年为什么是单线程
时代变了大人,硬件发展快
为什么又要加多线程
4以前是单线程
4开始有一点多线程
6开始完全支持多线程
版本
Redis单线程VS多线程
对于恶意攻击导致的缓存穿透,使用布隆过滤器
对于业务逻辑本身就不能避免的缓存穿透,将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存和数据库中都没有的数据,而用户不断发起请求。
缓存穿透
设置热点数据永不过时
接口限流与熔断,降级
互斥加锁
缓存中可能过期了,没有,请求到数据库
缓存击穿
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
主从
哨兵
集群
redis高可用
数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
缓存雪崩
读数据时,先读缓存,缓存没有的话,再读数据源,然后将数据放入缓存,再响应请求
写数据时,先写数据源,然后失效(而不是更新)掉缓存
缓存污染多数是由开发者更新缓存不规范造成的
缓存污染是指缓存中的数据与真实数据源中的数据不一致的现象
缓存污染
缓存问题
只读缓存
写数据库后同步写redis
同步直写
容许数据库和redis之间的数据存在一定的延时
异步缓写
读写缓存
QPS<=1000可以用,高并发不行!
先读redis,没有再度数据库,读完回写redis
redis没有,加锁,再查一次redis,没有,从数据库查,查完回写redis,返回
双检加锁
高QPS怎么解决?
具体实现
先更数据库,再更缓存×
先更缓存,再更数据库×
但是也会引申出其他问题
进阶 延迟双删
先删缓存,在更数据库×
先更新数据库,在删除缓存(主流的,但是不100%权威)
四种更新策略
消息中间件实现最终一致性
引申
认清一个现实:缓存和数据库之间的数据是不可能永远维持在一致性状态的
缓存双写一致性
更进一步set NX PX + Lua
setNX(小公司够用)
WatchDog机制,默认情况下有30秒的锁持有时间,会进行续期操作
WatchDog机制
①单点失效
②锁续期
③可重入
④原子性
Redission解决了我们自己实现分布式锁时遇到的什么问题?
上redisson
基于RedLock实现分布式锁
分布式锁实现
对cpu友好,对内存不友好
取出的时候才会去检查
惰性删除
对内存友好,对cpu不友好
字面意思,定期扫描
定期删除
缓存过期策略
最近最少使用
lru
最不经常使用
lfu
将要过期
ttl
随机
random
volatile(设置了过期时间)
allkeys(所有key)
不淘汰
noeviction
缓存淘汰机制
缓存过期淘汰策略
简单动态字符串SDS:Redis会根据不同的键值选择使用不同的编码格式
压缩链表ziplist(特殊编码的双向链表)
哈希表hashtable
7之前
listpack(紧凑列表,它的特点就是用一块连续的内存空间来紧凑地保存数据)
7之后
ziplist(特殊编码的双向链表)
List
intset整数集合
Set
skiplist(redis跳跃表并没有在单独的类(比如skplist.c)中定义,空间换时间)
ZSet
数据类型底层的数据结构
进阶
Redis是一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),使用C语言编写,Redis是一个key-value存储系统(键值存储系统),支持丰富的数据类型,如:String、list、set、zset、hash
是什么
基于内存,读写优异
数据类型丰富,数据结构优化
持久化
为什么
工作中用的最多的就这个,而且主要是用String存token、权限信息、验证码这些
读取前,先去读Redis,如果没有数据,读取数据库,将数据拉入Redis
插入数据时,同时写入Redis
缓存的使用方式
要么是RedisUtil封一下setnx方法,要么直接用redisson
这个主要利用redis的setnx命令进行,setnx:\"set if not exists\"就是如果不存在则成功设置缓存同时返回1,否则返回0
分布式锁
计数器
消息队列
......
使用场景
keys * 查看当前库所有的key
exists key 判断某个key是否存在
type key 查看key的类型
del key 删除指定的key
unlink key 非阻塞删除
ttl key 查看还有多少秒过期 -1永不过期 -2已过期
expire key 设置过期时间
select dbindex 切换数据库
dbsize key数量
flushdb 清空当前库
flushall 通杀全部库
常用命令
基础和概念
介绍:String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象
使用场景:缓存、计数器、Session,分布式锁
动态字符串SDS
底层数据结构
介绍:Redis中的List其实就是链表
使用场景:排队功能、消息队列
快表
介绍:Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
使用场景:标签、点赞、踩、收藏
整数集
哈希表
使用场景:排行榜
跳表
压缩列表
ZSet(SortSet)
介绍:Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
使用场景:存储对象
5种基础
可以非常省内存的去统计各种计数,比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等
基数统计
Hyperloglog
两个状态的,都可以使用 Bitmaps
位图数据结构,都是操作二进制位来进行记录,只有0 和 1 两个状态
bitmap
地理位置信息
geospatial
bitfield
3中特殊
Stream
数据结构
RDB持久化是把当前进程数据生成快照保存到磁盘上的过程,由于是某一时刻的快照,那么快照中的值要早于或者等于内存中的值
内存快照
阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用
save
Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短
bgsave
手动触发
基于配置文件里设置的规则m时间内有n次修改 触发
主从复制会触发
flushdb/flushall会触发
shutdown命令,如果没有开启aof,会触发
Save m n
自动触发
配置文件里改成save \" \"
禁用rdb
触发方式
redis-check-rdb命令
rdb文件检查修复
关闭redis,把RDB文件复制到Redis的安装目录里,启动redis
恢复
RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景
Redis加载RDB文件恢复数据要远远快于AOF方式
无法做到秒级持久化
fork子进程属于重量级操作,开销大
RDB文件没有可读性,是二进制文件
实时性不够
RDB(默认)
针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决
配置文件里 appendonly yes
AOF开启(默认关闭)
AOF日志记录Redis的每个写命令
同步写回
always
每秒写回
everysec(默认)
躺平,操作系统自己控制时机
AOF写回策略
和rdb一样
redis-check-aof --fix
AOF文件修改
实时性持久化,秒级
日志不易损坏,损坏了也能修复
满足配置文件的配置,默认是大于上一次的一倍且文件到达64M
自动
bgrewriteaof命令
手动
AOF重写触发机制
Redis通过创建一个新的AOF文件来替换现有的AOF,新旧两个AOF文件保存的数据相同,但新AOF文件没有了冗余命令
①主线程fork出子进程重写aof日志
②子进程重写日志完成后,主线程追加aof日志缓冲
③替换日志文件
重写原理
AOF文件过大的情况下有AOF重写机制进行瘦身
日志内容可读
优势
AOF文件比相同数据集的RDB文件更大
恢复速度慢于RDB
劣势
AOF
同时开启数据恢复只会加载aof文件,不会加载rdb
aof-use-rdb-preamble yes
开启
aof做增量,rdb做全量
混合模式
同时关闭rdb和aof
纯缓存模式
持久化机制
Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能
1开始事务(MULTI)
2、命令入队(批量操作 Redis 的命令,先进先出(FIFO)的顺序执行)。
3执行事务(EXEC)
使用过程
你也可以通过 DISCARD (opens new window)命令取消一个事务,它会清空事务队列中保存的所有命令
WATCH是乐观锁,基于CAS
WATCH (opens new window)命令用于监听指定的键,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监视的键被修改的话,整个事务都不会执行,直接返回失败。
UNWATCH:取消WATCH对所有key的监视。
Redis的事务是原子性的:所有的命令,要么全部执行,要么全部不执行。而不是完全成功。
redis事务可以保证命令失败的情况下得以回滚,数据能恢复到没有执行之前的样子,是保证一致性的,除非redis进程意外终结。
redis事务是严格遵守隔离性的,原因是redis是单进程单线程模式(v6.0之前),可以保证命令执行过程中不会被其他客户端命令打断。但是,Redis不像其它结构化数据库有隔离级别这种设计。
redis事务是不保证持久性的,这是因为redis持久化策略中不管是RDB还是AOF都是异步执行的,不保证持久性是出于对性能的考虑。
Redis事务的ACID
消息队列,实现进程间消息通信
干嘛的
不推荐使用,专业的事交个专业的做,市面上有更好的消息中间件
subscribe channel:1
订阅
publish channel:1 hi
发布
基于频道(Channel)的发布/订阅
psubscribe c? b* d?*
publish c m1
publish c11 m1
基于模式(pattern)的发布/订阅
发布订阅(了解即可)
复制
高可用(TODO)
Redis
解耦
削峰
异步
应用场景
易用,能满足通用场景,中小企业用的多
RabbitMQ
已经没维护了
ActiveMQ
针对大数据场景,功能比较单一
Kafka
比RabbitMQ强,Java开发,但是相比较RabbitMQ,运维成本会更高,规模比较大的有运维团队的公司用的比较多
RocketMQ
和其他消息队列的对比
事务(属于代码端支持的,没用过)
生产者
消费者
Channel(AMQP),就是个连接池
connection(http)
路由到所有与它绑定的Queue中
fanout
一对一定点发送
一对多广播
Routing Key==Binding Key
direct(默认的也是这个类型,比较特殊的是它的交换机名的空字符串,所有队列以队列名作为路由键绑定到这个交换机上)
Routing Key模糊匹配
topic
不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的header属性进行匹配。
headers
exchange交换机,将消息路由到匹配的队列中
消息没那么快到内存(队列)中,会先持久化到磁盘
①基于死信队列实现延迟队列
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
使用x-delayed-message交换机
消息带x-delay头
延迟时间内,消息不会进入队列,时间到了才会进入队列
②安装插件
延迟队列
配置了死信队列的队列,消息过期后,会投递到死信队列
死信队列
queue队列
拒绝,直接丢弃
溢出,先进先出
死信的产生
丢弃
入库
监听,进入死信队列,消费端监听死信队列
死信的处理
死信消息
message消息
Virtual Host(环境隔离)
核心概念
最简单的一个Hello world
默认的策略是轮询,可以通过basicQos(1)方法更改策略,实现公平调度,消费者在处理并确认前一条消息之前,不要向消费者发送新消息
工作队列模式(竞争消费)
exchange的fanout实现
发布/订阅模式(广播)
routing模式(广播细化,通过key匹配)
topic模式(routing细化,key模糊匹配)
RPC
属于拓展特性,默认没开启,保证消息不丢失的效果非常强,比持久化强
发布者确认模式
工作模式
①声明ConfirmCallBack接口,重写confirm方法,绑定到RabbitMQTemplate
配置文件publisher-confirm-type: correlated,交换机确认
①声明ReturnsCallBack接口,重写returnMessage方法,绑定到RabbitMQTemplate
publisher-returns: true,队列确认
生产者异步确认机制
使用备份交换机,消息投递失败,投递到备份交换机
生产者生产消息到RabbitMQ Server消息丢失
配置队列的持久化
配置Message的持久化
Server存储的消息丢失
自动ACK
basicAck方法
basicNAck
acknowledge-mode: manual
手动ACK(√)
消费者使用消费成功使用ACK确认消息
消费者消费失败使用NACK,消息放回服务端,消费者重试一次
RabbitMQ Server到消费者消息丢失
消息丢失
消息内部会生成一个唯一id(deliverTag 64位),很容易解决,建立去重表,做幂等处理
重复消费
扩容
监控报警
增加消费者数量
优化消费者处理能力
消息积压
多个队列, 每个队列都有一个自己的消费者,将一类消息投递到一个队列中消费者维护内存队列,同一类消息hash到一个内存队列中
顺序消息
常见场景
Java 原生API
①默认提供了消息和队列持久化机制
②工作线程默认的最大接受消息数量为250,即公平调度
③自动ACK机制,消息消费不成功的处理策略也默认是重新入队
相较于原生的特点
starter-amqp
依赖
交换机名称、队列名称、绑定关系
基础配置
@RabbitMQTemplate
发送
@RabbitMQListener
接收
SpringBoot
Spring AMQP
使用方式
ES 是一个开源的高扩展的分布式全文搜索引擎
field名
可分词,支持模糊查询,支持准确查询,不支持聚合查询
text
不可分词,数据会作为完整字段进行匹配,支持模糊查询,支持准确查询,支持聚合查询。
keyword
基本数据类型:long、integer、short、byte、double、float、half_float
浮点数的高精度类型:scaled_float
Numerical
Date
Array
是否索引,默认为 true
是否将数据进行独立存储,默认为 false
store
映射(类似于MySQL中的字段约束)
index索引:可以理解为mysql的数据库
document文档:理解为mysql里的记录,JSON格式
fields属性:理解为mysql里的字段
操作索引
操作文档
Restful
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
maven依赖
Spring Data集成
spark stream集成
flink集成
集成
document_id=>当前文档包含的所有词项
在数据库管理系统(DBMS)中,主键索引就是一个正排索引的例子
正排索引的优势在于可以快速地查找某个文档里包含哪些词项。同理,正排不适用于查找包含某个词项的文档有哪些。
正排索引
词项=>包含当前词项的doc_id的列表
倒排索引的优势是可以快速查找包含某个词项的文档有哪些。如果用倒排来确定哪些文档中是否包含某个词项就很鸡肋。
倒排索引
ElasticSearch
Linux
Docker VS 虚拟机
百度清华镜像源
安装
去阿里云ACR看去
代理
镜像
容器
网络
将服务器上的目录与容器内目录关联,会覆盖容器目录内容,启动时,服务器上的目录要准备好对应的文件
卷挂载
将容器内部的某个目录和某一个volume映射,会把容器里的文件内容同步出来
卷映射
卷
docker info
docker ps
docker stats
docker images
docker logs
正常情况下别这样玩,提交个黑盒子出来谁也不知道你做了什么,写Dockerfile build出来!
docker commit
docker pull
docker push
docker volume
docker rmi
dockr rm
docker exec
docker stop
docker run
docker tag
docker build
docker start
docker restart
docker login
docker history
docker help
网上找个模板照着改就完事了
Dockerfile
docker compose up -d -f xxx.yaml
docker compose down
docker compose start containid
docker compose stop containid
docker compose scale
DockerCompose
自定义
桥接默认
bridg
和宿主机的端口一一对应
host
不使用网络功能
none
指定新容器和某一个存在的容器共享网络
container
网络模式
隔离文件:chroot
隔离访问:namespaces
隔离资源:cgroups
封装系统:LXC
Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。这叫叫作联合文件系统(Union File System)
一个“容器”,实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境。
Docker实现原理
docker logout
docker df
通用操作
docker status
docker exec -it xxx /bin/bash
docker rm
docker search
docker network
挂载
Docker
为用户提供一个具有普遍意义的容器编排工具
Kubernetes解决了什么?
基于 RAFT 的一致性,节点数必须为奇数
保存了整个集群的状态,理解成后端数据库得了
etcd
提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制
apiserver
负责维护集群的状态,比如故障检测、自动扩展、滚动更新等
controller manager
负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上
scheduler
负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理
kubelet
负责镜像管理以及 Pod 和容器的真正运行(CRI)
Container runtime
负责为 Service 提供 cluster 内部的服务发现和负载均衡
kube-proxy
kube-dns 负责为整个集群提供 DNS 服务
Ingress Controller 为服务提供外网入口
Heapster 提供资源监控
Dashboard 提供 GUI
Federation 提供跨可用区的集群
Fluentd-elasticsearch 提供集群日志采集、存储与查询
其他
Container
共享Network Namespace
共享Volume
Pod 最重要的一个事实是:它只是一个逻辑概念。Pod,其实是一组共享了某些资源的容器。
Pending
Running
Succeeded
Failed
Unknown
Pod
Node
Namespace
my-svc.my-namespace.svc.cluster.local-》serviceip-》podip
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的
一直以来,基于 iptables 的 Service 实现,都是制约 Kubernetes 项目承载更多量级的 Pod 的主要障碍。而 IPVS 模式的 Service,就是解决这个问题的一个行之有效的方法
集群的内部 IP 公开 Service,选择该值时 Service 只能够在集群内部访问
ClusterIP(默认)
通过每个节点上的 IP 和静态端口(NodePort)公开 Service。
NodePort
使用云平台的负载均衡器向外部公开 Service。
LoadBalancer
将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example)。 该映射将集群的 DNS 服务器配置为返回具有该外部主机名值的 CNAME 记录。 集群不会为之创建任何类型代理。
ExternalName
不会获得集群 IP,kube-proxy 不会处理这类 Service, 而且平台也不会为它们提供负载均衡或路由支持。
Headless
Service
Label
Annotations
Deployment 实际上是一个两层控制器。首先,它通过ReplicaSet 的个数来描述应用的版本;然后,它再通过ReplicaSet 的属性(比如 replicas 的值),来保证 Pod 的副本数量。
Deployment 控制 ReplicaSet(版本),ReplicaSet 控制 Pod(副本数)。这个两层控制关系一定要牢记。
Deployment
Job
CronJob
DaemonSet
StatefulSet
ConfigMap
Ingress
基本概念
YAML文件详解
在默认情况下,被限制在 Network Namespace 里的容器进程,实际上是通过 Veth Pair 设备 + 宿主机网桥的方式,实现了跟同其他容器的数据交换。
Flannel,VXLAN 模式
Calico
容器“跨主通信”的问题
容器网络
保底分配的资源,调度是根据这个值
requests
最大资源使用上限
limits
资源请求和限制
Kubernetes
设计模式
计算机系统概论
运算方法和运算器
存储系统
指令系统
中央处理器
总线系统
外围设备
输入/输出设备
并行组织和结构
计算机组成原理
广域网WAN
城域网MAN
局域网LAN
个人区域网PAN
范围
公用网
专用网
使用者
速率
吞吐量
带宽
时延
时延带宽积
往返时间RTT
利用率
计算机网络性能指标
应用层
表示层
会话层
运输层
网络层
数据链路层
物理层
OSI七层协议
网际层
网络接口层
TCP/IP四层协议
DNS
HTTP
HTTPS
SMTP
举例协议
通过应用进程之间的交互来完成特定网络应用。应用层协议定义的是应用进程间通信和交互的规则。
面向连接的、可靠的数据传输服务
TCP
提供无连接的、尽最大努力的数据传输服务
UDP
主要协议
负责向两台主机中进程之间的通信提供通用的数据传输服务。
IP
负责为分组交换网上的不同主机提供通信服务
数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧
尽可能的屏蔽掉各种计算机的传输媒体和通信手段的差异
五层协议
分层
作用:屏蔽传输媒体和通信手段的差异
机械特性
电气特性
功能特性
过程特性
特性
连续信号
模拟信号
离散信号
数字信号
信号分类
单工通信
单向通信
半双工通信
双向交替通信
全双工通信
双向同时通信
信道
不归零制
归零制
曼彻斯特编码
差分曼彻斯特编码
编码
调幅
调频
调相
带通调制
双绞线
同轴电缆
光缆
传输媒体
数据单元:帧
封装成帧
透明传输
循环冗余检验CRC
差错检测
点对点协议PPP
点对点信道
CSMA/CD协议
广播信道
网络层只提供简单灵活的、无连接的、尽最大努力交付的数据报服务
版本4位
首部长度4位
区分服务8位
总长度16位
标识16位
标志3位
片位移13位
生存时间8位
协议8位
首部校验和16位
源地址32位
目的地址32位
首部(固定20字节)
数据
格式
IP地址32位
IP地址=网络号+主机号
网络号8位主机号24位 0——
A类
网络号16位主机号16位 10——
B类
网络号24位主机号8位 110——
C类
1110——多播地址
D类
1111——保留使用
E类
分类IP地址
IP地址=网络号+子网号+主机号,从网络的主机号借用若干位作为子网号
子网划分
超网
三个阶段
网际协议IP
根据IP地址解析出相应的硬件地址
在主机ARP高速缓存中存放一个从IP地址到硬件地址的映射表,并且这个表经常动态更新
首先在ARP高速缓存中查找,如果查不到,就在局域网上广播发送一个ARP请求分组
本局域网上所有主机上运行的ARP进程都收到此分组请求
目标主机收下ARP请求分组,并向主机发送ARP响应分组
收到响应分组后就在ARP告诉缓存中写入更新
tip:ARP协议解决的是同一个局域网中的问题
过程
地址解析协议ARP
ICMP差错报告报文
ICMP询问报文
网际控制报文协议ICMP
网际组管理协议IGMP
内部网关协议IGP
外部网关协议EGP
内部网关协议RIP
内部网关协议OSPF
外部网关协议BGP
路由选择协议
使用TCP之前必须建立连接,传输完之后必须释放连接
面向连接
每条TCP连接只能一对一
点对点交互通信
无差错、不丢失、不重复且按序到达
提供可靠交付
双方应用程序在任何时候都能发送数据
TCP不知道所传送的字节流的含义
面向字节流
TCP连接的端口,由IP地址:端口号定义
Socket是什么
源端口和目的端口
表示本报文中所发送的数据的第一个字节的序号
序号
期望收到对方下一个报文段的第一个数据字节的序号
若确认号=N,表面到序号N-1位置的所有数据都已正确收到
确认号
数据偏移
保留
紧急URG
当ACK=1时,确认号字段才有效,当ACK=0时,确认号无效。TCP规定,在建立连接后所有传送的报文段都必须把ACK置1
确认ACK
推送PSH
复位RST
同步SYN
FIN=1时,表明该报文段的发送方数据发送完毕,要求释放运输连接
终止FIN
窗口
检验和
紧急指针
选项
TCP报文段首部格式
以字节为单位的滑动窗口
超时重传机制
选择确认SACK
TCP如何实现可靠传输
对资源的需求超过了资源所能提供的可用部分
拥塞定义
防止过多的数据注入到网络中,防止路由或链路过载
拥塞控制定义
PS:拥塞控制是全局性的过程,而流量控制属于端对端的
总之就是一直乘以2
由小到大逐渐增大拥塞窗口数值
慢开始
每一个往返时间RTT,窗口+1
到达慢开始门限就开始线性增长
拥塞避免
超时了门限会变成当前窗口的一半,窗口会重新从1开始
当发送方连续收到3个队同一报文段的确认ACK,说明发生了丢失问题。可以立刻进行重传,避免超时。
快重传
慢开始门限变为当前窗口的一半,拥塞窗口变成当前门限,然后开始线性增加
快恢复
TCP拥塞控制方法
拥塞控制
1、客户端发送请求连接报文,其中首部同步位SYN=1,同时选择一个序号seq=x。TCP规定,SYN报文段=1的不能携带数据,但要消耗掉一个序号。此时客户端进行SYN-SENT状态
2、服务机接收到报文段,若同意连接,发送确认报文段。其中SYN、ACK都为1,并且确认号ack=x+1,同时也为自己选择一个属实序号seq=y。此时服务机进入SYN-RCVD状态
3、客户端接受到确认信号,向主机发送确认。其中ACK=1、确认号ack=y+1,而自己的序号seq=x+1。TCP规定,确认报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+1。此时连接已经建立,客户端进入ESTABLISHEND状态,主机收到后也进入该状态。
防止已失效的连接请求报文段突然传送到服务机,产生错误,导致资源浪费。
A发出请求报文没有丢失,但是滞留了,在连接释放后的某个时间间才到达B。B收到这个就认为A重新发起了连接请求,于是就发送给A发送确认,建立连接。假定没有第三次握手,在B发出这个确认报文后,连接就建立了,但是A实际并没有发起请求,所以不会理会B的确认,这就导致B的连接一直无意义的建立。
举例
问题:为什么要三次握手?两次可以吗?
TCP三次握手建立连接
1、进程A向进程B发送连接释放报文段,其中首部终止控制位FIN=1,序号seq=u,然后进入FIN-WAIT-1状态
2、进程B收到连接释放报文后发出确认报文,其中确认号ack=u+1,序号seq=v,然后B进入CLOSE-WAIT状态,A收到确认号进入FIN-WAIT-2状态,此时连接进入半关闭状态
3、若B没有要发送的数据时,B向A发出连接释放报文段,其中FIN=1,确认号ack=u+1,然后B进入LAST-ACK状态
4、A收到连接释放报文后,发出确认,其中ACK=1,然后进入TIME-WAIT状态,此时连接并没有释放掉,需要等待2MSL后才释放。
1、在第四次挥手
2、2MSL, 四分钟
3、保证最后一个ACK报文能够到达
4、防止已经失效的连接请求报文出现在本次连接中,导致资源浪费
问题:TIME-WAIT在哪个阶段?实际为多长?为什么?
TCP四次挥手释放连接
传输控制协议TCP
发送数据之前不用建立连接,减少了开销和时延
无连接
不保证可靠交付
尽最大努力交付
应用层交给UDP多长的报文,UDP就照发
面向报文
无拥塞控制
支持一对一、一对多、多对多、多对一的交互通信
只有8个字节比TCP20个字节要短
首部开销小
源端口
目的端口
长度
校验和
首部格式(注意没个字段都是两个字节)
用户数据报协议UDP
FTP
TELNET
URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL
统一资源标识符
URI
协议://主机:端口:/路径
用于标志or定位在整个互联网范围内的某一资源
统一资源定位符
URL
虽然建立了TCP连接,但是双方在交换HTTP报文之前不需要建立HTTP连接
无连接的
服务器并不记得曾经访问过的客户,也不记得为客户曾经服务过多少次。
无状态的
每请求一个资源都要有两边RTT时间开销
短连接
每一次建立新的TCP连接都要分配缓存和变量
HTTP1.0
使用了持续连接(长连接),服务器在发送响应后扔在一段时间内保持这条连接
相较HTTP1.0
客户在收到响应前就能够接着发送新的请求,效率高
流水线式
客户在收到前一个响应后才能发出下一个请求,效率不好,浪费服务器资源
非流水线式
持续连接的工作方式
HTTP1.1
二进制分帧
首部压缩
多路复用
请求优先级
服务器推送
总之HTTP2.0肯定比之前的好
HTTP2.0
使用明文进行通信,内容可能会被窃听
不验证通信方的身份,通信方的身份有可能遭遇伪装
无法证明报文的完整性,报文有可能遭篡改
HTTP存在的安全问题
HTTPS=HTTP+加密+认证+完整性保护
解决安全问题引入的HTTPS,HTTPS不是新协议,只是对HTTP进行加密
对称性加密
非对称性加密
PS:HTTPS使用混合加密的方式,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率
加密方式
1、客户端发起HTTPS连接
2、服务器发送公钥和证书到客户端
验证证书
生成随机数
用公钥对随机数加密
3、客户端验证服务器发送的证书
4、服务器接受加密的随机数,用私钥进行解密,随后用这个随机数当做私钥对需要发送的数据进行对称加密
5、客户端在接受到加密后的数据使用私钥对数据进行解密并且解析数据呈现结果给用户
6、连接建立
HTTPS连接过程
要钱
加密解密需要时间开销
HTTPS的缺点
包含请求的方法、URL、版本
请求行
说明浏览器、服务器或报文主体的一些信息
首部行
请求的实体数据
请求体
请求报文
版本、状态码、短语
状态行
响应的实体数据
响应体
响应报文
HTTP报文格式
get
put
post
trace
connect
head
option
GET 用于获取资源,而 POST 用于传输实体主体
作用
GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看
因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码
参数
GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。
安全
幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)
在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是
幂等性
请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的
拓展:get和post的比较
请求方法
表示通知信息
1XX
200 ok
204 请求已经成功处理,但是返回的响应报文不包含实体的主体部分
206 表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容
表示成功
2XX
301 永久重定向
302 临时重定向
303 和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源
304 条件不满足
表示重定向
3XX
400 报文语法错误
401 认证失败
403 拒绝
404 懂的都懂
表示客户端错误
4XX
500 一般情况下 都是代码出现了问题
503 服务器暂时处于超负载或正在进行停机维护,现在无法处理请求
表示服务器错误
5XX
拓展:转发和重定向的区别
状态码
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息
为什么要有cookie
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)
cookie是什么
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
浏览器行为跟踪(如跟踪分析用户行为等)
个性化设置(如用户自定义设置、主题等)
用途
浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效
会话期cookie
指定一个特定的过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie
持久型cookie
cookie
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全
session
cookie存储客户端,session存储在服务器
1、存储位置
cookie以文件的形式存储,session以类似HashMap的结构存储在服务器
2、存储方式
Cookie 只能存储 ASCII 码字符串,而 Session 则可以存取任何类型的数据
cookie存储上线为4kb,session没有限制
3、存储容量
cookie不安全
session相对安全(对网络抓包可以抓到)
4、安全性
cookie存储在客户端,不会影响服务器性能
session存储在服务器,会影响服务器性能
5、性能
比较
因此在考虑数据复杂性时首选 Session
Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密
选择
cookie和session的比较和选择
WWW
1、DNS域名解析
建立TCP连接
发生HTTP请求
服务器处理请求
服务器返回HTTP响应
关闭TCP连接
2、请求和响应数据
解析
渲染
3、浏览器加载渲染
其他问题:从网页上数据一个URL到产生渲染效果,其中发生了什么
自下而上
计算机网络
数据结构与算法
操作系统是配置在计算机上的第一层软件,是对硬件系统的首次扩充
操作系统的定义
方便性、有效性、可扩充性、开放性
目标
命令方式
系统调用方式
图标-窗口方式
1、作为用户与计算机硬件系统之间的接口,通信方式:
2、计算机系统资源的管理者
3、实现了对计算机资源的抽象
操作系统的目标和作用
并行是指两个或多个事件在同一时刻发生。
并发是值两个或两个事件在同一时间间隔内发生
并行与并发
在多道程序环境下,并发是指在一段时间内宏观上有多个程序在同时运行,但在单机处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行
并发
互斥共享方式
同时访问方式
共享
时分复用技术
空分复用技术
虚拟
操作系统的基本特性
进程控制
进程同步
进程通信
调度
处理机管理
内存分配
内存保护
地址映射
内存扩充
存储器管理
缓冲管理
设备分配
设备处理
设备管理
文件存储空间管理
目录管理
读写管理和保护
文件管理
用户接口
程序接口
操作系统与用户之间的接口
操作系统的主要功能
操作系统引论
间断性
失去封闭性
运行结果不可再现性
程序并发执行的特征
在多道程序环境下,程序的执行属于并发执行,此时它们将失去其封闭性,并具有间断性,以及其运行结果不可再现性的特征。由此决定的通常的程序是不能参与并发执行的,否则,程序的运行也就失去了意义。为了能使程序并发执行,并且可以对并发执行的程序加以描述和控制,引入了进程的概念。
进程定义的引入
PCB是进程存在系统中的唯一标志
程序段+相关数据段+PCB
进程的结构
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
进程的标准定义
由创建而生,由调度而执行,有撤销而消亡
动态性
多个进程实体同时存在于内存中,且能在同一时间段内同时运行
并发性
进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位
独立性
异步性
进程的特征
创建
就绪
执行
阻塞
终止
进程的状态
进程的描述
原语:若干条指令组成,用于完成一定的功能的过程,它们是原子操作,要麽全部完成,要麽全部不完成
进程控制一般都是由OS内核中的原语来实现的
用户态
内核台
状态划分
中断处理
时钟管理
原语操作
支撑功能
进程管理
资源管理
内核功能
操作系统内核
进程的控制
对多个相关进程在执行次序上进行协调,使并发执行的程序之间按照一定的规则共享系统资源,使程序的执行具有可再现性
进程同步的基本概念
临界区资源没被使用,可以进程使用
空闲让进
临界区资源被使用,其他进程必须等待
忙则等待
保证等待的进程在有限的时间能获得资源的使用权,防止死等状态
有限等待
当进程获得不了资源使用权时,应该立即释放处理机,避免进程陷入忙等状态
让权等待
同步机制遵循的规则
关闭中断
Test and Set指令
Swap指令
硬件同步机制
P、V操作
整型信号量
记录型信号量
AND型信号量
信号量集
信号量机制
信号量机制需要自备同步操作,难以管理,容易引发死锁问题
为什么管程出现?
一个管程定义了一个数据结构和能为并发进程锁执行的一组操作,这组操作能同步进程和改变管程中的数据
管程的定义
管程的名称
局部于管程的共享数据结构说明
对该数据结构进行操作的一组过程
对局部于管程的数据设置初始值的语句
管程的组成
就是一个数据结构,将表征共享资源的数据结构及对其数据结构操作的一组过程,,包括同步机制,都封装起来了
进一步理解
管程机制
生产者消费者问题
哲学家就餐问题
经典进程同步问题
共享数据结构
共享存储区
共享存储器系统
管道通信系统
直接通信
间接通信
消息传递系统
Socket
客户机-服务器系统
进程通信类型
如果说引入进程的目的是为了使程序能够并发执行,以提高资源利用率和系统吞吐量,那么,在操作系统中再引入线程,则是为了减少程序在并发执行时所付出的时空开销,使OS具有更好的并发性
线程的引入
比进程更小的基本单位,用以提高程序并发执行的程度,以进一步改善系统的服务质量
线程的基本概念
tip:一个误区:传统OS中,进程是作为独立调度和分派的基本单位,因而进程是能独立运行的基本单位。但是在引入线程的OS中,已把线程作为调度和分派的基本单位,因而线程是能独立运行的基本单位
引入线程的OS中,线程作为调度单位
引入线程的OS中,进程作为资源分配单位
资源
线程之间并发
进程之间并发
系统开销
进程和线程直接的比较角度
TCB
程序计数器
堆栈
寄存器
线程的数据
内核支持线程
用户级线程
多对弈
组合方式
线程的实现方式
互斥锁mutex
信号量
条件变量
线程之间的同步机制
共享内存
消息传递
由于多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度),线程间通信的目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制
线程之间的通信方式(参考juc)
线程
进程的描述和控制
抢占式
非抢占式
轮转调度算法
优先级调度算法
多队列调度算法
多级反馈队列调度算法
基于公平原则的调度算法
算法:
进程调度方式
先来先服务
短作业优先
高响应比优先调度算法
作业调度算法
死锁的起因,通常是源于多个进程对资源的争夺,不仅对不可抢占资源进行争夺时会引起死锁,而且对可消耗资源进行争夺时,也会引起死锁
如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的
死锁的定义
一段时间内,某资源只能被一个进程占用
互斥
获得资源的进程可以发起新的资源请求,若新的资源被占用,进程被阻塞,但是原本占有的资源不会释放
请求和保持
进程已经获得的资源在未使用完成之前不能被其他进程抢占,只能自己释放
不可抢占
发生死锁时,必然有个进程——资源的循环链
循环等待
死锁产生的条件
破坏请求和保持
破坏不可抢占
破坏循环等待
预防
系统安全状态
银行家算法
避免
资源分配图
检测
抢占资源
终止进程
解除
处理死锁的方法
死锁
处理机调度与死锁
虚拟内存
输入输出系统
操作系统
408
常考面试题梳理
0 条评论
回复 删除
下一页