java 核心
2023-10-20 12:18:40 2 举报
AI智能生成
登录查看完整内容
学习整理
作者其他创作
大纲/内容
InnoDb
表级别锁,插入与查询速度较快,更新速度慢,不支持事务。一般适合做报表类
MyIsam
数据存放内存,读写速度快,数据库重启崩溃会丢失数据,适合做临时表
Memory
数据存放CSV文件,不支持索引,适用于导入数据之类场景
CSV
存储引擎
左小右大
优点:结构简单 缺点:树深度不够平衡
二叉树
子树深度差值不能超过1
优点:解决二叉树问题 缺点:数据量大树太深,IO次数过多,innerDB加载page 16KB,空间浪费问题
平衡树
节点拥有子树数量叫度
页分裂/合并 保持树的平衡
频繁修改索引值,无序的值会导致页频繁分裂,合并
B 树
B树加强版
只有在叶子节点上存储数据
叶子节点之间是双向指针
B+树(innoDb)
数据结构(排序)
innoDb不能使用哈希索引,特殊引擎可以使用Memory
通过hashCode映射查找数据,效率快,但存在hashCode碰撞问题
无序索引,比较等操作都不支持
Hash索引
BTree索引
索引类型
通过*.myi文件找到地址,在*.MYD查找到相应的数据
*.myi(索引文件)
*.myd(数据文件)
myisam
叶子节点存储的数据(聚集索引)
叶子节点存储的是主键索引值(非聚集索引)
*.idb 数据跟索引
innodb
索引存储
2.个数不宜过多
3.离散度低的字段不宜建立索引 如性别
4.经常需要更新的字段
5.无序的值不宜建立索引
6.复合索引 离散度高的建立在前
7.过长的字段,使用前缀索引(字段长度截断)
使用原则
索引
作用:主从复制,数据恢复
提交事务时候追加写
优点:日志量少,IO少,性能好
缺点:不同版本SQL语句不兼容问题,master与slave执行相同函数不同结果问题
Statement(记录SQL语句)
优点:没有兼容性问题,无BUG
缺点:日志量大,性能差
Row(记录修改前后值)
Mixed(Statement与Row的混合)
模式
作用:服务异常崩溃,数据恢复
redolog效率高,顺序IO,安装目录lb_logfile
大小固定,默认48M,追加写,写满触发刷脏
redolog
作用:事务日志,回滚事务,保证原子性
undolog
内存缓冲区 提升读写性能 inner_buffer_pool
热数据区
冷数据区(新访问数据进入)
内存淘汰LRU算法
1.缓冲操作到change_buffer
2.数据在buffer_pool中直接更新
执行流程
change_buffer(非唯一性索引数据操作)
0 事务提交后 每1秒将数据刷到磁盘
1 事务提交立即刷新磁盘
2 事务提交 写入操作os系统缓冲区,os操作系统每隔1s刷到磁盘
innodb_flush_log_at_trx_commit参数
log_buffer(redolog缓冲区)
buffer pool
1.从buffpool查找数据,未查到从磁盘查找,加载到server执行器
2.更新Server执行器数据
3.记录undolog 更新前原数据
4.记录redolog 更新后数据
5. 更新buffpool 原数据更改成更新后数据
6.Server 层记录binlog
7.提交事务 , 刷脏
更新
执行顺序
日志
原子性A
隔离性I
持久性D
一致性C
特性
innoDb快照读
脏读(未提交读)
innoDb加行锁
幻读(已提交读,add)
不可重复读(已提交读,update/delete)
MVVC
脏读问题
幻读问题
不可重复读问题
RU(未提交读)
高并发推荐,不加行锁
RC(提交读)
幻读问题(innodb通过行锁解决幻读问题)
系统默认,并发小,强一致性推荐
RR(可重复读)
解决任何问题,效率差
SZ(串行化)
如何选择隔离级别
隔离级别
事务
=查询,命中记录
记录锁
=查询,未命中记录
间隙锁
临间锁
1.互斥
2.不可剥夺
3.持有并等待
发生条件
1.顺序访问
2.数据排序
3.申请足够级别的锁
4.避免没有where条件更新
5.事务拆分,大事务拆小
6.少用范围查询
如何解决
死锁
锁(锁定主键)
先大后小(3-2-1)
从上往下
id(查询序号)
简单查询
SIMPLE
包含子查询的主查询
PRIMARY
子查询
SUBQUERY
临时表
DERIVED
联合查询
UNION
select_type(查询类型)
table(查询的表)
partitions(分区)
主键索引/唯一索引查询
const
系统表查询
system
关联查询 匹配主键索引/唯一索引
eq_ref
匹配索引 最左匹配原则
ref
匹配索引 范围查询
range
索引全部扫描 返回所有索引
index
全表扫描
All
type
possible_keys(可能用到的索引)
key(实际用到的索引)
key_len(索引长度)
ref(索引比较的列)
rows(预估扫描行数)
filter(按表条件刷选行的百分比)
使用覆盖索引
using index
存储引擎拿到多余数据 需要过滤
using where
索引不能排序 需要额外排序
using filesort
索引使用临时表
using temporary
extra
执行计划(Explain)
是否启用慢sql查询日志 默认0 0-否 1-是
slow_query_log
指定慢sql日志位置以及文件名称
slow_query_log_file
慢sql执行阈值 默认10 单位s
long_query_time
慢sql 输出目标 默认是file 即输出到文件
log_ouput
输出文件显示时区 默认UTC 建议更改成SYSTEM
log_timestamps
是否记录未使用索引查询的语句 默认未off
log_queries_not_using_indexes
开启慢SQL日志(/etc/my.cnf)
mysqldumpslow
MySQL Enterprise Monitor
分析慢sql
慢sql
show VARIABLES like '%max_connection%'
服务端最大连接数
show variables like '%max_user_connections%';
每一个用户的最大连接数
show full processlist
数据库链接情况
当前连接中各个用户的连接数
当前连接中各个IP的连接数
当前连接中连接时间最长的的连接
show global variables like 'wait_timeout';
查询链接超时时间
性能监控
问题排查
mysql
缓存
分布式session
分布式锁setnx
分布式ID
限流Spring Cloud
应用场景
设置单个/多个Key的值
set/mset
获取单个/多个key的值
get/mget
获取单个Key对应值长度
strlen
在key的值末位添加Value
append
从start-end截取字符串
getrange
递增相应的值
incr/incrby/incrbyfloat
递减相应的值
decr/decrby
操作命令
String(KV结构)
一个key对应多个键值对
1.节省空间 2.减少key冲突 3.减少资源消耗
优点
1.无法单独设置TTL time 2.分片存储不适合
缺点
设置单个/多个哈希值
hset/hmset
获取单个/多个哈希值
hget/hmget
获取所有的key
hkeys
获取所有的value
hvals
获取所有键值对
hgetall
判断某个field是否存在
hexists
对某个字段递增
hincrby/hincrbyfloat
获取元素个数
hlen
删除某个元素
hdel
Hash
有序集合 从左到右
向左添加元素 添加 a b 生成b-a
lpush
向右添加元素 添加 a b 生成 a-b
rpush
向左取出元素 如 a-b 取出 a-b
lpop
向右取出元素如 a-b 取出 b-a
rpop
从index处取出元素
lindex
从start-end取出元素
lrange
List
存储的String的无序类型的集合 集合成员唯一
集合添加数据
sadd
获取集合数据
smembers
获取集合成员个数
scard
随机返回N条成员
srandmember
移除并返回集合中一个随机元素
spop
移除集合中元素
srem
判断元素是否存在集合中
sismember
返回一个集合不存在另外一个集合的元素
sdiff
返回相同元素 交集
sinter
返回不同元素 去重
sunion
Set
有序集合
添加多个元素 score 越小越靠前
zadd
zcard
获取score 在min与max之间成员个数
zcount
对key的field增加score分数
zincrby
列举索引在min与max之间所有字段
zrange
列举区间内的成员。的位置按分数值递减(从大到小)来排列
zrevrange
列举出score 为min与max之间所有字段
zrangebyscore
移除一个元素
zrem
获取一个元素下标索引位置
zrank
获取一个元素分值
zscore
ZSet
数据类型
优点:实时性高,清理内存
定时过期(redis 不采用)
平衡了系统cpu与内存问题
redis 默认是每隔一段时间就随机抽取一些设置了ttl的 key,检查其是否过期
定期过期
优点:不会消耗cpu
缺点:当大量key过期了一直没有被访问,就会占用大量内存
获取 key 的时候,如果此时 key 已经过期,就删除
惰性过期
过期策略
maxmemory 2GB设置最大内存
maxmemory-policy allkeys-lru 设置内存淘汰策略
操作配置
随机删除
random
每个key 都带有lru属性字段,包含访问时间
get key时候更新lru属性字段的访问时间
实现原理
最近最少使用(根据访问时间 推荐)
lru
Lru属性字段增加counter字段 计算字段访问频率
1. 首先根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出去
2. 如果两个数据的访问次数相同,再比较两个数据的访问时效性,把距离上一次访问时间更久的数据淘汰出去
最不经常使用(根据访问次数)
lfu
优先淘汰更早过期的键值
ttl
内存淘汰策略
内存淘汰机制
内存淘汰
900s内有1个Key 发生了变更触发备份
save 900 1
生成的rdb文件
dbfilename dump.rdb
压缩rdb文件 节省空间 但是消耗cpu
rdbcompression yes
验证rdb文件完整性
rdbchecksum yes
内容紧凑 占用空间小
不影响主进程
恢复快
可能会丢失数据,可靠性低
优缺点
rdb(默认 记录视图)
开启AOF
appendonly yes
aof 文件名称
appendfilename “appendonly.aof”
每次同步 效率低 安全性高
always
一秒一次 性能均衡
everysec
不同步 安全性能差
no
appendfsync (内存同步磁盘)
重写aof 到达压缩文件大小目的,剔除重复的指令。
bgrewriteaof
aof文件重写最小的文件大小
auto-aof-rewrite-min-size
aof文件增长比例
auto-aof-rewrite-percentage
同步频率快
可靠性高
文件大 占用空间大
性能低
aof(记录命令)
持久化
1.修改从库redis.conf slaveof masterIp masterPort/masterauth password
2.重启从redis 生效。
配置流程
首次同步数据
场景
1.slave发送同步命令到master 并且与master建立长连接
3.生成rdb后 master 开始缓冲写命令
4.master发送rdb到slave
5.slave 清空脏数据,加载rdb 数据
6.master 将缓冲命令repl buffer发送给slave
7.slave接收repl buffer写命令,执行命令
8.master与slave 建立长连接,持续发送写命令
流程
全量复制
主从断开链接,期间有数据写入master
1.从节点断开master缓冲写命令以及数据
3.比较master与slave的offset偏移量,如果在master中 则将offset之后数据同步给master 否则全量同步
增量复制
原理解析
slave-read-only=yes
保证从节点只读
主节点产生的命令数据无论大小都会及时地发送给从节点,主从之间延迟变小
增加了网络带宽的消耗 适用于同机房
repl-disable-tcp-nodelay 关闭
主节点会合并较小的TCP数据包从而节省带宽,主从延迟会变大
节省了带宽但增大主从之间的延迟
repl-disable-tcp-nodelay 开启
主从延迟问题
问题
主从复制
见备注
Raft 算法
1.断开链接的时长
2.优先级
3.offset 数据备份越多
4.进程ID最小
影响因素
选举算法
sentinal节点每隔一段时间向master发送心跳请求,如果超时down-after-milliseconds未收到回复 则sentinal认为master 主观下线
主观下线
主观下线后 sentinal会向其他sentinal发送ismaster-down-by-addr命令 如果过半sentinal认为master主观下线,则master升级为客观下线
客观下线会重新发起master选举
客观下线
哨兵
jedis 实现数据分片 一致性哈希算法 不依赖中间件,逻辑可以自定义
客户端
Codis/twemproxy
代理端
数据分片
数据不在缓存,也不在数据库 比如请求一个不存在的订单号
本身是一个很长的二进制向量,存放的不是0就是1
定义
对数值进行多次哈希取得多个哈希值,并对数组小标取模 放在不同位置上
1 代表占位 0 代表未占位
原理
优点:高效 占用内存少
缺点:具有一定误判率
1. 布隆过滤器 将所有数据缓存到redis 布隆过滤器中
解决方案
缓存穿透
同一时间段内,大量缓存失效/redis挂掉了
1. 分布式锁
2.设置不同过期时间
3.redis 高可用搭建
缓存雪崩
热点数据过期,大量并发请求同一个数据
分布式锁
缓存击穿
redis
MongoDB
数据存储
Broker->VHost->Exchange->Queue
发送消息
Connection->channel-Queue
接收消息
工作模型
Direct直连 1-1
Topic 主题 1-n
Fanout 广播 1-n
Headers 头部匹配
Exchange路由
消息过期ttl
消息超长
消费者拒绝reject/nack 并且requeue=false
死信队列
插件rabbitmq_delayed_message_exchange
延迟队列
发送消息效率低下(不建议使用)
Transaction模式
效率不高
单条Confirm
效率相对较高
批量提交数目问题,过大失败问题,过少效率低下
批量Confirm
效率高,回调机制
异步AsyncConfirm
Confirm模式
消息可靠性
不能保证高可用,数据分散分布
普通集群
高可用,性能低
镜像模式
集群高可用
rabbitMq
stream
高吞吐,低延迟
partion
高伸缩性
副本机制
持久性,可靠性
自动选举Leader
容错性,高并发
消息传递
网站活动跟踪
指标监控(实时数据分析)
日志聚合(log)
使用场景
基础介绍
手工指定ProducerRecord
自动指定 实现接口Partitioner
Leader操作数据
Follower 同步数据
partion0 分区
partion1 分区
Topic1 队列
Topic2 队列
kafka服务Broker0
kafka服务Broker1
生产者producer
效率低
单个消费者
offset 记录消费index
一个consumer 消费一个 partion
consumer:partion=1:1
存在空闲的consumer
consumer>partion
RangerAssignor 平均分配
RoundRobinAssignor 轮询分配
consumer<partion
消费者组consumer group
消费者consumer
原理:2PC
事务Topic:_transaction_state
事务ID:transaction_id
事务消息
架构分析
KafkaProducer
给消息增加自定义样式
拦截器ProducerInterceptor
序列化Serializer
分区器Partitioner
main线程
ProducerBatch 批量发送消息
sender线程
0 不需要服务端Ack 效率快 可靠性不高
1 leader落盘Ack 有丢消息风险
-1 所有Follower 全部落地Ack retries=0 消息重复发送问题
服务端acks
Leader 读写消息
Follower 同步消息
partition存储消息
接受消息
原理分析
kafka
Broker启动注册 定时发送心跳续约
NameServer 定时检测Borker 是否长时间未发送心跳
NameServer集群都会收到消息
服务注册
NameServer定时检测
服务剔除
NameServer存储相同数据
Cilent定时器刷新路由列表
路由发现
一致性
NameServer(服务中心)
0 2 4 6 8
Borker0
1 3 5 7 9
Borker1
存储部分数据 分片存储
Broker(主机)
slave 不参与读写
slaveReadEnable=true 可以让slave 参与读负载
Replica(副本)
存储并非按照Topic分目录
autoCreateTopicEnable=true Topic不存在自动创建
Topic(消息队列)
消息队列名称
Topic
主机名称
BrokerName
队列ID位置
queueId
0->对应queueId
3
BrokerA
1
2
BrokerB
队列多少个数据存储目录 默认4
写队列数量
代表从多少数据目录读取数据 如为 2,代表消费0 1目录消息 2 3消息不会被消费
读队列数量
MessageQueue(队列)
消息队列ID
topic
过滤消息
tags
索引键 多个空格隔开
key
Message
单条发送MessageQueue
批量发送Collection<MessageQueue>
必须ACK 返回sendResult
同步发送消息
onSuccess回调接受结果
异步发送消息
不需要Ack
单向发送消息
单向顺序消息
队列选择
Producer(生产者)
轮询消费
集群模式
重复消费所有消息
广播模式
消费模式(MessageModel)
Consumer(消费者)
对磁盘访问,完全的顺序写
顺序IO 提高写入性能
消息的offset
offset
消息大小size
size
tag的hashCode
hashcode
1.Broker 的ReputMessageService 定时轮询CommitLog 查找未添加ConsumeQueue的log
3.consumer消费 首先查找consumeQueue未消费的记录,根据OffSet 查找commitlog 消费消息
初始化过程
ConsumeQueue(索引)
commitLog
文件->内核缓冲区->应用程序Buffer
读取IO
应用程序Buffer->内核发送缓冲区->文件
发送IO
传统Copy
文件->Buffer(内存映射)
Buffer(内存映射)->文件
mmap
限制最大值2G
mmap 零拷贝
消息存储
CommitLog
ConsumeQueue
清理类型
MessageStoreCofig.FileReservedTime=72 小时
过期文件
MessageStoreCofig.deleteWhen=04 一天执行一次
删除策略
清理策略
自动清理过期文件
占用率>75%
按照设定规则清理文件,无论是否过期。默认会从最老的文件开始清理
占用率>85%
拒绝消息写入
占用率>90%
磁盘已满
文件清理
brokerClusterName 集群名字相同
NameServer 相同
0-matser
1-slave
brokerId (定义master)
主写入成功Ack
优点:效率高 缺点:可靠性低
异步复制
ASYNC_MASTER
主从均写入成功ACK
优点:效率低 缺点:可靠性高
同步复制(推荐)
SYNC_MASTER
brokerRole
写入内存PAGECACHE发送ACK
优点:效率高 缺点:消息丢失
异步刷盘(推荐)
ASYNC_FLUSH
每条message刷到磁盘 发送ACK
优点:效率低 缺点:消息不丢失
同步刷盘
SYNC_FLUSH
flushDiskType(刷盘类型)
配置说明
1.定时器 slave链接master 发送拉取请求 以及自己的commitLog的offset
2.master 接收请求,与master offset比较,生成要同步commitLog buffer,发送给slave
3.slave接收到master请求,写入本地commitLog
commitLog复制
定时任务处理
元数据的复制
流程解析
master->slave 不能够自动切换的问题
主从同步
Head
Body
DLedgerCommitLog
enableDlegerCommitLog=true 是否启用
dLegerGroup=brokerA 分组名称
dLegerPeers=n0-IP:PORT;n1-IP:PORT;n2-IP:PORT; 至少3节点
dLegerSelfId=n0 本节点ID
故障转移(DLedger)
通过Hash选择MessageQueue
SelectMessageQueueByHash(默认)
随机选择MessageQueue
SelectMessageQueueByRandom
未实现功能不可以用
SelectMessageQueueByMachineRoom
自定义选择 实现接口MessageQueueSelector
MessageQueueSelector(接口)
MessageQueue选择
SYNC:同步发送
ASYNC:异步发送
ONEWAY:单次发送 不需要服务端ACK
CommunicationMode
单线程发送
单线程消费
前提条件
指定唯一队列发送
全局有序
根据业务需求指定队列
局部有序
顺序消息
消息主题Topic:RMQ_SYS_TRANS_HALF_TOPIC
producer 设置Listener setTransactionListener
1.发送未确认消息给Broker 不可见
2.执行本地事务
3.发送Ack消息给Broker 消息可见
half Message
1.定时器 轮询未Ack的half Message,主动发送命令给producer
2.Producer检查本地事务 执行Commit/rollBack操作
事务状态回查
设置TRAN_MSG=true
写入CommitLog
发送half消息
获取事务消息commitLog
获取原Topic与却u额ID,重写组装CommitLog
写入Topic=**RMQ_SYS_TRANS_OP_HALF_TOPIC** 队列(打删除标记 op队列)
half消息打上删除标记
commit 事务
写入Topic=**RMQ_SYS_TRANS_OP_HALF_TOPIC** 队列(打删除标记 op队列)
rollBack 事务
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
api 实现 msg.setDelayTimeLevel=0/1/2
不支持自定义时间 商业版本支持
1.获取commitLog 属性DelayLevel判断是否延迟消息
2.延迟消息重写Topic=SCHEDULE_TOPIC_XXXX,queueId 保存原有的Topic与queueId到Properties中
3.定时器扫描 获取要触发的commitLog 改写Topic与queue写入原有队列
写入Topic=**RMQ_SYS_TRANS_OP_HALF_TOPIC** 队列(打删除标记 op队列)
4.删除延迟队列commitLog(打标)
延迟消息
producer(生产者)
所有消费者消费所有消息
平均分配算法
AllocateMessageQueueAveragely(默认)
轮询
AllocateMessageQueueAveragelyByCircle
机房分配算法
AllocateMessageQueueByMachineRoom
一致性hash算法
AllocateMessageQueueConsistentHash
通过配置
AllocateMessageQueueByConfig
机房临近原则算法
AllocateMachineRoomNearby
rebalance
负载均衡
1.集群模式CLUSTERING
2.返回状态RECONSUME_LATER
3.非OneWay消息
consumer.setMaxReconsumeTimes=2 设置重试次数
1.获取待重试消息,重写Topic=%RETRY% + 消费者组名,重试次数+1 延迟级别+3 保存原信息到Properties
2.走延迟消息逻辑,写入延迟队列,执行消息
3.消费失败 重试次数+1,延迟级别叠加,重新再来一遍。超过最大次数写入死信队列Topic=%DLQ%+消费者组名
4.消费成功。删除commitLog(打标)
重试消息
消息会自动进行重试,达到最大重试次数后进入队列
角色分析
rocketMq
消息队列
dubbo
zk
中间件
xxlJob
Quartz
定时任务
es
搜索引擎
ID唯一获取,只能声明一个
Id
name 可以声明多个 a1:a2 a1与a2均可获取
name
IOC容器只可以存在一个Bean
组合条件查找
primary=true 优先获取
Class
获取Bean
基本使用
IOC容器顶层接口
初始化时候不会实例化对象
BeanFactory
继承了BeanFactory接口,继承了其他接口 ,丰富了功能
初始化时候实例化对象
ApplicationContext
顶层接口
注入factory类 定义factory-method
静态工厂注入
实例化工厂对象
定义bean调用工厂对象初始化factory-bean/factory-method
动态工厂注入
工厂注入
name:属性名称
value:属性值
index:构造器下标
constructor-arg(构造函数)
property(属性)
对象引用注入
数组注入
array
List列表注入
list
HashMap注入
map
构造注入
声明一个类为配置类,用于取代bean.xml配置文件注册bean对象
@Configuration
用于注册bean对象
@Bean
单例 默认
singleton
获取时候创建对象
prototype
同一个请求创建一个实例
request
同一个session创建一个实例
session
声明该bean的作用域
@Scope
配置类注入
包名
base-package
是否使用默认注解
use-default-filters
包含注解
include-filter
剔除注解
exclude-filter
component-scan(xml)
要扫描的包路径
value
Filter[] 指定排除的组件
excludeFilters
Filter[] 指定包含的组件,要设置useDefaultFilters = false
includeFilters
按照注解过滤
ANNOTATION
ASSIGNABLE_TYPE
使用ASPECTJ表达式
ASPECTJ
正则表达式
REGEX
自定义规则
CUSTOM
FilterType
@ComponentScan(默认扫描当前目前以及子目录)
泛指注解,不好归类时候用
@Component
标注业务层组件
@Service
标注控制层组件
@Controller
标注数据访问组件如Dao
@Repository
属性赋值组件
@Value
按照类型装配
@Autowired
读取配置文件赋值
@PropertySource
多实例组件
@Qualifier
多个相同Bean 首选Bean组件
@Primary
默认按照名称匹配,匹配不到按照类型匹配
@Resource
@Lazy
实现condition接口 重写match方法
实现ConfigurationCondition接口
按照一定条件判断,满足给容器注册Bean
@Conditional
通过ImportSelector导入
通过 ImportBeanDefinitionRegistrar 导入
导入外部资源,IOC容器初始化
@Import
用于初始化方法上
@PostConstruct
用于销毁方法上
@PreDestory
指定Bean 初始化以及销毁的顺序
@DependsOn
类或方法在特定的 Profile 环境生效
spring.profiles.active=prod 多环境
@Profile
注解
扫描注解
IOC
AOP
基础
初始化Environment
prepareRefresh()
解析XML/注解 生成BeanDefinitions
缓存BeanDefinitions到BeanFactory.beanDefinitionMap中
初始化BeanFactory
ConfigurableListableBeanFactory beanFactory=obtainFreshBeanFactory() (IOC beanDefintion初始化)
解析SpEL表达式,解析#{}
beanExpressionResolver
注册一些类型转换器(spring在进行值注入的时候,需要把字符串类型转换为其他类型)
propertyEditorRegistrars
去管理一些特殊的对象用来进行依赖注入。
resolvableDependencies
在Bean创建时,对Bean的功能进行一些扩展,对里面的注解进行一些识别
beanPostProcessors
prepareBeanFactory(beanFactory);
空实现,留给子类去实现。
postProcessBeanFactory(beanFactory);
beanFactory后处理器,充当beanFactory的扩展点,可以用来补充或修改BeanDefinition
ConfigurationClassPostProcessor-解析@Configuration、@Bean、@lmport、 @PropertySource
PropertySourcesPlaceHolderConfigurer -替换BeanDefinition中的${}
invokeBeanFactoryPostProcessors(beanFactory);
bean 后处理器,充当bean的扩展点,可以工作在bean的实例化、依赖注入、初始化阶段
AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标bean自动创建代理
registerBeanPostProcessors(beanFactory);
实现国际化
initMessageSource();
发布事件给 监听器
可以从容器中找名为applicationEventMulticaster的bean作为事件广播器,若没有,也会新建默认的事件广播器
可以调用ApplicationContext.publishEvent(事件对象)来发布事件
initApplicationEventMulticaster();
空实现,留给子类扩展
onRefresh();
用来接收事件
实现ApplicationListener接口,重写其中onApplicationEvent(E e)方法即可
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
ClassPathXmlApplicationContext.refresh()
AnnotationConfigApplicationContext
AbstractApplicationContext
源码解析
DI
MVC
Spring
自动装配
Environment
PropertySource
Sping Boot
基于 Netflix Ribbon 实现的客户端负载均衡和服务调用工具
作用
LoadBalancerClient
@LoadBalanced
goods-service.ribbon.NFLoadBalancerRuleClassName=**.** 设置负载均衡策略
使用方法
线性轮询策略,即按照一定的顺序依次选取服务实例
RoundRobinRule
随机选取一个服务实例
RandomRule
按照 RoundRobinRule(轮询)的策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试
RetryRule
按照平均响应时间,来计算所有服务实例的权重,响应时间越短的服务实例权重越高,被选中的概率越大
WeightedResponseTimeRule
先过滤点故障或失效的服务实例,然后再选择并发量最小的服务实例。
BestAvailableRule
先过滤掉故障或失效的服务实例,选择并发较小的节点
AvailabilityFilteringRule
默认的负载均衡策略,综合判断服务在不同区域(zone)的性能和服务(server)的可用性,来选择服务实例。
ZoneAvoidanceRule(默认)
重写Choose方法
继承AbstractLoadBalancerRule
自定义负载均衡策略
负载均衡策略
其实就是@Qualifier注解,具备它的所有特性
@LoadBalanced解析原理
1.获取所有增加了@LoadBalanced注解的RestTemplate
2.为每个RestTemplate增加LoadBalancerInterceptor拦截器
LoadBalancerAutoConfiguration
注入LoadBalancerClient
intercept(拦截请求)
LoadBalancerClient.execute(处理请求)
继承ClientHttpRequestInterceptor
LoadBalancerInterceptor
1.存储并维护服务列表
2.根据路由规则,通过servicename获取一个服务地址
3.将请求数据发送到服务地址
初始化LoadBalancerClient
RibbonAutoConfiguration
1.获取负载均衡器ILoadBalancer
2.根据负载均衡器和规则选择服务(chooseServer)
3.执行request请求并返回结果
RibbonLoadBalancerClient
定时任务 30s执行一次
更新Server列表
UpdateAction.doUpdate
PollingServerListUpdater#start(UpdateAction)
enableAndInitLearnNewServersFeature
updateListOfServers
定时任务 Ping心跳检测
BaseLoadBalancer#setupPingTask
DynamicServerListLoadBalancer.restOfInit
ZoneAwareLoadBalancer
ILoadBalancer
IRule#choose(PredicateBasedRule)
chooseServer
1.封装ServiceRequestWrapper
2.重构请求URI
3.ClientHttpRequestExecution执行Http请求
execute
可以创建一个子容器(或者说子上下文),每个子容器可以通过 Specification 定义 Bean。
NamedContextFactory
Ribbon
服务端注册
@EnableEurekaServer
消费端注册
@EnableEurekaClient
可获取当前服务的相关信息
@EnableDiscoveryClient
关键注解
服务注册中心实例的主机名
eureka.instance.hostname
注册在Eureka服务中的应用组名
eureka.instance.app-group-name
注册在的Eureka服务中的应用名称
eureka.instance.appname
该实例注册到服务中心的唯一ID
eureka.instance.instance-id
该实例的IP地址
eureka.instance.ip-address
该实例,相较于hostname是否优先使用IP
eureka.instance.prefer-ip-address
用于AWS平台自动扩展的与此实例关联的组名
eureka.instance.a-s-g-name
部署此实例的数据中心
eureka.instance.data-center-info
默认的地址解析顺序
eureka.instance.default-address-resolution-order
该实例的环境配置
eureka.instance.environment
初始化该实例,注册到服务中心的初始状态
eureka.instance.initial-status
表明是否只要此实例注册到服务中心,立马就进行通信
eureka.instance.instance-enabled-onit
eureka.instance.namespace
该服务实例的子定义元数据,可以被服务中心接受到
eureka.instance.metadata-map.test
eureka.instance.lease-expiration-duration-in-seconds
该实例给服务中心发送心跳的间隔时间,用于表明该服务实例可用
eureka.instance.lease-renewal-interval-in-seconds=30
该实例,注册服务中心,默认打开的通信数量
eureka.instance.registry.default-open-for-traffic-count
每分钟续约次数
eureka.instance.registry.expected-number-of-renews-per-min
eureka.instance.health-check-url
eureka.instance.health-check-url-path
eureka.instance.home-page-url-path
https通信端口
eureka.instance.secure-port
https通信端口是否启用
eureka.instance.secure-port-enabled
http通信端口
eureka.instance.non-secure-port
http通信端口是否启用
eureka.instance.non-secure-port-enabled
该实例的安全虚拟主机名称(https)
eureka.instance.secure-virtual-host-name
该实例的虚拟主机名称(http)
eureka.instance.virtual-host-name
eureka.instance.status-page-url
eureka.instance.status-page-url-path
instance配置项
#该客户端是否可用
eureka.client.enabled=true
#实例是否在eureka服务器上注册自己的信息以供其他服务发现,默认为true
eureka.client.register-with-eureka=false
#此客户端是否获取eureka服务器注册表上的注册信息,默认为true
eureka.client.fetch-registry=false
#是否过滤掉,非UP的实例。默认为true
eureka.client.filter-only-up-instances=true
#与Eureka注册服务中心的通信zone和url地址
eureka.client.serviceUrl.defaultZone
#client连接Eureka服务端后的空闲等待时间,默认为30 秒
eureka.client.eureka-connection-idle-timeout-seconds=30
#client连接eureka服务端的连接超时时间,默认为5秒
eureka.client.eureka-server-connect-timeout-seconds=5
#client对服务端的读超时时长
eureka.client.eureka-server-read-timeout-seconds=8
#client连接all eureka服务端的总连接数,默认200
eureka.client.eureka-server-total-connections=200
#client连接eureka服务端的单机连接数量,默认50
eureka.client.eureka-server-total-connections-per-host=50
#执行程序指数回退刷新的相关属性,是重试延迟的最大倍数值,默认为10
eureka.client.cache-refresh-executor-exponential-back-off-bound=10
#执行程序缓存刷新线程池的大小,默认为5
eureka.client.cache-refresh-executor-thread-pool-size=2
#心跳执行程序回退相关的属性,是重试延迟的最大倍数值,默认为10
eureka.client.heartbeat-executor-exponential-back-off-bound=10
eureka.client.heartbeat-executor-thread-pool-size=5
#询问Eureka服务url信息变化的频率(s),默认为300秒
eureka.client.eureka-service-url-poll-interval-seconds=300
#最初复制实例信息到eureka服务器所需的时间(s),默认为40秒
eureka.client.initial-instance-info-replication-interval-seconds=40
#间隔多长时间再次复制实例信息到eureka服务器,默认为30秒
eureka.client.instance-info-replication-interval-seconds=30
#从eureka服务器注册表中获取注册信息的时间间隔(s),默认为30秒
eureka.client.registry-fetch-interval-seconds=30
#获取实例所在的地区。默认为us-east-1
eureka.client.region=us-east-1
#实例是否使用同一zone里的eureka服务器,默认为true,理想状态下,eureka客户端与服务端是在同一zone下
eureka.client.prefer-same-zone-eureka=true
#eureka服务注册表信息里的以逗号隔开的地区名单,如果不这样返回这些地区名单,则客户端启动将会出错。默认为null
eureka.client.fetch-remote-regions-registry
#服务器是否能够重定向客户端请求到备份服务器
eureka.client.allow-redirects=false
#客户端数据接收
eureka.client.client-data-ac
#改变eureka server中注册的服务的健康检测方式
eureka.client.healthcheck.enabled=true
#获取实例所在的地区下可用性的区域列表
eureka.client.availability-zones.china
client配置项
基础使用
相互注册
高可用
在15min内超过85%的客户端节点都没有正常的心跳,Eureka进入自我保护机制
触发条件
不会剔除过期服务
可以接受新增或查询请求,但是不会同步到其他节点
网络故障恢复后,EurekaServer节点会自动退出自我保护模式
工作模式
自我保护机制(AP)
源码分析
Eureka
声明式伪RPC的Rest客户端
扫描所有使用注解@FeignClient定义的feign客户端,并把feign客户端注册到IOC容器中
@EnableFeignClients
声明伪RPC的Rest接口,必需声明在Interface上
接口创建动态代理,然后通过这个代理发送HTTP请求到我们指定分服务端
@FeignClient
default
针对全部服务
serviceName
针对某个服务
GZIP 注意okhttp不生效
数据压缩
加载默认配置
registerDefaultConfiguration
加载FeignClient对应的配置
registerClientConfiguration
将标注@FeignClient 类加载到IOC容器
获取FeignContext上下文
FeignContext context = this.applicationContext.getBean(FeignContext.class);
从FeignContext中,根据contextId获取ApplicationContext,然后获取Feign.Builder
Feign.Builder builder = feign(context);
从FeignContext中,根据contextId获取ApplicationContext,然后获取Client
LoadBalancerFeignClient
从FeignContext中,根据contextId获取ApplicationContext,然后获取Targeter
创建并返回代理对象
getTarget
getObject
FeignClientFactoryBean
registerFeignClient
registerFeignClients
FeignClientsRegistrar.registerBeanDefinitions
实现ImportBeanDefinitionRegistrar接口
@Import({FeignClientsRegistrar.class})
OpenFeign
防止单个服务的故障耗尽系统中的所有线程资源
保护线程资源
当某个服务发生了故障,不让服务调用方一直等待,而是直接返回请求失败
快速失败机制
请求失败后,提供一个设计好的降级方案,通常是一个兜底方法,当请求失败后即调用该方法
提供降级(FallBack)方案
使用熔断机制,防止故障扩散到其他服务
防止故障扩散
提供熔断器故障监控组件 Hystrix Dashboard,随时监控熔断器的状态
监控功能
意义
单个服务降级
fallbackMethod
HystrixCommandProperties找到Key
@HystrixProperty
commandProperties
@HystrixCommand
全局服务降级
defaultFallback
@DefaultProperties
启用Hystrix断路器
@EnableHystrix
启用Hystrix断路器,允许使用@CircuitBreaker注解来标记需要使用断路器的方法,支持其他断路器实现
@EnableCircuitBreaker
注解说明
Hystrix Dashboard
监控
集成GateWay
一定的时间窗口内,当请求的次数达到一定的失败比率后,hystrix就会主动拒绝服务,一段时间内不会处理请求(即使服务恢复了)
熔断触发的最小个数,即在一定的时间窗口内请求达到一定的次数,默认20
hystrix.command.default.circuitBreaker.requestVolumeThreshold
时间窗口,默认10s
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=5000
失败率达到多少百分比后熔断 默认值:50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
熔断多长时间后,尝试放一次请求进来,默认5秒
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
主要参数
THREAD(线程池 默认)
SEMAPHORE(信号量)
execution.isolation.strategy
支持超时直接返回
支持熔断,线程池达到maxSize会降级调用fallBack方法
每个服务单独用线程池
可以异步
线程切换造成CPU负载高
线程池
不支持超时,会一直阻塞
支持熔断,信号量达到maxConcurrentRequest会降级调用fallBack方法
通过信号量计数器实现
同步调用
单线程
信号量
模式对比
隔离模式
滑动窗口
熔断算法
熔断机制
Hystrix
API网关是一个服务器,是系统的唯一入口
鉴权,限流,日志,缓存等
client->Gateway->Gateway Handler Mapping->Gateway Web Handler->Filter->Service
工作流程
路由的ID
id
匹配路由的转发地址
uri
多个Predicate组合
各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配
一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发
predicates
路由的优先级,数字越小,优先级越高
order
过滤器 过滤掉一些请求, 满足则转发
Filter
路由(Route)
作用:匹配http请求
After某某时间以后,断言才会进行匹配
After
在某某时间以前,断言才会进行匹配
Before
Between
name=regexp 匹配cookie名称为name,值为正则匹配
Cookie
header=regexp 匹配请求Header名称为header,值为正则匹配
Header
一组匹配的域名列表
Host
匹配一个或多个Http请求方式
子主题
Method
请求路径匹配,包含1个或者多个路径,matchTrailingSlash=true才会生效
Path
Query
RemoteAddr=192.168.1.1/24 IP地址匹配 比如匹配192.168.1.1-192.168.1.24地址
自定义获取remote address的处理器RemoteAddressResolver
返回始终采用标头中找到的第一个 IP 地址
trustAllRemoteAddressResolver
采用与在 Spring Cloud Gateway 前面运行的可信基础设施数量相关的索引
maxTrustedIndex
基于X-Forwarded-For请求头的XForwardedRemoteAddressResolver
部署在代理服务器(Nginx)获取不到真实请求IP
部署代理服务器应用获取不到真实请求的客户端IP,而是代理服务器IP
背景
HTTP代理会在HTTP协议头中添加X-Forwarded-For头,用来追踪请求的来源
发起请求时,请求头上带上一个伪造的X-Forwarded-For
proxy_set_header X-Forwarded-For $remote_addr;
最外层Nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
里层Nginx
Nginx配置
从右向左遍历 剔除内网IP与本身服务器IP,获取可信任IP
RemoteIpValve
Tomcat服务器
指定代理层数,然后取X-Forwarded-For头中从右往左数第maxProxyCount个IP即为真实 IP 地址
maxProxyCount
Egg.js
伪造X-Forwarded-For
X-Forwarded-For
RemoteAddr
指定当前请求被路由的权重的,其接收两个参数:分组和比重,参与权重路由的地址必须在同一个分组,否则weight无效果,而权重为整型数字
Weight
继承AbstractRoutePredicateFactory重写apply方法
类必须加上RoutePredicateFactory作为结尾
自定义断言
断言(Predicate)
作用:可以修改输入输出请求
网关过滤器,需要通过spring.cloud.routes.filters配置在具体路由
通过spring.cloud.default-filters 配置在全局,作用在所有路由上
说明
给当前请求添加一个请求头
AddRequestHeader
当前请求头不存在Key时,给当前请求添加请求头,冒号分隔的 name 和 value 键值对的集合
AddRequestHeadersIfNotPresent
给当前请求添加请求参数 多个参数添加多次
AddRequestParameter
给响应的请求添加一个请求头
AddResponseHeader
集成hystrix 支持错误fallBack
CircuitBreaker
缓存请求Body key在ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR定义
CacheRequestBody
对指定响应头去重复。
RETAIN_FIRST保留第一个/RETAIN_LAST保留最后一个/RETAIN_UNIQUE保留唯一
去重策略strategy
DedupeResponseHeader
降级时在Header里面添加信息
FallbackHeaders
将一个JSON payload 转换为gRPC请求。
JsonToGrpc
过滤器允许缓存响应体和header,只能缓存Get
LocalResponseCache
接受 fromHeader 和 toHeader 参数 toHeader将匹配改写fromHeader 存在的值
MapRequestHeader
发送至网关之前修改请求body
ModifyRequestBody
修改响应体发送给客户端
ModifyResponseBody
给请求添加前缀/PrefixPath/url
PrefixPath
无参,在转发请求到服务提供者的时候,会保留host信息
PreserveHostHeader
跳转到指定位置
RedirectTo
移除Reponse的相关属性
RemoveJsonAttributesResponseBody
删除请求指定Header
RemoveRequestHeader
删除请求指定Parameter
RemoveRequestParameter
删除响应指定的Header
RemoveResponseHeader
限定请求Head的大小,超过Size发送431状态码
RequestHeaderSize
限流 令牌桶算法 可使用redis 实现
RequestRateLimiter
重写Location 响应头的值
RewriteLocationResponseHeader
重写请求路径
RewritePath
重写响应头的值
RewriteResponseHeader
强制保存Session
SaveSession
改写请求路径
SetPath
改写请求的header,将指定key改为指定value,如果该key不存在就创建
SetRequestHeader
改写响应的header,将指定key改为指定value,如果该key不存在就创建
SetResponseHeader
控制返回http状态码
SetStatus
改写/ path
StripPrefix
retries-重试次数/statuses-遇到什么样的返回状态才重试/methods-那些类型的方法会才重试/backoff-重试策略/exceptions-重试异常/series-series值重试
重试请求
Retry
控制请求大小,可以使用KB或者MB等单位,超过这个大小就会返回413错误
RequestSize
修改请求header中的host值
SetRequestHostHeader
将第三方的token转发到服务提供者那里去
TokenRelay
重写AbstractGatewayFilterFactory实现apply方法
自定义Filter
分类
GatewayFilter
全局过滤器,不需要在配置文件中配置,作用在所有的路由上
自定义路由
GlobalFilter
业务逻辑之前
pre
业务逻辑之后
post
生命周期
过滤器(Filter)
三大核心
引入redis依赖,配置端口 登录信息
自定义KeyResolver,定义限流标识(比如IP)
burstCapacity:令牌桶总容量
replenishRate:填充令牌速率 每秒
key-resolver 自定义Bean 的name
添加RequestRateLimiter配置
限流
引入注册中心Eureka/nacos
配置 uri:lb//serviceName
配置:discovery.locator.enabled= true
登录成功创建JWT令牌
登录请求
校验JWT令牌合法性,获取登录用户信息
非登录请求
定义全局过滤器AuthorizeFilter
鉴权
记录请求Body等日志存储在NOSQL
定义全局过滤器LogFilter
集成Actuator 具体操作查看actuator 官方API 动态创建 删除路由
实现RouteDefinitionRepository接口
持久化动态路由
动态路由
应用
Gateway
配置中心
注册中心·
功能
应用名-环境profile名.文件后缀(appName-dev.yaml)
Data ID(应用名)
分组
GROUP
默认是空串,公共命名空间
Namespace
基本组成
添加依赖nacos-spring-context
启用 Nacos Spring 的配置管理服务
serverAddr(配置服务地址)
@NacosProperties
@EnableNacosConfig
dataId=** 加载数据源为**
autoRefreshed 自动刷新配置
@NacosPropertySource
获取注解设置属性值
@NacosValue
添加依赖nacos-config-spring-boot-starter
nacos.config.server-addr=127.0.0.1:8848 配置服务地址
application.properties
@NacosPropertySource 同Spring
@NacosValue 同Spring
Spring Boot
添加依赖spring-cloud-starter-alibaba-nacos-config(需注意版本对应)
配置服务地址bootstrap.properties 优先加载配置
spring.cloud.nacos.config.server-addr
${prefix}-${spring.profiles.active}.${file-extension} 多环境
spring.cloud.nacos.config.prefix 配置
spring.application.name 默认 未配置
prefix
dataId
配置自动更新
@RefreshScope
spring.cloud.nacos.config.shared-configs
共享配置
spring.cloud.nacos.config.extension-config
扩展配置
主配置 > 扩展配置(extension-configs) > 共享配置(shared-configs)。
shared-configs[3] > shared-configs[2] > shared-configs[1] > shared-configs[0]
extension-configs[3] > extension-configs[2] > extension-configs[1] > extension-configs[0]
相同配置 优先级高的配置覆盖优先级低的配置
配置优先级
Spring cloud
项目集成
derby数据库(默认)
版本要求:5.6.5+
修改conf/application.properties
执行conf/nacos-mysql.sql脚本,重启nacos
Mysql
long pull 长轮训
配置同步
1.先写某个节点缓存,写入MySql数据库
2.发布一个事件通知,其他节点接收通知,从mysql中重新拉取缓存
磁盘缓存同步
存储Mysql不存在集群问题
集群
Nacos
Sentinel
Seata
Spring Cloud
分布式事务
临界问题,不在特定区间内请求无法被统计到
传统计数器
解决临界问题,对大窗口切割小窗口,平滑移动窗口统计
滑动窗口计数器
无法应对短时间的突发流量
消费速率固定问题
漏桶算法
解决令牌桶问题
消费速率不受限
令牌桶算法
限流算法
分布式
对修改关闭
对扩展开放
开闭原则(抽象定义框架,实现定义细节)
减少耦合性
提高可读性 可维护性 稳定性
依赖倒置原则(细节依赖抽象 针对接口编程)
降低复杂度
提高可读性 可维护性
降低变更风险
单一职责原则(一个类,接口,方法只负责一个职责)
多个专门接口替代单一总接口
接口隔离原则(细化接口)
迪米特法则(减少类之间耦合)
子类扩展父类,不能改变父类原有功能
子类可以增加独有方法
子类输入参数更加简洁宽松
子类输出参数更加全面
里氏替换原则(子类强化父类功能,不修改父类功能)
降低类的耦合度
合成复用原则(尽量使用对象的组合,聚合)
设计原则
逻辑简单,只需要传入参数即可
工厂类职责过重,不利于扩展
简单工厂模式(适用于创建对象少Calendar)
扩展性强,符合开闭原则
代码复杂,子类臃肿过多
工厂方法模式(定义创建对象接口,子类实现 ILoggerFactory)
抽象工厂模式(适用于一系列有关联系产品)
工厂模式(FactoryBean)
优点:执行效率高 无锁
缺点:浪费内存
饿汉式单例
优点:节省内存
缺点:线程不安全
懒汉式单例
优点:线程安全
缺点:效率问题
加锁懒汉式单例(double check)
性能高,无线程安全问题
能够被反射破坏
内部类单例
容器式单例(IOC)
单例模式
拷贝的是对象引用
浅拷贝
序列化对象,生成全新对象
深拷贝
原型模式(对象拷贝复制)
复杂的对象创建与使用分离,封装解耦优秀
内部结构变化 维护成本高
建造者模式(对象的创建Netty)
底层反射实现
被代理类必须实现接口
jdk动态代理
底层通过编码继承类实现
被代理类不需要实现接口,继承实现功能
cglib代理
代理模式(AOP)
JdbcUtils
Dao/Controller
门面模式(统一调度/工具类)
BeanWrapper
装饰器模式(扩展功能)
系统需要大量相似对象,需要缓冲池
常用于底层开发(Integer)
享元模式(资源共享)
目标角色(外部系统接口)
原角色(自己系统接口)
适配接口(适配接口)
角色定义
类适配器
对象适配器
接口适配器
适配器类型
适配器模式(适应不同接口开发的接口)
抽象与具体实现之间存在灵活多变的场景
一个类存在多个变化过程,这个过程需要独立扩展
不希望被继承 实现功能
适用场景
Abstraction 抽象类
RefinedAbstraction 扩充抽象类
Implementor 实现类接口
ConcreteImplementor 具体实现类
主要角色
桥接模式(抽象与具体实现分离)
委派模式
模板方法模式
策略模式
责任链模式
迭代器模式
命令模式
中介者模式
访问器模式
观察者模式
访问者模式
设计模式
继承Thread 重写run方法
实现Runnable接口 实现run方法
实现Callable接口 实现call方法 带返回值
实现方式
让当前线程睡一会
sleep
是等待调用join线程结束继续运行
join
短暂放弃CPU使用权,让其他线程优先执行
yield
打断线程,但当打断阻塞线程 sleep wait join 会中断服务,打断后打断标记仍为false,不会打断线程
interrupt
操作说明
Thread t=new Thread()
NEW 初始状态
Thread.yield()
Object.notify()
Object.notifyAll()
LockSupport.unpark()
t.start()等CPU调度
READY 就绪状态
CPU正在调度
RUNNING 运行状态
synchronized
BLOCKED 阻塞状态
Thread.join()
LockSupport.park()
Object.wait()
WAITING 等待
Thread.sleep(long)
Thread.join(long)
LockSupport.parkUntil(long)
Object.wait(long)
TIMED_WAITING 超时等待
TERMINATED 终止状态
线程状态
不推荐,会强制退出正在执行的任务
Thread.stop()
推荐使用,中断标志 判断是否结束线程
Thread.currentThread.isInterupted()
终止线程
锁定整个方法
方法上
锁定包含的代码块上
代码块上
类锁
静态方法/类对象
Object对象头部区域存储锁状态标记
JMM MonitorEnter 加锁/MonitorExit 释放锁 唤醒其他线程
锁升级:偏向锁-轻量级锁-重量级锁
获取::当一个线程获取该对象锁时候。对象头存储当前线程ID 单次CAS
释放:线程结束/其他线程获取偏向锁
偏向锁
CAS自旋退出,抢占到线程 多次CAS
轻量级锁
监视器锁(Monitor)来实现的
重量级锁
锁优化
锁定当前线程
wait
锁定当前线程 如果经过long(毫秒)时间后没有收到notify或者notifyAll的通知,自动从等待队列转入锁池队列
wait(long)
随机从等待队列中通知一个持有相同锁的一个线程唤醒
notify
通知等待队列中的持有相同锁的所有线程,让这些线程转入锁池队,唤醒全部线程
notifyAll
通信
内存中的值V、旧的预估值X、要修改的新值B 如果V=X 则将V改成B
while 循环比较直到V=X
原子更新整型变量
AtomicInteger
原子更新长整型变量
AtomicLong
原子更新布尔变量
AtomicBoolean
原子更新整型数组的某个元素
AtomicIntegerArray
原子更新长整型数组的某个元素
AtomicLongArray
原子更新引用类型数组的某个元素
AtomicReferenceArray
原子更新引用类型
AtomicReference
原子更新引用类型里的字段
AtomicReferenceFieldUpdater
原子更新带有标记位的引用类型
AtomicMarkableReference
原子更新整型字段
AtomicIntegerFieldUpdater
原子更新长整型字段
AtomicLongFieldUpdater
原子更新带有版本号的引用类型
AtomicStampedReference
原子类
ABA问题 用AtomicStampedReference解决问题
自旋时间太长 带来性能开销问题
只能保证一个共享变量的原子操作
CAS
如何保证互斥性--共享资源state 0-无锁 1-有锁
被拒绝访问的线程如何挂起--lockSupport.park
同一个线程重复访问如何处理--可重入性保存抢锁线程ID
设计思考
重入锁定义:一个线程多次抢占同一把锁,不需要抢占锁,只需要记录重入次数
Lock(接口)
AQS父类AbstractQueuedSynchronizer
跟NonfairSync大体一致, 区别 无抢锁过程 tryAcquire之前判断队列是否为空
lock
跟NonfairSync大体一致
unlock
FairSync(公平锁 子类 实现类)
CAS 抢锁 0-1 设置当前线程
0 无锁
exclusiveOwnerThread=currentThread当前锁定线程 记录重入次数
非当前线程 抢锁失败
1 有锁
getState 获取锁状态
tryAcquire
Node 存储线程节点Thread
表示当前结点已取消调度。当timeout或被中断(响应中断的情况下),会触发变更为此状态,进入该状态后的结点将不会再变化
CANCELLED(1)
表示后继结点在等待当前结点唤醒。后继结点入队时,会将前继结点的状态更新为SIGNAL。
SIGNAL(-1)
表示结点等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁
CONDITION(-2)
共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。
PROPAGATE(-3)
新结点入队时的默认状态
INITAL(0)
WaitStatus=0
构建双向链表Head-Node-Node-Tail
addWaiter
成功:退出循环
失败:Node.Pre<>Head流程
tryAcquire(抢锁)
Node.Pre=Head
是否要挂起抢锁失败线程
shouldParkAfterFailedAcquire
挂起线程
parkAndCheckInterrupt
Node.Pre<>Head
aquireQueued
acquire
失败
保存当前线程setExclusiveOwnerThread
成功
抢锁 state(CAS 0-1)
lock(加锁)
state=state-1
exclusiveOwnerThread=null
操作
LockSupport.unpark Head.next 线程节点
tryRelease
release(1)释放锁
unlock(释放锁)
NonfairSync(非公平锁 子类 实现类)
Sync(父类)
实现
ReentrantLock(重入锁)
特性:写写互斥/读写互斥/读读不互斥
ReadLock(读锁)
WriteLock(写锁)
ReentrantReadWriteLock(重入读写锁)
StampedLock
阻塞当前持有锁的线程
await
唤醒处于condition阻塞状态下的线程
signal
唤醒处于condition阻塞状态下所有线程
signalAll
用法
构建condition单向链表,存储当前线程
addConditionWaiter
release(state)彻底的释放锁
unparkSuccessor 唤醒Lock 处于阻塞状态下线程
fullyRelease
阻塞当前线程
LockSupport.park
将condition队列 第一个线程移动到AQS队列
transferForSignal
唤醒condition队列中的线程
LockSupport.park(node.Thread)
condition
锁
保证内存的可见性
禁止指令重排序
volatile
作用:线程独有变量
获取当前线程currentThread.ThreadLocalMap.get(threadLocal)
get
获取当前线程currentThread
set
调用threadLocal.remove 删除弱引用
内存泄漏
ThreadLocal
作用:线程安全的hashMap
jdk1.7 数组+Segment+分段锁
jdk1.8 数组+链表+红黑树 CAS+Synchronized保证线程安全
弱一致性 get可能获取到过期的值
ConcurrentHashMap
组件/关键字
功能:一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行
构造函数初始化state=n
countDown方法实现state=state-1,并且如果state=0唤醒await阻塞在AQS队列中线程
自循环阻塞latch线程 当state=0时候退出循环
CountDownLatch
功能:同一时间段只能有N个线程访问,其他线程等待挂起,等到线程释放,等待线程恢复访问(限流)
初始化 state=N N个线程可以同时访问
成功 线程继续执行
失败 线程保存在AQS阻塞队列
获得令牌 state=state-1
唤醒处于AQS阻塞队列中的线程
释放令牌state=state+1
release
Semaphore
功能:可循环使用(Cyclic)的屏障(Barrier)
初始化 state=n
condition.await
到达指定N个线程 condition.signalAll
reset 重新初始化
1.CyclicBarrier基于Lock+condition实现的,CountDownLatch继承AQS实现的
2.CyclicBarrier可以重复使用;而CountDownLatch是一次性的,不可重复使用
3.CyclicBarrier中操作计数和阻塞的是同一个线程,调用方法只有一个await方法;而CountDownLatch中操作计数和阻塞等待是N个线程,控制计数调用方法countDown
与CountDownLatch区别
CyclicBarrier
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newFixedThreadPool
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
newSingleThreadExecutor
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newCachedThreadPool
创建一个定长线程池,支持定时及周期性任务执行
newScheduledThreadPool
基本类别
线程池中的常驻线程数,即保持存活的线程数量
corePoolSize(核心线程数)
线程池中允许的最大线程数,即线程池中允许存在的最多线程数量。
maximumPoolSize(最大线程数)
由数组构成的有界阻塞队列
ArrayBlockingQueue
由链表构成的有界阻塞队列
LinkedBlockingQueue
支持优先级排序的无界阻塞队列
PriorityBlockingQueue
支持优先级的延迟无界阻塞队列
DelayQueue
单个元素的阻塞队列
SynchronousQueue
由链表构成的无界阻塞队列
LinkedTransferQueue
由链表构成的双向阻塞队列
LinkedBlockingDeque
用于保存等待执行的任务的队列,有多种实现方式BlockQueue
workQueue(任务队列)
当线程池中的线程数量大于核心线程数时,多余的空闲线程的存活时间
keepAliveTime(线程存活时间)
用于创建新线程的工厂,可以定制线程名、线程优先级等
threadFactory(线程工厂)
直接抛出 RejectedExecutionException 异常
AbortPolicy
在任务被拒绝添加后,会使用调用线程池的线程来执行该任务
CallerRunsPolicy
将等待时间最长的任务丢弃,然后尝试将当前任务添加到工作队列中
DiscardOldestPolicy
直接将任务丢弃,不作任何处理
DiscardPolicy
实现接口自定义策略
RejectedExecutionHandler
当任务队列满时,对新任务的处理策略
rejectedExecutionHandler(拒绝策略)
ThreadPoolExecutor参数
runWork执行线程
addWork(**,true)创建线程
corePoolSize未满
执行rejectedExecutionHandler拒绝策略
线程池已满
addWork(**,false)创建线程
线程池未满
workQueue队列已满
任务work存储队列
workQueue队列未满
corePoolSize已满
execute提交执行任务
true
true->reject(command)执行拒绝策略
false->
false->remove(command) 删除任务
true->isRuning判断线程running状态
true->
false->reject(command)执行拒绝策略
true->workQueue.offer(command)加入workQueue阻塞队列
false
isRuning(验证线程是否runing状态)
workerCountOf(c) <corePoolSize 线程池个数<核心线程数
thread.start()
true->HashSet.add(new Work(task))
回滚操作
false->addWorkerFailed
addWork
while(task=getTask())
runWorker
从阻塞队列workQueue拿数据 不用等
从阻塞队列workQueue拿数据 阻塞 死等
false->workQueue.take()
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize
getTask
processWorkerExit
重写beforeExecute方法
重写afterExecute方法
继承ThreadPoolExecutor
自定义线程池
ThreadPoolExecutor
功能:获取线程的执行结果
任务结束后返回一个结果,阻塞线程
停止一个任务
cancel
方法是否完成
isDone
方法是否取消
isCancel
调用callable.call方法执行线程获取返回结果
result=Callable.call()
CAS操作 将返回结果赋值共享变量
遍历waiters链表 unpark 唤醒线程
finishCompletion
outcome=set(result)
run
保存当前线程到waitNode 单项链表
addWait
挂起当前线程
park
awaitDone
未完成<=COMPLETING
report
已完成
原理分析(FutureTask)
Future
CompletableFuture
功能:大任务拆分小任务,最后统计结果
ForkJoin
线程组件
互斥 共享资源X与Y只能被一个线程占用
占用且等待 线程1占用资源X等待获取资源Y时候不释放X
不可抢占 其他线程不能抢占线程1占用资源X
循环等待 线程1等待线程2资源Y释放,线程2等待线程1资源X释放
产生条件
2. 锁顺序一致性 例如 线程A,B均是锁Obj1 然后锁Obj2
如何破坏
保持与CPU核心数量一直如8核CPU 可以设置8线程
CPU密集型
可以适当提高线程数量 一般是CPU核心数*2
IO密集型
线程数量选择
多线程
访问文件
FileInputStream
访问数组
ByteArrayInputStream
访问管道
PipedInputStream
缓冲流
BufferdInputStream
对象流(反序列化)
ObjectInputStream
抽象基类流
FilterInputStream
推回输入流
PushbackInputStream
特殊流
DataInputStream
InputStream(字节输入流)
FileReader
CharArrayReader
PipedReader
访问字符串
StringReader
BufferedReader
转换流 字节流转换为字符流 解决编码问题
InputStreamReader
FilterReader
PushbackReader
Reader(字符输入流)
输入流I
FileoutputStream
ByteArrayOutputStream
PipedOutputStream
BufferedOutputStream
对象流(序列化)
ObjectOutputStream
打印流
PrintStream
DataOutputStream
OutputStream(字节输出流)
FileWriter
CharArrayWriter
PipedWriter
StringWriter
BufferedWriter
转换流 字节流转换为字符流 解决编码问题
OutputStreamWriter
FilterWriter
Writer(字符输出流)
输出流o
8位的二进制数据
以字节为单位进行读写,不涉及字符编码转换
用于处理图像、音频、视频等二进制数据。
字节流
16位Unicode字符
对数据进行编码和解码,可以用来处理不同字符集的数据
处理文本数据
字符流
区别
减少IO次数
源文件10B buffer=6B只会2次IO
buffer
清空缓冲区,强制将缓冲区的数据写入文件或是读取
flush
一个中文3b 每次读取1b导致乱码问题
中文乱码
ObjectOutputStream 对象流(序列化)
ObjectInputStream 对象流(反序列化)
序列化
应用程序缓冲区
内核缓冲区
本地磁盘(DMA)
内核缓冲区无数据,会挂起请求的用户请求,加载到数据会唤醒用户请求
本地磁盘
应用程序
写入IO
client 端
获取输入流(接收消息)阻塞操作
getInputStream
获取输出流 发送消息
getOutputStream
Socket
Server 端
阻塞 获取客户端链接
accept
ServerSocket
网络IO
应用层->传输层->网络层->数据链路层->物理层
请求过程
http请求报文
应用层
tcp头
传输层
ip头
网络层
mac头
数据链路层
流传输
物理层
分层模型
Http协议
Https协议
网络协议
阻塞IO,ServerSocket.accept 阻塞后续请求
1.用户进程recvfrom
2.内核无数据包,准备接受数据
3.数据包接收完成
4.复制数据包
5.复制完成
6.处理成功 返回数据包
流程分析
BIO
非阻塞IO 面向缓冲区
6.处理成功 返回数据包
从文件中读写数据
FileChannel
从UDP协议中读取数据
DatagramChannel
从TCP协议中读取数据
cofigureBlocking=false 非阻塞模式
SocketChannel
监听一个Tcp链接,每个Tcp链接都会产生一个SocketChannel
cofigureBlocking=false 非阻塞模式
ServerSocketChannel
实现类
Channel(通道)
创建和销毁效率高
工作效率低
在堆中创建缓冲区(间接缓冲区)
allocate(int capacity)
创建和销毁效率低
工作效率高
在系统内存创建缓冲区(直接缓冲区)
allocatDirect(int capacity)
=堆缓冲区
数组创建缓冲区
wrap(byte[] arr)
创建缓冲区
向当前可用位置添加数据
put(byte b)
向当前可用位置添加一个byte[]数组
put(byte[] byteArray)
添加一个byte[]数组的一部分
添加数据
Buffer所能够包含的元素的最大数量。定义了Buffer后,容量是不可变的
容量(capacity)
limit以及之后限制写入与读取,不能大于capacity
限制(limit)
当前可写入的索引位置,不能小于0 不能大于limit
位置(position)
调用reset position=mark
标记(mark)
重置postion
重置(reset)
position=0
limit=capacity
mark舍弃
还原(clear)
limit=position
mark丢弃
反转读写模式
缩小范围(flip)
mark 丢弃
重绕缓冲区(rewind)
缓冲区是否只读
只读(isReadOnly)
获取当前缓冲区是否为直接缓冲区
直接缓冲区(isDirect)
获取position与limit之间的元素数
获取元素数(remaining)
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
Buffer(缓冲区)
定义:管理多个NIO的channel通道是否为读写事件做好准备的组件
阻塞 所有注册事件
select()
返回所有可执行事件(解除阻塞)的Keys
连接就绪
OP_CONNECT
接收就绪
OP_ACCEPT
读就绪
OP_READ
写就绪
OP_WRITE
selectedKeys()
Selector(选择器)
核心组件
NIO
定义:多个IO请求可以注册到select上,liunx监控所有select注册的链接,数据准备好就会返回
弊端:支持连接数有限 一般支持1024个链接,内核会扫描所有注册的select 所有链接 会带来性能下降
1.用户IO请求注册select
2.内核无数据 不阻塞
3.数据包接收完成 select 返回状态 可读
4.复制数据包 系统调用 recvfrom
select/poll
定义:采用fd事件通知CallBack的方式,解决poll模式下轮询带来性能问题
优势:处理连接数不限制(受限于内存),连接数取决于最大句柄数,不需要轮询,性能得到提升 mmap复制内存 连接数少socket活跃情况下不如poll效率高
1.用户IO请求注册select fd注册信号处理程序
2.内核无数据 不阻塞
3.数据包接收完成 select 返回状态
4.mmap 映射内存区域
5.复制完成 调用fd的callback 通知应用程序处理后续逻辑
epoll(2.6内核之后)
1.应用程序调用sio_read 直接返回
2.系统接收数据完成 提交SIGIO信号
3.系统复制数据完成 递交aio_read 通知应用程序处理
aio
IO复用
原理解析:用户缓冲区与内核缓冲区 映射共享内存
拷贝过程:硬盘—>内核—>socket缓冲区—>网卡
API调用:MappedByteBuffer.map()
特点:用户可以修改内存,不能超过2G 适用于小文件 rocketMq
原理解析:用户进程发送指令,内核空间内部进行 I/O 传输 无法映射共享内存
API调用:FileChannel.transferFrom()/transferTo()
特点:用户不可以修改内存,适合大数据量的传输,kafka
sendFile
零拷贝
Netty
IO
xx.java->javac->xx.class->jvm
xx.java->词法分析器->token流->语法分析器->语法树->语义分析器->注解语法树->代码生成器->xx.class字节码
javac
加载JAVA_HOME/jre/lib/rt.jar或Xbootclassoath选项指定目录jar包
Bootstrap ClassLoader
加载java平台扩展功能包JAVA_HOME/jre/lib/*.jar 或者 -Djava.ext/dirs指定目录下jar包
Extension ClassLoader
加载classpath下jar包以及Djava.class.path指定目录类或者jar
App ClassLoader
继承ClassLoader 自定义加载class
Custom ClassLoader
类加载器ClassLoader
类加载优先级Bootstrap ClassLoader>Extension ClassLoader>App ClassLoader>Custom ClassLoader
继承classLoader 重写 loadClass 方法
Thread.currentThread.setContextClassloader(mysql驱动)
如何破坏?
双亲委派模式
类加载
类的字节码文件流信息->jvm方法区
类文件中对象的class->jvm堆
装载
验证被加载的类的正确性
验证
静态变量分配内存空间,初始化默认值(static int a=10;为a分配内存空间,初始化为0)
准备
将类中符号引用转为直接引用(映射内存地址)
解析
链接
为静态变量初始化真正的值 (a=10)
初始化
使用
卸载
内存加载
只有一个 线程共享 非线程安全 生命周期跟jvm虚拟机一样
类信息
常量
静态变量
即时编译器编译后的代码
存储结构(OOM)
方法区Method
对象
数组
堆Heap
一个线程创建一个 线程私有 线程安全 生命周期跟线程一样
局部变量表
操作数栈
动态链接
方法的返回地址
栈帧(1:n)
存储结构(StackOverflowError)
虚拟机栈stack
c语言方法 native 方法
本地方法栈native stack
线程私有,线程安全 记录线程代码运行位置
程序计数器pc resigter
运行时数据区
大对象 新生代放不下
youngGC>15次还没有被垃圾回收
老年代/元空间 old GC 2/3
eden区 8/10
空间不够可向old借用空间(担保机制)
s0 1/10
s1 1/10
s区(解决空间碎片问题)
新生代 young gc 1/3
内存结构
栈中本地变量
static 成员
本地方法栈中变量
类加载器
Thread[java线程]
引用不可达算法GCRoot
判断垃圾
空间碎片问题
标记清除算法
空间浪费问题,s区
复制算法
效率问题,老年代
标记整理算法
垃圾回收算法
单线程垃圾收集器 适用新生代 复制算法
Serial
Serial老年代版本 标记整理算法
Serial Old
多线程垃圾收集器 适用于新生代 复制算法
ParNew
与ParNew类似 更加关注吞吐量 复制算法
Parallel Scavenge
Parallel 老年代垃圾收集器 标记整理算法
Parallel Old
1.初始标记
2.并发标记(不会终止用户线程)
3.重新标记
4.并发处理(不会终止用户线程
3.最终标记
4.筛选回收(有选择性回收垃圾)
G1(新老年代,自设置停顿时间,标记整理算法,用户交互 web端)
垃圾回收器
垃圾回收
jps 查询Java进程信息
jinfo查看jvm参数/修改jvm参数
jstat 查看jvm运行时状态信息, 包括内存状态、垃圾回收
jstack 查看jvm线程快照的命令
jmap 生成dump文件,查询堆内对象示例的统计信息、查看classLoader的信息以及finalizer队列
jhat 分析dump文件 用于离线分析
常用命令
jconsole
jvisualvm
MAT
arthas
堆内存分析
GCViewer
gceasy.io
GC日志
1.jstat 打印gc日志,查询内存空间大小
2.minor gc/major gc 工具gceasy查看
3.jmap 查询内存使用情况
1.堆内存空间不足
2.edm区域太小,频繁ygc 大量对象进入old区
3.代码问题 大对象
原因分析
GC频繁
1.jstat 打印gc日志,查询内存空间大小
2.jmap 查询内存使用情况
3.jstack 查询线程,cpu请求情况
4.dump堆文件,mat工具分析
5.增大S区
1.大对象问题,系统创建了太多大对象进入了Old区域
2.高并发问题,S区太小,太多对象来不及销毁就进入了Old区域
3.Metaspace(永久代)因为加载类过多触发Full GC。
4.内存泄漏问题,垃圾对象无法被回收
5.错误调用System.gc()调用
6.垃圾收集器问题 空间碎片问题
Full GC频繁
1.top查询cpu使用率高的应用ID
2.jstack 查询线程使用情况,排除死锁问题
3.jinfo 查询JVM参数信息,jmap查询堆内存使用情况
4.jstat 统计FullGC情况
1.内存消耗过啊,导致Full GC次数过多
2.代码中有大量消耗CPU的操作,导致CPU过高,系统运行缓慢;
3.死锁
4.大量线程访问接口
CPU 飙升
1.确定是堆上OOM还是方法区/元空间上OOM
2.方法区OOM 查询代码有没有动态生成类加载类
3.jmap dump 堆上内存信息。mat 分析有没有大对象
4.jinfo 查询堆内存大小,比例情况。元空间大小。垃圾收集器的选择。
1.大对象问题,Old区域放不下
2.Metaspace 加载类过多触发OOM
3.本身内存空间过小问题,加大内存
4.高并发问题,太多对象创建来不及销毁回收
OOM
性能优化
JVM
ArrayList
Linked
HashMap
TreeMap
HashSet
TreeSet
集合
泛型
反射
java 核心
maven
安装配置
基础操作
远程仓库
分支管理
git
jenkens
docker
k8s
虚拟主机
反向代理
http缓存
动静分离
跨域问题
nginx
运维/工具
mycat(代理端)
sharding(JAR端)
分库分表
java 知识库
0 条评论
回复 删除
下一页