- 简述分布式锁的几种实现方案
三种实现方式
数据库
通过唯一索引,做排他锁(唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功)
缺点:
锁强依赖数据库,挂掉导致业务系统不可用
锁不可重入
锁没有失效
redis
redis加锁实现, SET key random_value NX PX 5000
值得注意的是:
random_value 是客户端生成的唯一的字符串
NX 代表只在键不存在时,才对键进行设置操作
PX 5000 设置键的过期时间为5000毫秒
缺点:
集群环境中也会出现,锁失效情况
zookeeper
在Zookeeper当中创建一个持久节点lock。当线程A想要获得锁时,需要在lock这个节点下面创建一个临时顺序节点 Lock1
之后,线程A查找lock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁
线程B继续上一个步奏,线程B向排序仅比它靠前的节点Lock1注册Watchers,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态,一次类推就形成一个后面监听前面的队列
缺点:
创建删除节点性能不是很高;
网络抖动也会出现琐失效
- 对外提供的接口如何保证幂等?
token机制
服务端提供了发送token的接口。在必须在执行业务前,先去获取token,服务器会把token保存到redis中。(微服务肯定是分布式了,如果单机就适用jvm缓存)。
把token携带过去,一般放在请求头部。
服务器判断token是否存在redis中,存在表示第一次请求,这时把redis中的token删除,继续执行业务。
如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。
数据库去重表
往去重表里插入数据的时候,利用数据库的唯一索引特性,保证唯一的逻辑
分布式锁
- 简述一下秒杀系统的设计思路
秒杀的痛点
并发读(库存,前端静态资源),并发写(减库存),以及系统的高可用
激增的流量,让他趋于平缓,把流量挡在系统上层,保护系统稳定
客户端限流(前端提交按钮变灰),
服务端限流(nginx限流,服务端限流最大请求数),
降级(确保系统挂后,防止系统雪崩),
队列削峰(逻辑分布式缓存库存信息,请求异步返回,服务层一步返回)
https://www.jianshu.com/p/60319f5f4167
- JVM堆内存溢出之后,其它线程是否能继续工作? java中什么样的对象能够进入老年代,GC的过程 什么情况下发生fullGC,怎么避免发生fullGc
能,溢出之前至少要发生一次full gc
https://www.jb51.net/article/167945.htm
超过年龄阀值对象(默认15)
动态年龄判断,当s区的对象大小大于s区的50%,直接进入老年代
大对象直接进入老年代
显示调用system.gc()
老年代空间不足
方法区空间不足
- left join查询时,左右表过滤分别采用什么方式比较合理? Mysql innodb索引为什么不用hash、 二叉、 红黑 数据结构而采用B+Tree
A Left Join B on (…)on 后面的条件是对B数据的过滤,
如果要对A的数据或者联合之后的数据集进行过滤,则要把过滤条件放在where子句中
hash:查找快,但是不适合范围查找
有序数组:查找和范围查找都很快,但是插入就需要移动之后的所有数据
二叉树:二分查找法,会有左倾或者右倾的情况,且不适合做范围查询
平衡二叉树:避免的左倾和右倾,但是数据量大的时候,树高会很高,也就是IO次数会很多
B-Tree:相比平衡二叉树,树高是降低了,但是还是不适合范围查询,范围查询需要遍历所有数据
B+Tree:将所有数据都放到叶子节点,且叶子节点形成一个列表(可以做范围查询),非叶子节点只放键值,每个数据叶中的有效数据就多了,可以减少IO次数
https://blog.csdn.net/weixin_39428938/article/details/77944939
https://blog.csdn.net/weichi7549/article/details/108179677
- 消息队列如何做到消息幂等性?
去重表
分布式锁
- 简述一下API接口限流的思路
Nginx前端限流
按照一定的规则如帐号、IP、系统调用逻辑等在Nginx层面做限流
常见的限流算法有:
计数器 qps时间范围内清空
令牌桶
漏桶
https://www.cnblogs.com/exceptioneye/p/4783904.html
- 尽可能多的列举出你所知道的Redis使用场景和用到的数据结构
https://www.jianshu.com/p/40dbc78711c8
热数据缓存
限时业务的运用
计数器
排行榜
分布式锁
延时操作
队列
string list hash set sorted set
- 如何保证缓存与数据库的双写一致性?
Cache Aside Pattern
最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。更新的时候,先更新数据库,然后再删除缓存
很多种策略,双写,双删除
https://www.cnblogs.com/semi-sub/p/13735800.html
- 缓存穿透、缓存击穿、缓存雪崩区别和解决方案
缓存穿透:
是指缓存和数据库中都没有的数据,而用户不断发起请求,导致数据库压力过大。
解决方案:
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
设置热点数据永远不过期。
互斥锁
缓存雪崩
是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期
- 简述一下Spring AOP与AspectJ的不同之处
静态 AOP 实现:
AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类 AspectJ
动态 AOP 实现:
AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP
而 Spring AOP 不能拦截对对象字段的修改,也不支持构造器连接点,我们无法在 Bean 创建时应用通知
https://blog.csdn.net/qq_41981107/article/details/87920537
https://www.jianshu.com/p/872d3dbdc2ca
Spring Aop采用的动态织入,而Aspectj是静态织入。
静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。
动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行
从使用对象不同:
Spring AOP的通知是基于该对象是SpringBean对象才可以,而AspectJ可以在任何Java对象上应用通知
AspectJ可以对类的成员变量,方法进行拦截
- 列举一下MyBatis中使用到的设计模式及相关类名
mybatis流程
启动通过XmlConfigBuilder,SqlSessionFactoryBuilder(建造者模式)加载的配置文件(mybatis-config.xml,mapper.xml)
XmlConfigBuilder.parse()构建Configure对象实例,并且SqlSessionFactoryBuilder构建SqlSessionFactory.open(); 创建SQLSession
通过sqlsession获取到以及configuration获取到mapper代理对象,代理通过Executor执行sql,StatementHandler ,ParameterHandler ResultSetHandler
- ElasticSearch中文与拼音的搜索联想如何实现?
Elasticsearch使用一种叫做倒排索引(inverted index)的结构来做快速的全文搜索。倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成,正序索引是一个索引对应一个文档字段
使用elasticsearch实现的搜索联想就是通过分词器进行分词 生成tokens,然后通过倒排索引的方式来搜索出所在的文档,然后会显回来
https://blog.csdn.net/denggouya9281/article/details/101223999
- Thread类中start()和run()方法有什么区别? 在并发情况下,Elasticsearch如果保证读写一致? Elasticsearch是如何实现Master选举的?怎么避免脑裂问题?
start()方法来启动一个线程,JVM通过调用线程类的run()方法来完成实际的业务逻辑,当run()方法结束后,此线程就会终止,所以通过start()方法可以达到多线程(主线程开辟另一个独立线程)的目的,异步调用
直接调用线程类的run()方法,会被当做一个普通的函数调用,同步调用,完成之后才能调用接着执行其他方法
读写操作
https://www.cnblogs.com/duanqibo/articles/12686420.html
选举和脑裂
https://blog.csdn.net/u012270682/article/details/106262379
- Java如何指定多个线程的执行顺序?
join方式实现
使用同步块和wait、notify的方法控制
三个线程的执行次序为了控制执行的顺序,必须要先持有prev锁(也就前一个线程要释放其自身对象锁),然后当前线程再申请自己对象锁,两者兼备时打印。
之后首先调用self.notify()唤醒下一个等待线程(注意notify不会立即释放对象锁,只有等到同步块代码执行完毕后才会释放),再调用prev.wait()立即释放prev对象锁,
当前线程进入休眠,等待其他线程的notify操作再次唤醒
ReentrantLock
ReentrantLock搭配的通行方式是Condition
https://blog.csdn.net/qq_29882585/article/details/108567964
- 简述一下Spring的Bean生命周期
Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
Bean实例化后对将Bean的引入和值注入到Bean的属性中
如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
- 微服务架构中使用统一配置中心有什么优缺点?列举一下你所了解的相关产品
spring-config
nacos
zookeeper
配置自动发布到应用
配置支持版本控制
支持不同环境隔离部署
问题
需保证高性能、高可用
- SQL优化的一般步骤,(追问SQL索引失效的一些情况)
通过 show status 命令了解各种sql的执行频率(crud)
定位执行效率较低的sql语句
通过explain分析低效sql的执行计划
通过 show profile 分析sql
通过trace分析 优化器 如何选择执行计划
确定问题并采取相应的优化措施
SQL索引失效的一些情况
联合索引,没有遵循左前缀原则
is null 有时候不走索引
索引字段进行在操作运算
like以%开头
索引列隐式转换
or语句前后没有同时使用索引。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效
- 现有一个需要关联7张表的复杂查询,该如何优化?
关联键
可适当设计冗余字段
切分查询
可将单条的多表关联查询分解为多条查询,对每一个表进行一次单表查询,然后将结果在应用程序中进行关联
查询缓存
或者job视图
- 简述一下Spring Boot配置的加载顺序
优先级按照顺序由高到低,数字越小优先级越高
1.在命令行中传入的参数。类似于java -jar -Dspring.profiles.active之类。
2.SPRING_APPLICATION_JSON属性,该属性以JSON形式存储在系统环境变量中。
3.java:comp/env中JNDI属性。
4.Java的系统的属性,可通过System.getProperties()获得相关内容。
5.操作系统中的环境变量。
6.通过random.*配置的随机属性。
7.位于当前应用jar包外,针对不同{profile}环境的配置文件内容。
8.位于当前应用jar包内,针对不同{profile}环境的配置文件内容。
9.位于当前应用jar包外的application.properties或application.yml配置内容。
10.位于当前应用jar包内的application.properties或application.yml配置内容。
11.在@Configuration注解修改的类中,通过@PropertySource注解定义的属性。
12.应用默认属性,使用SpringApplication.setDefaultProperties定义的属性内容。
- 现有Spring Boot 1.X的微服务需要迁移到2.X,需要注意哪些方面的坑?
一些配置类,有jdk版本
- 微服务架构如何保证安全性
身份验证; 访问授权; 安全的进程间通信
- mvn install和mvn package的区别
区别是maven package只是把包打在自己的项目下。
maven install会把包打在maven本地仓库下,可以给依赖它的其他项目调用,并自动建立关联
- spring boot启动做的事情
- spring cloud starter是通过什么机制实现的
工厂加载机制(Factory Loading Mechanism),自动装配,meta-info spring.factory
28技术选型和比较
29ddd和解耦
30常用设计模式,设计模式思想和在项目中的使用
https://blog.csdn.net/qq_38024548/article/details/80480831
31各种中间件原理和核心源代码
32线程安全
33 gc过程及gc算法
gc新生代的氛围eden 区 s1 s2 区,s1 和s2发生from-to交换,就是所谓的minor gc ,
如果年龄对象大于15进入老年代,如果新生代的对象大于to区,直接进入老年代,老年代满了会发生一次full gc
https://www.jianshu.com/p/57c0710c4969
标记清除 碎片
标记压缩 压缩费时间
可达性分析算法 gc roots
引用计数 循环引用
复制算法 空间效率低
34线程池的使用&解决线程安全问题的办法及原理
https://blog.csdn.net/wolf909867753/article/details/77500625/
https://www.cnblogs.com/zincredible/p/10984459.html
常见线程池
①newSingleThreadExecutor 单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n) 固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)可缓存线程池, 当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor 大小无限制的线程池,支持定时和周期性的执行线程
线程安全问题解决
https://www.cnblogs.com/aspirant/p/8657681.html
决多线程的并发安全问题,java无非就是加锁,具体就是两个方法
(1) Synchronized(java自带的关键字,同步互斥锁)
(2) lock 可重入锁 (可重入锁这个包java.util.concurrent.locks 底下有两个接口,分别对应两个类实现了这个两个接口:
(a)lock接口, 实现的类为:ReentrantLock类 可重入锁;
(b)readwritelock接口,实现类为:ReentrantReadWriteLock 读写锁)
也就是说有三种:
(1)synchronized 是互斥锁;
(2)ReentrantLock 顾名思义 :可重入锁
(3)ReentrantReadWriteLock :读写锁
35 Spring事务的原理,失效场景 Spring事务的原理,失效场景?为什么会失效
事务原理
https://blog.csdn.net/pengdeman/article/details/98507934
配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法
并且为这些类和方法生成代理,代理对象通过拦截器 TransactionInterceptor 来使用拦截在逻辑处理之前添加事务,利用事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource
提交或回滚事务,真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
失效场景 https://www.cnblogs.com/javastack/p/12160464.html
数据库引擎不支持事务
没有被 Spring 管理
方法不是 public 的
自身调用问题
数据源没有配置事务管理器
异常被吃了
异常类型错误
36 多线程CPU处理方式
37 现场说一个场景,让设计架构和表,如何应对高并发
38 架构规划,为什么这样设计,扩展性,安全性等如何,具体体现在哪里
tcp粘包
https://www.cnblogs.com/kex1n/p/6502002.html
Mysql innodb索引,
mysql数据量超过千万级的操作,
redis分布式锁,数据类型以及各个数据类型的应用场景,
缓存雪崩,缓存穿透,缓存击穿的解决方案,
spring 事务,aop,
bean加载过程,
jvm内存模型,
jvm调优经验。
多线程交替打印ABC,
多线程生产者、消费者。
Spring cloud微服务架构。消息队列等。
tcp粘包:如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包,发送方TCP默认使用Nagle算法接收方:CP接收到数据包缓存
(1)发送方
对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
(2)接收方
接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。
(2)应用层
应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。
mysql的数据结构:
顺序查找: 最基本的查询算法-复杂度O(n),大数据量此算法效率糟糕。二叉树查找(binary tree search): O(log2n),数据本身的组织结构不可能完全满足各种数据结构。hash索引 无法满足范围查找。哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。二叉树、红黑树 [复杂度O(h)]导致树高度非常高(平衡二叉树一个节点只能有左子树和右子树),逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,IO次数多查找慢,效率低。
todo 逻辑上相邻节点没法直接通过顺序指针关联,可能需要迭代回到上层节点重复向下遍历找到对应节点,效率低B-TREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。
检索原理:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回null指针。
缺点:1.插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。造成IO操作频繁。
2.区间查找可能需要返回上层节点重复遍历,IO操作繁琐。B+Tree: B-Tree的变种
与B-Tree相比,B+Tree有以下不同点:非叶子节点不存储data,只存储索引key;只有叶子节点才存储data
Mysql中B+Tree:在经典B+Tree的基础上进行了优化,增加了顺序访问指针。
在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。这样就提高了区间访问性能
Redis有哪些数据类型:
redis主要有5种数据类型,包括String,List,Set,Zset,Hash
应用场景:
热数据缓存,
限时业务的运用,
计数器,
排行榜,
分布式锁,
延时操作,
队列
redis持久化:
rdb,
aof
Redis的过期键的删除策略:
定时过期(定时器),
惰性过期(访问判断过期),
定期过期(expires)
Redis的内存淘汰策略:
全局的键空间:
noeviction(写入报错),
allkeys-lru(移除最近最少使用的key),
allkeys-random(随机移除);
过期时间的键空间:
volatile-lru(最少使用),
volatile-random(随机),
volatile-ttl(更早时间移除)
Redis 事务
的本质是通过MULTI、EXEC、WATCH等一组命令的集合,
redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
multi : 标记一个事务块的开始( queued )
exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard : 取消事务,放弃事务块中的所有命令
unwatch : 取消watch对所有key的监控
集群方案:
哨兵模式,
Redis Cluster 方案(服务端路由查询),
Redis 主从架构(一主多从,主负责写,并且将数据复制到其它的 slave 节点)
Redis实现分布式锁:
当且仅当 key 不存在,将 key 的值设为 value。
若给定的 key 已经存在,则 SETNX 不做任何动作,
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。返回值:设置成功,返回 1 。设置失败,返回 0,设置过期时间
SET key random_value NX PX 5000
值得注意的是:
random_value 是客户端生成的唯一的字符串
NX 代表只在键不存在时,才对键进行设置操作
PX 5000 设置键的过期时间为5000毫秒
Redis支持的Java客户端都有哪:Redisson、Jedis、lettuce等等,官方推荐使用Redisson
springcloud
常用组件:
Eureka:服务治理组件,包括服务端的注册中心和客户端的服务发现机制,
Zuul(gateway):API网关组件,对请求提供路由及过滤功能,
Ribbon,Feign:客户端的负载均衡,以及回调,IRule实现
Hystrix:服务容错组件,实现了断路器模式,为依赖服务的出错和延迟提供了容错能力;
bus用于传跨多个实例刷新配置的功能
config 用于传跨多个实例刷新配置的功能
微服务之间通讯:
远程过程调用(Remote Procedure Invocation)消息
mysql
自增列作为主键:InnoDB会选择主键作为聚集索引; 数据记录本身被存于主索引(一颗B+Tree)的叶子节点上
B树,每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为nul,叶子结点不包含任何关键字信息,
B+树所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接,
所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字
B+比B树更适合实际应用中操作系统的文件索引和数据库索引:
B+的磁盘读写代价更低(具体信息的指针,因此其内部结点相对B树更小),
B±tree的查询效率更加稳定
联合索引:
key index (a,b,c). 可以支持a 、 a,b 、 a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效
表分区,
是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成;
分表:
指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。
分表与分区的区别在于:
分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表
表分区有什么好处:
存储更多数据; 优化查询,分区表更容易维护,避免某些特殊的瓶颈
MySQL支持的分区类型
RANGE分区; LIST分区; HASH分区; KEY分区
mysql隔离级别:
Read uncommitted (读未提交):最低级别任何情况都无法保证。
Read committed (读已提交):可避免脏读的发生。不可重复读
Repeatable read (可重复读):可避免脏读、不可重复读的发生。mysql默认 幻读
Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
MySQL优化:
开启查询缓存,优化查询;
explain你的select查询;
通过 show status 命令了解各种sql的执行频率,定位执行效率较低的sql语句;
通过explain分析低效sql的执行计划;
通过 show profile 分析sql;
通过trace分析 优化器 如何选择执行计划; 确定问题并采取相应的优化措施
SQL索引失效的一些情况:
联合索引,没有遵循左前缀原则
is null 有时候不走索引
索引字段进行在操作运算
like以%开头
索引列隐式转换
or语句前后没有同时使用索引。当or左右查询字段只有一个是索引,该索引失效,只有当or左右查询字段均为索引时,才会生效
MyISAM 和 InnoDB 的区别:
InnoDB支持事务,MyISAM不支持;
InnoDB支持外键,而MyISAM不支持;
InnoDB是聚集索引;
Innodb不支持全文索引,而MyISAM支持全文索引
mq优点:
解耦
异步
削峰
避免重复消费:
每个消息都会有唯一的消息 id。
业务表添加约束条件; 添加消息表;
redis 或者 zookeeper 的分布式对消息 id 加锁
kafka与zookeeper的关系
kafka用zk做meta信息存储,consumer的消费状态,group的管理以及 offset的值
kafka producer ACK:
1(默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。
0 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
-1 producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。"
Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么:
ISR:In-Sync Replicas 副本同步队列
AR:Assigned Replicas 所有副本
ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度,
当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度)
任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
kafka 为什么那么快:
顺序写;零拷貝;批處理,pull
Kafka中的消息是否会丢失和重复消费
针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功;
异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态;
针对消息重复:
将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可
Kafka中是怎么体现消息顺序性的:
partition中的消息在写入时都是有序; 每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的
producer消息生产者:指消息kafka broker发送数据的客户端
consumer消息消费者:向kafka broker 取数据的客户端
topic:主题,可理解为一个队列
cocnsumer group:消费者组,多个消费者共同消费一个topic消息
broker就是一台kafka服务器,一个集群中有多个broker,一个broker有多个topic
partition一个topic可以分为多个partition,每个partition是一个有序队列,partition中的消息都有一个有序的id(offset),
kafka只保证一个partition中的消息的有序性,不能保证一个topic或者多个partition中消息的有序性
offset存储文件按照offset.kafka来命名,用offset命名的好处是方便查找
生产流程:
producer采用推(push)模式将消息发布到broker,每条消息都被追加**(append)到分区(patition)**中,属于顺序写磁盘(顺序写磁盘效率比随机写内存高,零拷贝,kafka分段日志(segment),kafka预读(read ahead),后写(Write behind)保障kafka吞吐率)
消费过程:
consumer链接zookeeper获取kafka集群的相关信息(主要是消费数据的offset值保存在zookeeper,在后期版本之后offset可放在集群中不用放在zookeeper)
消费者pull拉取kafka集群中指定的topic分区的信息消费,分批次取数据(缓冲数据多条数据),一个分区只能一个消费者消费,但是一个消费者可以消费多个分区,
group consumer(消费者组)中包含的多个消费者里面的offset统一管理同组消费者成员消费后期添加消费者消费,集群可做再平衡,
消费者组从新从zookeeper获取集群信息,再平衡在kafka集群中进行
zookeeper= 文件系统+通知机制;
特点
一个leader ,多个follower组成的集群
集群中只要半数以上的节点存活,zookeeper集群就能正常服务
全局数据一致,每个server保存一份相同的数据副本,client无论链接那个server,数据都是一致的
更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
数据更新原子性,要么成功要么失败
实时性,一定时间范围内,client能读到最新数据
zookeeper文件结构
数据模型的结构与Unix文件系统很相似,整体上可以看作是一个树,每个节点称作一个ZNode,每个ZNode默认能够存储1mb的数据,每个ZNode都可以通过其路径唯一标识
半数机制:
集群中只要半数以上机器存活,集群可用,集群适合奇数台服务器,server启动没有历史数据的情况下,先自投,选举状态一直是looking,
集群相互交换选举信息,所以后加的server票数更多,超过半数以上的投票作为leader ,其他为follower
持久(Persistent)节点 客户端和服务端断开后节点不删除
临时(Ephemeral)节点 客户端和服务端断开后节点自删除
临时有序,持久有序
【面试技巧|2021 java面试题目(持续更新...)】zookeeper监听器原理
创建一个main线程
其中在main线程中创建一个zookeeper的客户端,同时这个客户端拥有两个线程,一个负责网络通信(connection)一个负责监听(listener)
通过connection将注册的监听事件发送给zookeeper
在zookeeper的注册监听器列表将注册的监听事件添加到列表中
zookeeper监听到数据或者路径变化,就会将这个消息告诉给listener线程
listener就会调用相应的process()方法处理
推荐阅读
- 编程|【整理】IDEA优化措施
- java|分享Java代码的一些小建议,脱离小白——学会优化代码50个方案
- mysql|mysql(InnoDB的主键采用聚簇索引,二级索引不采用聚簇索引)
- 微服务|RabbitMQ之消息可靠性、死信交换机、惰性队列及集群
- java|iOS 高刷屏监控 + 优化(从理论到实践全面解析)
- 大数据|一次关于架构的“嘴炮”
- 大数据|自动拦截 50% crash,字节自研 Fastbot 如何助力今日头条稳定性测试
- c++|一文读懂 Android FFmpeg 视频解码过程与实战分析
- java|【Rust日报】2022-03-21 Firefox 现在约 10% 的代码为 Rust