类加载器(通过parent变量维护父子关系,非直接继承)启动加载器
bootstrap ClassLoader %JAVA_HOME%lib
扩展加载器
Ext ClassLocaer %JAVA_HOME%/lib/ext
系统加载器、以及线程上下文加载器
App ClassLocaer classpath下的类文件
自定义加载器
继承ClassLoader
双亲委派模型(默认三块缓存,存储全路径的作用域不同,并且可扩展因为有可能是自定义加载器)
先向上委派(查找缓存,有则直接返回,没有继续向下)
再向下查找(查找类路径,有则加载返回,没有继续向下)
核心.java类安全性,并避免加载冲突,判断依据是 全路径名 + 类名
异常
trowable
error exception
runtimexception
checkexception
GC
其他语言,引用计数法:循环引用,无法被回收
Java语言, 可达性分析:GC Root
GC Roots
虚拟机栈(栈帧中的本地变量表)中引用的对象,会弹栈回收
方法区静态属性应用的对象
方式去常量引用的对象
本地房发展JNI Java Natvie方法 引用的对象
ArraryList
数据结构:动态数组,元素内存连续,长度固定
实例化容量:0
触发实例化:10
扩容机制:新数组Copy,当前容量不足,会进行(1.5倍)的比例。如果不满足,则按照最低所需容量上限扩充。
遍历方式:for下标
按值获取:indexOf(破坏下标结构的优点,如果为null,依然全部遍历)
LinkedList
数据结构:链表,元素内存分散
内部底层:维护Node内部类
遍历方式:iterator.next指针(性能略低)
HashMap
k,v 允许为null
HashTable
线程安全:使用properties子类
(6,64)>数组+链表
(8,64)> 数组+红黑树
元素始终以内部类Node节点存在
存储方式:
一次哈希 key、二次哈希 数组长度取值
如果Hash不会产生冲突,直接创建Node节点
如果产生Hash冲突,直接形成链表
数组元素链表呈线性,随着长度增加,效率降低,8+64将会数组索引链表转换为红黑树,如果6+64 将会恢复为链表,对于null:
如果key为null,则存固定的0位置
所有的数据结构最总都会成为数组 或 链表
ConcurrentHashMap
ReentrantLock + Segment + HashEntry
元素查找:二次哈希,第一次定位数组Segment,第二次定位Segment链表头部
锁:Semgent分段锁 ConnhashMap的静态内部类,继承ReentrantLock,
并发度:Semgen的个数,可以通过构造函数干预,数组扩容
可见性:get()读数据无需加锁,volatile
Synchronized+CAS+Node+红黑树
可见性:Node中val和next都使用volatile
为什么使用Synchronize,对于hash冲突,和数组扩容
锁:
读写都是使用CAS乐观锁
Syc锁链表的head头节点,不影响其他元素的读写,粒度更加轻量。数组扩展时阻塞所有读写并发操作
votile用于扩充读线程,和读写操作的线程
集合第一代 安全
Vector Hashtable
sync保证
集合第二代 非安全
ArrayList、HashMap
集合第三代 安全 CAS+AQS
java uril concurrent
ConcurrentHashMap
CopyOnWriteArrayList
CopyOnWriteArraySet
(ConcurrentHashMap升级后使用Sync,其他则使用Lock)
1.8新特性
接口默认方法>兼容Lambda表达式
函数式接口>新注解@FunctionalInterface
Lambda表达式以及新接口
方法引用或构造函数引用:: 对Lambda进行简化
(原理:任何类与方法或对象都能使用方法引用。分为四种,代码块引用或上下文引用)
Lambd作用域
访问访问外层局部Final标记变量或隐式Final不允许下文其他代码修改
访问静态方法
访问接口默认方法增加
Predicate:接口允许一个参数,自带默认与或非方法引用,或自定义返回boolean类型
Function:接口允许一个参数并可以返回一个结果。自带默认方法
Supplier:接口没有村塾,可以返回泛型
Comparator:接口增加了默认方法
Optional:防止空指针异常的接口提供返回默认值的方法
Stream接口:对一组数据源执行操作。中间操作或者最终操作并返回特定的结果。
Filter过滤:通过Predicate接口默认方法并记性选择保留过过滤
Sort排序:可以指定Comparator自定义排序规则
Map映射:通过实现Function接口的默认方法进行中间逻辑操作
Match匹配:指定Predicate接口默认方法并返回boolean判断
最总操作ForEach
Count计数:进行聚合结果操作
Reduce规约:通过Optional接口,进行Optional默认方法进行操作
并发parallelStream(普通的Stream不同)
Map集合虽然不支持Stream
但是依然增加了新的Lambda方法
putIfAbsent
forEach
computeIfPresent
computeIfAbsent
remove键值双匹配重载
getOrDefault默认值获取
merge合并
时间框架
多重注解
并发特性
原子性 一致性 隔离性
线程生命周期
创建、就绪、阻塞(细分三种)、运行、死亡
等待阻塞:执行wit方法,将会被JVM放入‘’等待池‘’,需要notify或者notifyall
同步阻塞:请求与保持,将会被JVM放入‘’锁池‘’
其他阻塞:sleep或join方法,或I/O请求中。
阻塞时放弃CPU使用权,暂停运行。恢复时转为就绪状态等待CPU资源分配。
如果执行完任务或发生异常会出现死亡将无法解锁问题。
锁池
遇见 锁,且没有抢到
等待池
遇见 wait() 方法
sleep() wait() join() yield()
sleep Tread类方法 不释放锁 仅释放资源 自己打断 用于暂停
wati Object类方法 释放锁 其他线程打断 依赖Syc 用于多线程通信
join 让步,看着别人阻塞自己直到,别打断或别人执行完
yield()礼让,释放cpu执行权,但保留cpu执行资格,可能继续执行
线程安全(内存是否共享)
栈 栈帧访问 单线程(栈帧不共享)
堆 全局访问 多线程
Tread和Runable
多个Tread重写多个Run方法(通过Sychrnized+volitel对共享变量控制),Tread继承与Ruable性能更强大
多个线程使用Runabe仅有一个Run方法
守护线程(后台线程):将会守护全部非守护线程,不能一对一
创建线程:守护线程将会默认创建守护线程
线程池中:将会把守护线程转为用户线程
ThreadLocal(事务隔离私有:维护线程本地值)
内部类 ThreadLocalMap
内部类中类 Entry extends WeakReference<ThreadLocal<?>>
弱引用:空键(即 entry.get() == null)意味着不再引用该键
Entry(ThreadLocal<?> k, Object v)
解决:
1. 控制层和业务层。同服务同一线程下跨层传播数据,冗余代码减少
2.线程事务隔离
3.Spring框架事务对数据库Connection的操作使用ThreadLocal保证隔离性
TreadLocal内存泄漏问题
内存泄漏与内存堆积OOM不同,是内存被JVM借用后不能被回收归还。
对于当前的ThreadLocalMap使用完后进行赋值null空键来实现弱引用、或线程正常退出 / 异常退出
ThreadLocalMap声明周期和Thread共生存
1、手动删除防止内存泄漏,调用remove()方法,是key为null
2、使用private static修饰,保持只分配一块存储空间,ThreadLocal强引用找到TreadLocal的Entry value 并进行删除
key为null的Entry,将无法通过key寻找value,存在一条强引用链
线程池为什么不是使用普通队列?为什么不创建最大线程?
保存任务:阻塞任务允许部分缓冲区任务
动态阻塞与唤醒
使用缓冲区,线程池目的尽可能少的创建线程
线程池复用特性
重写Thread增强,避免多次start,使用线程进行循环任务,进行run方法循环执行
如何手动实现SpringIOC容器
配置文件配置包扫描路径,并递归包路径扫描.class文件
通过反射.class文件全路径获取整个类,并存入Set集合
定义IOC容器,定义Map 对对象进行是否依赖其他类的实例,用Hash快速定位检索,然后递归注入(依赖注入)
Spring特性
面向热插拔式框架(其他框架支持)
面向容器框架(生命周期:单例原型)
面向JavaBean框架(解耦)
面向Aop增强框架(动态增强)
面向接口编程(接口注入)
BeanFactory和ApplicationContext
ApplicationContext继承BeanFactory增强
国际化、统一资源访问、监听器、多个配置文件、载入多个上下文
BeanFactroy延迟加载(懒加载),getBean时,进行Bean的属性填充
ApplicationContext容器启动就进行所有Bean的实例初始化,缺点是启动准备,单·1运行速度快
SpringBean生命周期
解析 > 实例化 > 属性填充 > 回调 > 前置 > 初始化 > 后置 > 单例池 > 使用 > 销毁
解析类路径
推断构造方法实例化
进行属性填充
调用Aware回调接口
调用BeanPostProcessor前置方法
调用初始化方法
调用BeanPostProcessor后置方法
将Bean放入单例池中
使用Bean
关闭容器destory销毁
Bean的作用域
Singleton
Protorype
Request
Session
Application
单例Bean线程不安全的问题(如果无状态)
1、使用栈帧变量
2、使用ThreadLocal隔离性
Spring使用的设计模式
工厂 :BeanFactory
单例 :IOC容器
适配器 : HandlerAdapter
装饰者:
动态代理:
观察者:
策略模式:
Spring事务(数据库)
@Transaction将代理对象Proxy类进行
1、自动提交设置为false
2、异常进行回滚,通过roollbackFor手动干预属性配置。除非进行catch捕捉将失效
隔离级别
read un.commit.ted
read commit.ted
re.peat.able read
se.ria.li.zable
如果数据库与Spring不一致,将以Spring为准覆盖
传播机制(多个事务进行交互传播)
nes.ted 有则嵌套,无则创建
requires_new 创建,并挂起可能存在的事务
re.quired 有则加入,没有则创建
sup.port.s 有则加入
man.da.tory 必须加入,否则异常
not_supported 不支持事务,否则挂起事务
naver 绝不出现事务,否则异常
nested 父事务影响子事务回滚,父事务可以进行catch,仅子事务回滚
require_new 回滚两个事务互不印象
required 合并后同时回滚,即使另一方无异常也需要回滚,除非catch当前异常
Spring事务@Tansaction如何失效
Spring底层通过AOP代理类进行实现事务,如果AOP代理类进行替换则会失效
1、自调用.this调用当前的对象,则会失效。使用Ioc容器注入
2、Transaction必须使用public修饰对外提供,否则手动使用Aspectj织入代理模式
3、特殊数据库引擎将不支持事务:如MyISAM
4、没有将被代理类交给Spring IOC容器管理,无法生成AOP代理类
5、异常被catch吃掉,或抛出Spring未定义异常,默认RuntimeException
Bean装配方式
缺省、by.Name、by.Type、构造器、Auto.detect混合、自动装配
Spring > AOP > 依赖注入 > 面向接口增强编程
SpringMVC > 解析Http请求 > 面向Web编程
SpringBoot > 约定(自动配置)大于配置 > 面向开箱编程
SpringMVC执行流程(Dis.pacher.Servlet)
Http请求 > Dis.pacher.Servlet前端处理器
Dis.pacher.Servlet前端处理器调用Http请求 > HandlerMapping处理器映射器
HandlerMapping处理器映射器 > 处理器映射器遍历寻找具体的处理器(xml、注解)> 生成处理器、处理器拦截器 > 前端处理器DispacherServlet
Dis.pacher.Servlet前端处理器调用 >HandlerAdapter处理器适配器 (适配器模式) 遍历判断
HandlerAdapter经过适配调用具体处理器(Controller/@RequstMapping/Serlvet,后端处理器)
Handler执行完返回ModelAndVie
HandlerAdapter将Controller执行结果ModleAndView返回给DispathcerServlet
DispathcerServlet将ModelAndView传递给ViewReslover试图解析器
试图解析器解析返回具体View
DispatcherServlet根据View进行渲染视图
DispatcherServlet想用用户
SpringMVC 九大组件
HandlerMapping > Map<url,handler>
HandlerAdapter >
适配Controller Servlet RequestMapping
Support 适配支持的适配器
HanlderExceptionResovler
全局异常解析器
ViewRsovler试图解析器 > 解析渲染
RequestToViewNameTranslator
LocaleResolver 国际化解析器
ThremResolver
MultipartResolver 文件解析器
FlashMapManager
SpringBoot自动配置原理?
AutoConfigurationImportSelector.class > selectImports > SpringFactoriesLoader.loadFacoryNames > META-INF/spring.factories > EnableAutoConfiguration
SpringBootApplication (@SpringBootConfiguration + EnableAutpConfiguration + @ComponentScan)
@Import + @Configuration/SpringBootConfiguration+META-INF/spring.factories + Spring SPI技术 + @Bean
MyBatis包路径无法被自动扫描,那么通过META-INF/spring.factories 约定大于配置,进行自动加载进行自动扫描,扫描到@Configuration配置类中的@Bean也同时被加载到IOC容器
什么是嵌入式服务器
内置tomcat在webapp目录下
Mybatis优缺点
优点:
1.SQL语句与JAVA代码解耦
2.消除JDBC冗余代码
3.兼容数据库连接池
缺点:
1.MySql数据库专用
Mybatis和Hibernate对比
ORM框架通过对象修改数据库 ,更加面向对象
SQL框架通过SQL语句修改数据库 , 面向表结构
#{} 和 ${} 区别
#{} 预编译防止SQL注入 第一次之后执行速度快
${} 每次编译SQL注入
Mybatis插件运行原理(Mybatis仅支持接口)
Pa.ra.meter.Handler Java类型传入mybatis转换
Re.sult.Set.Handler 接收映射结果集
Sta.te.ment.Handler 设置参数、结果集转换
Exe.cutor Mybatis执行器生成Sql语句
Mybatis版Interceptor接口(AOP动态代理)
@Intercepts + @Signature + @Component + invocation.proceed()
索引的基本原理(定义Sort排序规则)
无序数据 > 定义索引(排序的特征) > 规则排序数据
减少遍历行数据I/O读取,仅获取索引树的节点或获取Hash码的位置上保存行数据地址,通过索引快速定位行数据
聚簇索引
叶子结点 > 索引/唯一数据 > 行数据/范围数据
非聚簇索引
叶子结点 > 列数据/索引 > 通过了数据找到索引 > 列数据/二次查表
写操作维护索引复杂需要分页(pageSplit),大量写建议负载较低时执行,通过Opti.mize Tabble 优化表移动的内存碎步片,使用独享表空间弱化碎片
随机ID(UUID)数据系数,则会比全表扫描更慢,查询不同页需要排序,一定要是用步长规律的主键
索引过长导致非聚簇索引索引叶子结点存储的key值过大占用额外物理空间
InnoDB一定有主键:如果没有显示创建主键,则会使用uniquer索引,否则创建伪键作为主键索引
InnoDB索引结构
B+Tree索引
Hash索引 直接通过跳过根结点 快速等值查询定位 但是不适合多列 顺序 尽量不重复
索引设计规则
where子句
连接子句
数据量大
使用短索引(前缀长度),排除不匹配的值,然后筛选后索引查询
对于利于查询的索引的建立,非必要不要额外创建,需要维护关系
创建索引时,非必要优先考虑联合索引
写频繁
区分度低
对于过大数据字段不要建立索引,使用Es分词
MySQl锁乐行
纬度:共享锁、排它锁
粒度:
行级锁Innodb
表级锁innodb Myisam
页级锁BDB > 表锁行锁之间折中
记录锁 > 命中一条 且 唯一索引字段
间隙锁 > 锁住区间 左开右闭
临键锁 > 记录锁 + 间隙锁 左闭右闭
意向共享锁
意向排他锁
mysql执行优化怎么看
> Explain DQL
id DQL以及子DQL的查询顺序,从大向小
selectType 标识当前DQL子句所属类型
table 标识查询的表
partitions 分区表信息
type 优化sql的等级
一次命中
system 表中只有一行数据,每次命中
const 索引一次命中,匹配一行数据
可能二次回表
eq_ref:唯一索引命中且位而已数据
ref:非唯一索引
range:范围
index:遍历整个索引树
All:全表扫描
possible_keys 期望索引key
key 实际索引key
key_len 通过索引长度判断命中
ref 参考
rows 操作行数
filtered 命中过滤率
Extra
状态提示 using filesort / using temporary /usin index / using where
事务特性
A原子性 C一致性 I隔离性 D持久性
ACID如何保重
A undolog逆向执行SQL
C 通过其他三大特性保证
I 隔离性MVCC保证
D 持久化通过redo log日志
InnoDB redo log 写盘,InnoDB事务进入prepare状态
如果prepare成功,binlog写盘,在将事务日志持久化到binlog
如果持久化成功则进入commit状态,在redo log 写盘commit记录
集群binlog
redolog刷盘会在系统空闲时刷盘
如何处理Sql耗时?统计慢查询?
如果数据量过大,使用主键也无法进行优化,去测试库进行测试耗时。
索引未命中/优化SQL索引
查询不需要列/优化SQL
数据量太大/横竖分表
快照读、当前读
隐藏列(版本链)
事务键(越大越新,可能旧值,可能正确,可能提交)
回滚指针
虚拟主键
隔离级别底层MCCC
MVCC (Multi-Version Concurrency Control )多版本并发控制
隐藏列(版本链)、undo表、Read View 快照读
DQL > ReadView
使用快照,将读锁与写锁不再冲突,不同事务session仅可见自己特定的版本数据与版本链
仅支持read commited 和 repeatable read 事务行粒度
如何防止读到旧值
通过ReadView和事务id进行对比
聚簇索引两个隐藏列
trx_id 记录对某条聚簇索引进行修改的事务id
roll_pointer 回滚指针记录上一次聚簇索引的版本地址,并通过版本地址寻找记录信息(插入操作的undo日志没有这个属性,因为不记录老版本)
readcommited与repeatableread策略不同
开启事务是创建readview,将会提交的数据id(活动中事务id),排序成一个数组
访问数据,获取数据的事务id (最大id记录)对比readview
如果readview在右边,代表事务已经提交,可以访问
如果readview在右边,若在readview区间值,则代表事务未提交,不得访问,
进行读取roll_pointer进行递归向旧版本进行判断
readcommited
每次DQL都会生成独立的刷新ReadView
repeatableread
读将复用一个ReadView
事务ACID
原型性 undolog回滚表 保证
一致性 三护一
隔离性 MVCC 保证
持久性 redolog写日志表 保证 (随机读写效率低,顺序读写高,优先保证redolog后执行写操作)
redolog和binlog一致问题
2PC保证
通过最新的ReadView中的属性与当前事务进行对比:
活跃事务ids数组、最大事务id、ids最小活跃版本号、当前事务id
判断未提交的的活跃事务不允许进行操作
主从同步
三个线程 mater(binlog dump thread) slave(I/O thread、SQL thread)
两个日志 binlog 二进制日志 relaylog 中继日志
一个偏移量 position 增量同步
默认异步,Mater宕机,日志丢失
全同步复制>全部数据库返回ACK
半同步复制>仅一个Slave返回ACK
MyISAM和InnoDB(读写分离,区分表结构)
MyISAM存储表行数/不支持事务,但保证原子性查询/每次操作整表加锁
一个表三个文件(非聚簇索引)索引文件、表结构文件、数据文件
索引数据分离存储,二次回表
InnoDB
主键、ACID事务、行级锁
不记录总行数
一个表一个文件空间(共享表空间)
共享表空间>大小不受系统控制,一个表分布存储多个文件
一个表多个文件空间(独立表空间)
独立表空间>大小受系统文件限制,一般为2G
使用主键插入数据,更好的维护B+树的结构,减少较大调整
索引分类
普通索引:重复值
唯一索引:唯一
主键:特殊的唯一索引
联合索引:组合数据列
全文索引:建立倒排索引,推荐es
通过使用索引将会触发 优化隐藏器
索引滥用的问题
但是会降低所有写操作的速度,因为执行写操作依然维护索引文件
索引占用一定的物理空间,并且随着聚簇索引的变化,非聚簇索引跟着一起变化
Redis
RDB RedisDatebase
fork子进程 临时文件>二进制压缩存储>替换dump.rdb文件
容灾性好 快,方便存储 小
异步 且非主进程执行
数据量大时,优于AOP启动效率
间隔长,数据丢失大
fork子进程在数据集较大时,则会造成CPU占用加长
AOF append only file
策略:每秒同步(异步),每命令同步(同步)
间隔短,数据丢失小
提供redis-check-aof工具解决宕机数据不一致问题
Aop的rewrite模式:对AOF文件进行重写压缩
恢复速度慢
混合模式:优先使用AOF
Redis淘汰策略
Redis线程模型,为什么单线程快
基于Reacotr模式开发 > 文件事件处理器file event handler (单线程)
采用IO多路复用机制监听多个Socket 根据Socket上的时间类型选择对应的事件处理器来处理这个时间。(NIO)
文件事务处理器结构:
多个Socket、IO多路复程序、文件事件分配器、事件处理器
单线程
纯内存操作执行逻辑简答
使用NIO非阻塞 多路复用机制
RDB 会有fork子进程进行操作
避免了多线程上下文切换的性能问题
Nginx底层:
缓存雪崩
随机过期:避免同一时间
缓存续期:标记快过期的热点数据,然后续期
缓存预热:启动服务前进行预热
互斥锁:仅放行一个线程进行穿透测试
缓存穿透(可能来自于恶意访问)
接口效验:参数合法性校验
存储null值
布隆过滤器:hash到一个大的bitmap中,可以保证不存在的数据一定不存在,但是不能保证存在的数据是想要的数据,可以拦截掉不存在的访问
缓存击穿(临时热点过期)
永不过期:
互斥锁:仅放行一个线程进行穿透测试,其他线程进行CAS自旋操作
Redis事务支持(不支持回滚)
事务开启MUILTI > 打开flags属性打开Redis_multi标识
命令入队EXEC >
如果语法问题,造成flags属性关闭,且执行其他指令,不进行回滚
事务执行 >
判断是否打开flags属性打开Redis_multi标识
不支持回滚,遍历事务队列中语法是否错误
开始乐观锁WATCH>进行 chekk and set ,监控一个key或多个key,当预期值发生变化,将不会执行指令进行通知
关闭乐观锁UNWATCH>取消对所有key的监控
Redis集群方案
Sentinel哨兵模式:
集群监控/消息通知/故障转移/配置中心
哨兵集群,通常3个实例,宕机后单机投票
仅保证高可用,取法保证数据零丢失
结构复杂,需要足够的测试
RedisCluster模式服务端Sharding技术
采用16384个槽slot,互为主从,保证数据安全性
通过哈性对key确定槽位置,确定分片位置,支持阻塞同步每份数据分辨
不保证强一致性
读取转发,如果当前分片没有寻找到key,则会告知正确的节点
扩展时需要把纠结点的数据迁移一部分到新节点
RedisCluster架构下 需要开放 6379 与 16379 双端口
16379端口是节点间进行通讯,进行cluster bus 通信
故障转移、配置更新、故障转移授权
cluster‘bus用了另外一种二进制协议,gossip协议,用于节点进行高效的数据交换,占用更少的网络带款’
无中心架构,动态扩充,业务透明
并且可以使用Sentnel进行监控和Failover(故障转移)能力
客户端仅需连接一个任意节点即可
客户端直连Redis服务,免去proxy中间件代理损耗
复杂,数据迁移需要人为干预
只能使用0好数据库
不支持批量操作(pipeline管道操作)
分布式逻辑和存储模块耦合
Redis Sharding 客户端分片技术
由客户端决定key存储位置。用哈希算法
节点互相独立。将key映射到针对的Redis节点上。Java redis客户端确定jedis,支持REdisSharding功能,既ShadedJedis以及结合缓存池的ShardedJedisPool
节点实例独立无关联,线性扩容,灵活。
由于Sharding操作在客户端进行,客户端不支持动态增删节点。
服务端Redis实例拓扑结构发生变化时,每个客户端都需要更新调整。
连接不能共享,当规模增大时,浪费制约优化
Redis主从复制原理(数据同步问题)
首先集群是读写分离
全量复制 > 主节点 bg.save 命令 > fork子进程进行RDB持久化
该过程非常消耗CPU。内页(页表复制)、硬盘IO。
主节点通过网络将RDB文件发送从节点,对主从节点带块都带来很大消耗
dump.rdb文件通过网络进行传输
增量复制
复制offset偏移量
复制积压缓冲区(固定长度,FIFO队列) > offset偏差过大超过缓冲区长度,则全量复制
服务器运行ID(runid) 节点启动>自动生成>被从节点保存 > 从节点宕机恢复依然可以通过Id同步 :如果发生主机节点宕机重新选举,runid就会不相同,则需要找其他节点全量复制
判断ID如果相同判断是否能通过offset复制积压缓冲区
如不相同直接入读新的runid节点并进行全量复制
分布式均衡算法
轮询法
随机发
源地址哈希
加权轮询法
加权随机法
最小连接法
负载均衡类型
DNS 域名解析
硬件负载均衡F5 和 A10
>软件负载均衡
Nginx、HAproxy、LVS
Nginx 七层负载均衡 支持HTTP、E-mial 路由多
LVS 四层负载均衡
分布式架构下Session共享方案(节点数据不共享问题)
有状态的
本地>登录态>解析sid>
1.采用无状态,抛弃Session ,使用JWT,token
2.存入cookie有浏览器风险
3.服务器之间Session同步,存在失败的新的问题
4.IP绑定策略,使用Nginx进行IP绑定策略,同一个IP只能访问指定机器访问,这样失去了负载均衡的意义,降低了容错性,如果宕机。
5.使用Redis缓冲中间件 Spring提供Redis存入(最广泛)
服务器重启Session不丢失
可以跨服务器,还可以跨平台 ,跨语言 如网页端和App端,
RPC与RMI理解
Dubbo-RDC协议的火爆引出
RPC 调用远程函数,调用不同节点的方法,面向接口调用,使用了HttpClient工具,对RPC远程过程调用的实现,支持的语言多。http 。 utp 。tcp
Double基于TCP协议效率更高
RMI 远程方法调用 是RPC的一种机制,必须两个节点必须是Java JVM运行 ,面向对象思维。远程类调用 java.rmi.Remote接口,只要实现就可以远程对象被标记和UniCastRemoteObject继承宝成可以把自身的拷贝以Socket形式传输给客户端,拷贝存根(代理类)保存,通过客户端的存根调用方法,就相当于本地调用了。
需要启动注册表 JVM jdk.bin.rejastr.exe> 启动服务进行注册
分布式Id生成策略
uuid 时间戳+计数器+全局唯一IEEE机器识别号
简单 唯一
无序无法用于聚簇索引
写操作会导致页分裂
过长存储消耗大
无业务含义,可读性差
泄漏mac地址风险
数据库自增
简单 连续 可读
分布式步长间隔大于节点数不冲突
生成需要读/写操作,性能消耗
主从同步时,写入主库,单从库读取不到
Leaf-Segment(美团)
分段分配区域id
biz_tag业务
max_id最大
step 分词分配区间长度
双buffer保证安全
跨度固定,业务剧增 区域不够问题
跨度过大,空号过多
redis、mongodb、zk中间件
雪花算法
1符号占位0 41时间戳10workId机器房码+机器码 12序列号
毫秒生成支持量大,如果不懂可以变动位数
时间戳序趋势增加,又正好在低位
灵活调整bit位
存在始终回拨问题,调整节点时间,出现生成重复ID,如果发现始终回拨组织ID生成,抛出异常
分布锁解决方案
db数据库 逻辑字段 数据库非阻塞 不可重入业务获取两次 失效时间
key 未删宕机 未解锁导致死锁
zk 通过临时节点Znode,解决死锁问题,文件夹无法创建相同文件名的文件,通过监听机制,如果获取锁宕机Session断开(监听),那么临时节点就会删除,释放锁解决惊群效应。通过临时节点唤醒一个线程,无需唤醒全部线程
redis 使用setNx + EP 如果存在就返回false 否则创建并有超时时间 并且单线程不需要考虑并发问题 单线程使用NIO分阻塞 (并不是一个线程)仅处理网络请求时是一个线程。通过hash key 的特性,并且存入线程唯一标识,只有当前线程允许释放这一把锁。 key过期策略将会出现不同问题:定期被删除,value加入ep 通过get setnp 时间比对,使用lua脚本保证事务
通过Redission客户单 API解决锁续期的问题
RedLock机制使用Redission是java版实现,从多个节点申请锁。如果超过一半可以获取成功
数据同步,锁丢失情况,主从同步,一致性问题,通过redLock红锁,在多节点申请锁。一半以上即可。
分布式事务
1. 一次请求,A操作D1D2数据库,请求多个数据库 Spring无法保证 使用JTA 是单机XA协议接口操作 一个服务两个数据源
2. 一次请求,A操作D1数据库/B操作D2数据库 两个服务两个数据源
如A转账 B加钱
-
一个XA规范(核心)定义抽象模型
四个角色
事务管理器(协调者TM)
资源管理者(参与者RM)
应用程序AP
通信资源管理器CRM
一个跨服务的事务(事务管理器TM)
tx_id
两阶段协议
RM一阶段仅执行不提交,并上报TM
二阶段:如果TM收到全部确认则通知commit,否则(未执行成功,或宕机,异常)通知rollback
问题
单点故障,参与者会阻塞住
网络抖动,二阶段提交不一致
相应时间较长,等待先执行,后提交
不确定性
三阶段协议(解决3PC单点故障问题)
将一阶段拆分为两阶段
一阶段 : canCommit 先判断全部节点环境(加了判断)
二阶段 :precommit 执行sql,但不提交
三阶段:TM收到全部RM进行提交或回滚
依然存在
网络抖动,二阶段提交不一致
相应时间较长,等待先执行,后提交
不确定性
TCC补偿事务 Try Confirm Cancel
try检测资源,简则数据合法性
confirm/cannel (非数据库commit,canncel,业务自定义提交回滚方案)
confirm成功可以什么都不做,cancel可以加钱
TCC模型需要对自己编辑,通过代码保证,代码侵入性强
与数据库无关系,通过业务层控制,不通过数据库控制
消息队列MQ事务
A发送prepare消息到消息中间件
A正在执行B暂时不可见,B可能最终无法消费到,或消费失败,进行重试机制
或延迟队列A先执行完B才可见
回滚发送MQ进行B回滚消息
接口消息的保证幂等性
重复点击,多次请求,插入操作INSERT,造成多份数据影响
DQL是幂等性的,与数据变化无关系
而是通过人为导致数据逻辑不健壮,重复出现
唯一ID 电话号码,身份证 但会对数据库进行穿透
token标记 如果有标记则不进行拒绝,方式数据库压力
去重处理
版本控制:用于连续update 加入版本号乐观锁正确才能更新成功
状态控制:前置后续状态:通过状态转换: 未支付 已支付 支付失败 最总状态将无法修改
zK ZAB协议
崩溃恢复的原子广播协议,保证数据AP数据一致性
ZK节点分为Leader节点,同步到Follower节点,如果Leader阶段或Follower阶段同步宕机是,通过ZAB保证数据局同步问题保证数据一致性
ZAB模式 崩溃恢复 、消息广播
Leader节点 写操作 以及书屋处理,将客户端事务请求转换为事务Proposal,将Proposal分发给集群全部Follower。完成广播,收到Follwer一半以上的反馈。Leader会想Follower广播Commit信息,Commit信息就是确认之前Proposal提交,像2PC
第一步Leader广播事务,第二部广播提交操作。过半+1指的是反馈节点>=N/2+1 N是全部Follower节点数量
崩溃恢复
用于
初始化集群,刚刚启动
Leader崩溃,故障宕机
Leader失去板书机器支持(Followe节点一半+1断开)
进行Leader选举,产生新的Leader会与Follower进行同步,是数据一致,当与过半机器当成一致同步完成,退出恢复模式,进行消息广播模式
崩溃恢复阶段 进行 不可用状态 保证CP强一致性
重新选举通过
Zxid是Zab协议的事务编号(选举编号)64位(低32位是单调递增计数器,每次事务+1,高32位代表Leader周期年代编号) + Leader 周期 epoch 周期年代,选举新的Loader就会读取日志中最大的Zxid中epoch值然后+1
低32位事务唯一性。高32位保证Leader唯一性,
ZAB节点三种状态
Following : 服从Leader的命令
Leading: 负责协调事务
Election/looking:选举状态
Zk数据迷行和节点类型
多叉树形结构:Znode 兼具文件或文件夹的特性,可以存贮数据,也可以做路径标识。树进行日志快照记性保存。Znode保证原子性操作。读操作一次性获取所有相关数据,写操作覆盖所有数据看,每个Znode都有自己的ACL访问控制列表,控制用户权限
Znode 1M ZK设计目的做注册中心,维护一些配置数据,减少带款
Znode路径并且通过/zookeeper保存没有绝对路径的概念
Znode类型 持久节点 /临时节点 拼接有序节点 四种拼法
持久节点一旦创建,即使断开服务也保存节点信息
临时节点,一旦超时或异常关闭,则会被zk删除
分布式锁:通过Node节点临时节点可以用作锁删除,当发生异常关闭则会被删除
Zk功能: 命名服务 配置管理 集群管理
通过url+注册中心就可以获取唯一的服务位置,通过url服务名进行接口调用
配置管理,分布式固定配置通过,zk,watcher机制通知客户端
集群管理:集群监控和集群控制,通过watcher机制断开则剔除
Zk watch机制
客户端在Znode设置watch监听器监听变化
Znode父子节点发生变化将会触发Watcher事件,并且是一次性的触发器,一次次触发watch监听器就会被移除,可以修改触发多次
一次性可以减轻压力,重新注册watch,一些监听
轻量级:只通知事件,单不存具体内容,服务器自己进行查询详情,减服务器带宽压力
watcher三个角色
客户端线程
客户端watchmanger
zk服务器
zk服务器注册watch监听
监听信息存入watcherManger
当节点发生变化,客户端会调用watcher回调方法。进入队列穿行删除
zk和eureka的区别(区别两种CAP理论的实现机制)
集群节点发生网络,CA将出现矛盾,一致性和可用性进行二选一
Zk强一致性,做分布式协调系统,资源统一管理
通过Leader选举进入崩溃恢复期,此时暂时不可用
eureka AP 高可用,没有主从,每个节点都是平等的,所以节点宕机互不印象,Eureka客户端通过Eureka注册失败会注册到其他Eureka节点上,保证可用用,单数据可能不是最新的
Eureka发现85服务没有心跳,它就会认为自己的网络出现了问题,
就不会从服务列表中删除失去心跳的节点,同时eureka会缓存服务信息。
SpringCloud 迟 和Dubbo 早 的区别
底层协议SpringCloud基于Http协议,Dubbo基于(RPC框架)TCP协议实现,Dubbo性能相对比较好,SpringCloud可以选择GRPC
注册中心方面:Eureka 和Duboo推荐 Zookper的区别 CAP理论区别
模型定义:Dubbo 一个接口一个服务 SpringCloud 一个应用一个服务
Dubbo是分布式
SpringCloud粒度更细 应用级别 微服务
SpringCloud是一个生态
Dubbo仅能比对SpringCloud生态中的一种解决方案(服务治理)
Hystrix 分布式容错框架
阻止故障连锁反应,实现熔断
快速失败,服务降级
提供图形化监控和报警
资源隔离 :线程隔离 信号量隔离
线程隔离分配command线程池 舱壁模式 不会影响其他线程池
信号量隔离:当限号量打满进入fallback流程,进行限流和雪崩
HystrixCommand 或者 HystrixObservableCommand
阈值 打开断路器
SpringCloud核心组件
Eureka 注册(剔除)与发现 集群节点相互注册 只要存货一个节点就可以提供服务
发现 通过服务名可以到注册中心咨询,还会将服务实例清单缓存到本地
实例清单是相同服务名多个实例,如何做负载均衡
Ribbon 负载均衡
通过http服务名进行调用,通过负载均衡算法确认实例,但发送http请求需要显示执行
Feign 远程调用
基于AOP动态代理(就像mybatis注入接口即可找到实现),使远程调用变得和本地调用无很大的差距,隐式执行http请求,
Hystrix 服务容错保护
链路调用,会出现并发量扇出,造成链路压垮,对某链路进行断路保护
单独生成一个线程池,舱壁模式,实现服务调用隔离性,设定时间窗对接口统计返回默认值,实现熔断和降级,以及友好的相应,或存入MQ中,或记录日志
Zuul 网关
节点实际访问隐藏,且前台直接调用后台,前台未在Eureka注册中心注册,是无法实现注册与发现功能的,所以无法进行Rebbon+Fegin的功能无法进行代理,所以需要通过Eureka中的节点Zuul网关进行路由拦截并转发
(服务和服务也可以走网关)
Dubbo整体架构设计以及分层(国内架构清晰)
五个角色
注册中心Registry 注册与发现 (订阅与推送)
监控中心monitor 统计服务调用次数和调用时间 (接收)
服务提供者provider 暴露服务
服务消费者consumer 调用远程服务
容器container 服务允许容器,启动时加载Provider提供的服务集合
调用流程
container容器负责启动、加载、运行provider
provider在启动时,向regisitery中心注册自己提供的服务
consumer启动时,向regisitry中心订阅自己所需的服务
registry返回服务提供者列表给conusmer,如果有变动,registry将基于长链接推送变更数据给consumer
consumer调用provider服务,基于负载均衡算法调用
consumer嗲用provider的统计,基于短连接定时每分钟统计一次到monitor生成监控报告
分层
接口服务层Service 业务
配置层Config zk node 监控器 watcher监控管理器 并通知 更新
服务代理层Proxy 无感知进行http请求
服务注册层Registry 通过url发现服务
路由层Cluster 路由和负载均衡,并桥接注册中心
监控层Monitor RPC嗲用次数和调用时间架空
远程调用者Protocal 封装RPC TCP调用细节
信息交换层Exchange 封装请求相应模式,同步转异步
网络传输层Transport 抽象mina和netty为统一接口,统一网络传输接口,无感知
数据序列化层Serialize 数据传输序列化和反序列化
但是没有解决服务熔断和降级,以及事务
RabbitMQ架构设计(消息可靠性 优先)
角色
producer broker consumer
exchange routing key Binding queue
Queue 由priducer创建的RabbitMq内部对象发送到Exchange交换机
Consumer通过routingkey即可获取Queue
Exchange priducer发送的消息发送至交换机,投递由Exchange进行决定路由到哪些Queue或多个Queue,如果路由不到,丢弃或做其他处理
RoutingKey 特殊交换机需要符合routingkey的规则才能从交换机绑定到指定的Queue中
Binding 广播交换机直接Binding到队列中,忽略路由键。 topic则需要通过路由top
将交换机和队列是多对多的关系,就像是两张关系表,通过BindingKey关联。
Channel信道:建立在Connection之上的虚拟连接,应用程序与RabittBroker建立TCP连接,客户端紧接着创建一个AMQP信道(Channel),每个信道都会有一个唯一ID。RabbitMQ处理没掉AMQP指令都是通过信道完成。信道就像电缆里的光纤。无需TCP重复建立连接,进行信道类似于NIO进行长链接。
一条电联含有多光纤束,允许多个连接通过多条光纤束进行传输和接收
RabbitMQ如何保证消息发送? 消息接收? (MQ的两种确认机制)
发送放确认机制
信道Channerl可以设为Confirm模式,则会在每一条消息上分配一个唯一ID
一旦消息被投递到queue(如果是可持久化消息,还要写入磁盘),channerl发送一个确认给生产者 包含消息唯一ID
如果RabbitMq发送内部错误从而导致消息丢失,则会发送一条nack 未确认 消息给生产者
所有被发送的消息都将被confirm ack或者nack一次。但是没有对消息被confirm的快慢做除保证,并且同一条消息不会既被confirm又被nack
发送放确认模式是异步的,生产者应用程序在等待确认的同时,还可以继续发生功能消息。发确认回馈到达生产者则触发回调方法。
ConfirmCallback接口:值确认是否到达Exchange中,成功则回调
ReturnCallback接口 消息失败返回时回调
方法肯定会被调用 二选一
接收方确认机制
消费者声明队列时,
可以指定noack参数,当noack=false是。
默认自动提交rabbitmq消费者显示ack信号后才从内存(或者持久化保存信息)中或移除消息。
否则消费成功则会立即删除,所以选择程序手动移除消息,保证生产者的逻辑成功执行,如同一组事务。
如:如Spring的方法可能会方法回滚,使用手动提交保证消息
false手动提交的问题:
消费者接收每一条消息后都必须进行确认
(消息接收和消息确认是两个步骤操作)可能接收了但暂时没有确认
如果不返回ACk则无法重新发送消息(死等)不处理完不允许继续发
(abbitmq不会为ack的消息设置超时时间,它判断此消息是否需重新投递给消费者的唯一依据是消费的消息者连接是否已经断开。)
做到限流,如果无法返回ack,表明前面的消息没有执行完。
RabbitMQ允许消费一套时间可以很长。保证数据E最终一致性。
如果消费者返回ack却发生了断开了Channel长链接,RabbitMQ则会重新将消息发送给消费者。可能存在数据重复消费的隐患,需要去重,保证接口幂等性。通过唯一ID进行判断。保证最总一致性。
区别:连续发消息,生产方无需消确认,生产方必须获得消费方接收消费并确认
RabbitMQ 事务消息 ACID(特殊场景使用)
通过消费者确认机制无法达成、回调方法无法完成
通过信道chnnel
channel.txSelect() 通知服务器开启事务模式,服务端返回Tx.Select-OK
channel。basicPublish 发送消息,可以是多条,可以是消费消息提交ack
channel.txcommit提交书屋 /channel.txRollback回滚事务
注意:
autoACk=true 事务将失效,消息队列会消除消息
autoACk=false 手动提交ack,不以单词ack为准,以总体的事务提交或回滚为准。
如果期间出现问题则会抛出IOException异常,可以拦截异常进行事务回滚,或者决定重复发送消息
事务消息会降低RabbitMq的性能,但有使用场景,如果首选建议confirm模式,事务模式chnnel将于服务端多次连接
死信队列 延时队列
死信队列:延迟消息生命周期
如果消息被消费方否定确定,返回nack或basicreject 失败或拒绝。并且队列的属性别默认设置为false
消息队列存活时间超过了TTL time to love
消息队列的消息已满超过最大队列长度
如果配置DLX信息则会进入死新队列(默认存在,也是普通的队列)
配置死新交换机(普通交换机)只为Bingding,通过routingkey确定如何进入死新交换机
监听死新队列任务
延时消息:定时任务
通过建立 TTL队列>死信队列 如果消息超时到期,则会进入死信队列被消费 ,如果消息设置了TTL,消息队列TTL,则会最小优先
Kafka(Ali RocketMQ借鉴技术)
可靠性可能会差,但是并发量大
角色
Producer
Kafka Cluster(多个Broker)
Consumer
Consumer Group 消费者组,消费者组内每个消费者负责消费不同分区Partition的数据,提高消费能力。逻辑上的一个订阅者(负载均衡同一Topic同一只Partition能有一个消费者消费)。
Topic:可以理解为队列,Topic将消息分类,生产者和消费者面向的是同一个Topic
(Topic海量数据队列,拆分为Partition保证有序,且Partition保证副本区Replica存在主从关系,主要操作一个主分区,同步到多个从分区,保证容错性。且Topci拆分模型将会同步在多个Broker上)
Partition:提高并发能力,一个Topic以多个Partition划分的方式分布到多个Broker上,每个Partition是一个有序的队列。一个Topic的每个Partition都有若干个副本(Replica),一个Leader和若干个Follwer。生产者发送数据的对象。以及消费者消费数据的对象,都是Leader。Follower负责实时从Leader中同步数据,保证和Leader数据同步。Leader发送故障时,某个Follower将成为新的Leader
Offset 消费者消费的位置信息,标记架空数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。
Zookeeper:Kafka集群的正常工作,依赖Zooleeper注册中,Zookeeper帮助Kafka存贮和管理集群信息(检测Broker服务器,Producer可以通过zk感知Broker,Topic和partition关系 以及partition中leader和foolower的关系也存储在zk中,以及消费者组消费哪些partiton分区,也会通过一个消费者只能消费一个partiton保证,以及剔除重新分配,Leader重新选举。Offset偏移量也要上报zk)
Kafka消息丢失 发送 broker存储 消费
1.ack=0 不重试不确认 ,仅发送消息,就不管结果了,效率高
2.ack=1 , leader crash
等待lead节点写如成功,但是leadercarash宕机没有follower同步失败,数据丢失
unclean.leader.election.enable配置为true
选举ISR列表以外的副本作为Leader数据是同步的,否则被Zk删除,ISR以外的就是OSR,会导致数据丢失
默认为false。producer发送异步消息完,只等待lead写入成功就返回,leader crash是,这是ISR中没有follower,leader从OSR列表中玄机,因为OSR中本来落后于Leader造成数据丢失
解决方案
ack = all/-1 或 更大的数字 / tries重试 > 1,uncleanleaderelection。enable:false
producer等待follower同步完在返回,否则异常
不允许ISR以外副本进行选举
all = 同步一致的进入ISR节点
min.insync.replicas > 1
ISR同步副本必须大于一 保证最小同步数量,越大消息越可靠,保证持久性。如果不能满足抛出NotEnoughReplicas没有足够的副本异常
配合使用确保大部分副本没有收到则抛出异常
失败的offset单独记录
producer发送消息,会自动重试,遇到不可恢复异常则抛出异常。这时候可以捕捉异常记录日志到数据库或缓存,然后进行单独处理
消费者
先commit offset在处理消息。如果在处理消息的时候异常了,
但是offset已经提交了,这条消息对于消费者来说就是丢失了。再也不会消费掉
先处理消息再 commit offset 执行后没有commit就会重复消费
消费者主要解决重复消费的问题
保证接口的幂等性
broker Linux > page缓存 > 刷入硬盘
提高刷盘频率,保证存入Page页存+ISR副本变量
会影响性能
Kafka是pull?push? (Broker与Comsumer的关系,不同消息中间件不同)
pull 消费者主动拉取,消费根据能力拉取数据,凭力拉取,可以控制速率
可以批量拉取,也可以单条拉取
可以设置不同的提交方式,可以手动提交offset,可以执行完后提交offset,实现不同传输语义:至少一次 最多一次 精准一次
缺点 :消费者需要主动拉取 如果没有消息 comsumer空轮询
解决:通过配置参数,consumer拉取必须指定数据或空则进行阻塞释放CPU资源
push:不存咋consumer循环等待
缺点:速率固定,导致comsumer无法消费,消息堆积拒绝
Kafka根据Conusmer能力 pull 拉取
Kafka中zk的作用(旧版本使用zk,新版已替换)
/brokers/ids: 临时节点,保存所有broker节点信息,存储broker的物理地址、信息、节点名称BrokerID,通过定时发送心跳机制到zk,如果断开则被剔除,对producer可见
/brokers/topics:临时节点,节点保存broker节点下所有topic信息,每个topic节点包含一个固定partitions节点,partitions的子节点就是topic的分区,每个分区下保存state节点,保存当前leader分区和ISR选举可靠列表(仅存在于Leader节点保持一致的节点)的从节点的brokerID。state节点由leader所在的broker节点创建,leader宕机state(与当前leader共生存)节点就被删除,知道新的leader选举产生,重新生成state节点
/coonusmers/ 节点维护group中消费者和分区的注册关系
(如果没有对应消费分区被消费则会以出现问题)
/coonusmers/ 消息分区的Offset进度
(客户端通过topic>broker)
client通过topic找到topic的state树下state节点(核心),找到leader的brokerID,到broker树种找到broker物理地址,但是client对zk直接连接,通过broker的地址通过broker地址获取zk信息
Kafka的高性能问题
(高并发高吞吐是架构方面)
(读写也非常快)(考知识点:顺序写/0拷贝)
Kafka是个文件系统(适合做日志)通过数据进入 页存文件 >然后硬盘存储,消息堆积大 (硬盘按理说速度慢,实际磁盘按照指针的磁头的磁道进行寻址然后进行读写。Kafka不需要寻址,顺序读写访问速度接近内存,Kafka是appnend追加操作,partition分区有序,节省了磁盘寻道时间,并且通过一定的缓存批量操作节省写入次数。partition物理上分为多个segment存储,方便删除,文件会产生垃圾疷,segment直接删)
(大数据HBase也是基于顺序写)
传统
操作系统规则 > 用户线程 > 内核线程 > 硬盘/网卡 (保证安全中)
读取时,
需要将数据写入内核buffer缓冲区
进行CPU内核态转入用户态copy写入用户Buffer缓冲区
转为内核态copy到Socker buffer发送缓冲区 转入 网卡
将socket发送缓冲区通过网卡进行传输
零拷贝
直接将内核缓冲区的数据发送到网卡传输,节省三步
使用操作系统指令支持,依赖操作系统
节省数据迁移,和内核态和用户态转换
Kafka虽然部分java开发,但不太依赖JVM(会部分使用),因为堆是64位最大32G,Kafka存储数据不去使用,主要有操作系统的PageCache页缓存,通过Linux系统决定。如果生产者和消费者速率相当,直接通过pageCache交换数据,不需要经过磁盘IO
Kafka rebalance (日志提示:最好避免重新平衡)
consumer group 中 conusmer 会对应 topic partion分区 ,但是P 和 C 是一对一 或 一多多(仅一关系)
触发重平衡rebalance
conusumer gropup 成员变化
consumer消费超时(可以设置阈值)
group订阅的topic个数变化
gropu订阅toipic的partion个数发送生变化
进行数据印象是,人工选择低访问量时
coordinator 协调者:是partition的leader节点所在的broker ,broker是协调者,负责监控空groupu只用consumer中存货,consumer位置心跳到coordinator,判断是否心跳超时或消费超时
coordinator通知心跳返回通知consumer进行rebance状态
conusmer请求coordinator加入组,请求的可以判断为存货的,然后选举leader consumer(只是标记一个)
leaderconsumer从coordinator获取所有consumer,发送syncGroup(分配信息)给coordinator
coordinator通过心跳机制将syncGroup下发给consumer
完成rebalance
(broker节点无法topic和partition)
leader consumer监控topic变化,通知coordinator触发rebalance
如果C1消费者超时触发rebalance,重新分配,但消息可能会重发到其他消费,此时C2进行消费,c2执行完,c1也执行完offset导致错误
解决:rabalance发生的错误,coordinaotr每次rebalance,会标记一个连续的数字Generation代给consumer,酶促rebalance该Generation+1,consumer提交offset是,coordinator会对比Generation,不一致则拒绝提交,旧版本的Generation会被拒绝