分享

大佬20+公司面试题总结【包含Java Spring 、JVM等及大数据】

问题导读

1.本文大数据考察了哪些内容?
2.Mysql为什么不用红黑树?
3.JVM垃圾回收机制包含哪些内容?


导读:
下面答案仅供参考,大家切忌不要当成标准答案,仅是参考答案,面试来讲,只有自己真正理解的,能说出来的,才是最好的答案。面试不要考试,也没有标准答案,自己说的内容,经得起推敲,那就是好答案。

ZooKeeper
1. CAP定理
C:一致性(读操作总能读到以前的写操作)
A:可用性(在单台机器出错时,仍然能正常工作,不用迁移到其他机器)
P:分区容错性 (异常情况下仍然能满足CA)
该理论指出一个分布式系统不可能同时满足CAP,ZooKeeper保证了CP,A的话在Leader选举时会丢失部分请求

2. ZAB协议
分为消息广播(半数follower收到请求即commit)和崩溃恢复(leader崩溃后选举leader的过程)

3. Leader选举算法和流程
每个ZooKeeper都有两个Id,一个是代表自己的Pid,一个是代表本身所存储的数据的Zid,
一开始还没有leader也没有数据的时候会选举最大的Pid当leader
当运行突然崩溃时,先每个都选举自己,广播自己的Pid和Zid,然后收到的机器广播最大的那个Zid对应的Pid,当半数+1ZooKeeper同意的时候即成为Leader

Redis
1. Redis的应用场景
缓存、简单消息队列、分布式锁、共享session。

2. Redis支持的数据类型(必考)
String(SDS字符串,简单key-value,并发计数,简单缓存),
List(zipList,LinkedList,双向队列,可以用作消息队列,可以先进先出,也可以先进后出),
Set(intSet,HashTable,交并合集),
ZSet(ZipList,SkipList,热搜榜一类,有排名的Set集合,事实上还用Hashtable存了key-分值),
Hash(ZipList,HashTable,rehash概念、提高负载因子,符合多重 key的情况,比如用户的购物车?)

3. zset跳表的数据结构(必考)
首先实现一个单项链表,然后在此之上比如说每隔一个节点指向往后数数第二个节点。这样的跳层有很多层
查询方式为,先跳,如果大于了要查的分值,就往下一层查。
补充:跳表能用红黑树实现吗,跳表的自平衡怎么实现?
红黑树没法实现区间查询,跳表可以查到一个之后再前后查,时间复杂度为O(nlogn)
跳表是通过随机函数来维护索引与原始链表大小之间的平衡

4. Redis的数据过期策略(必考)
比如
1.每隔100ms,查询一部分数据,过期就删除
2.查询的key是过期的,删除
当内存不够使
对设置了过期时间的key:
随机删除、删除快要过期的、删除最不常用的、删除一段时间最少使用的
对没设置过期的key:
随机删除、删除最不常用的,删除最少使用的
或者直接报错

5. Redis的LRU过期策略的具体实现
维护了一个大小固定的pool,每次随机取值,将lru值最小的放到pool里来

6. 如何解决Redis缓存雪崩,缓存穿透问题
雪崩(随机过期时间、永不过期)、
穿透(表示恶意请求,在系统端判断是否符合规则,比如id<0,布隆过滤器)、
击穿(查询加for update,永不过期)

7. Redis的持久化机制(必考)
RDB(内存快照),比较快,存储的是内容,可以BGSave,fork一个子进程存储,对于Slave可以一开始用这个同步,之后再以aof的形式同步,开启时会影响并发性能。进行BGSave时服务可能会暂停几秒(需要注意)
AOF(AppendOnlyFile),比如每隔一秒将操作追加到log中,开启aof会导致吞吐量降低,但是比RDB可以更完整的保护数据,可以将AOF文件转成RDB,

8. Redis的管道pipeline
减少TCP连接次数,由客户端缓存一部分命令后一次性发送。

9.Redis和memcached的区别
redis支持多种数据结构,支持系列化,key-value可存储上限为1G,单核
memcached只支持String,不支持序列化,value大小最大为1MB,支持多核

10.Redis并发竞争key的解决方案
比如要set一个key,顺序为234 ,没加锁变成了432
zookeeper的分布式锁实现(监听前面一个节点,当自己为第一个节点时,获得锁)
1.redis的分布式锁实现(setnx+看门狗(比如当key过期快到的时候还没有delete,自动续时),或者直接redission)
2.消息队列串行执行

11.Redis与Mysql双写一致性方案
先更新mysql再删除reids,或者直接串行执行

Mysql
1. 事务的基本要素
原子性,隔离性,一致性,持久性

2. 事务隔离级别(必考)
Uncommited 脏读
commitedread 不可重复度
repeatedtable 幻读(mysql默认)
seriazable

3. 如何解决事务的并发问题(脏读,幻读)(必考)
直接上隔离级别就可解决
读加共享锁,写加排它锁

4. MVCC多版本并发控制(必考)
Inodb多 维护了两个字段,一个是createversion,一个是deleteversion
update时会更新createversion
读取到一行需要满足该事务的version大于等于createversion,而小于deleteversion

5. binlog,redolog,undolog都是什么,起什么作用
binlog记录的是sql语句,
redolog记录是为了保证事务安全,当数据库挂掉之后重启仍能通过redolog执行未完成事务
undolog记录的是上一个版本,用来回滚和快照读,

6. InnoDB的行锁/表锁
不走索引会表锁,走索引会行锁,for update、update,insert都是行锁
如果索引不为主键索引,且索引可重复,会有间隙锁

7. myisam和innodb的区别,什么时候选择myisam
innodb提供事务,可以崩溃恢复,最低行锁
myisam不支持事务,不能崩溃恢复,表锁,
读写分离,读库可以选择myisam

8. 为什么选择B+树作为索引结构(必考)
B树非叶子节点也会存,导致b树高度变高,io次数变多,而且叶子节点没有指向下一个叶子节点的指针

9. 索引B+树的叶子节点都可以存哪些东西(必考)
索引值,下一个叶子节点的开头(范围查询的实现)
主键索引可以不用再次查询,因为主键索引与数据放在一起。
普通索引需要再次查询主键索引之后再得到数据

10. 查询在什么时候不走(预期中的)索引(必考)
查询字段中虽然为索引,但是索引大量重复
用了or,但是后面的字段没有用到索引

11. sql如何优化
创建索引,索引字段设置成notnull ,注意不走索引的情况:%打头的like语句,!=用> union <实现,is
null的查询,注意最左匹配原则;再有就是去优化limit,先查id再limit,查询别用select*,防止表修改后报错或者查询冗余
SQL的执行顺序:from---where--group by---having---select---order by

12. explain是如何解析sql的
explain ***之后会显示sql走的类型,扫描的行数等等,可以根据这个判断sql

13. order by原理
利用索引的有序性获取有序数据,比较快(索引排序)
通过相应的排序算法,将取得的数据在内存中进行排序,比较慢(文件排序)

14.Mysql为什么不用红黑树?
红黑树每个节点下只有两个子节点,而硬盘IO时是按簇读取的,两个节点中的值可能不够填满簇导致每次IO的浪费,此时红黑树的高度会大于B+树,导致IO次数增加。

JVM
1. 运行时数据区域(内存模型)(必考)
堆、方法区、虚拟机栈,本地方法栈、程序计数器

2. 垃圾回收机制(必考)
强引用:会爆出OOM也不会被回收
软引用:在内存不够的时候被回收
弱引用:每次GC都会被回收(ThreadLocal中内部类Map的key就是)
虚引用:可以用来跟踪GC,对象准备被回收时发现他还有虚引用,会把这个虚引用加入一个引用队列,可以观察这个队列中虚引用是否存在来判断对象是否被回收了。

3. 垃圾回收算法(必考)
引用计数(redis就是用的这个)
GCRoots(GCRoots可以简单记忆为,如果被删就一定会影响程序运行的对象,比如有虚拟机栈/本地方法栈中的引用对象,synchronized持有的对象,方法区中的静态对象、常量)
分代收集
标记清除(内存泄漏)
标记整理
复制(一般不用在老年代,太耗时,且浪费空间)
补充:OOPMap和RememberSet
OOP是栈中所存储的是引用的堆中的对象,可以快速枚举GCRoots
RememberSet是为了加快新生代的GCRoots,他保存的是老年代中对象引用的新生代对象,
此时真正的新生代的GCRoots为 “新生代GCroot+rememberSet里的对象”
G1收集器将堆分为各个region,但是难免会有各个region互相引用的情况,所以G1也用到了RememberSet

4. Minor GC和Full GC触发条件
MinorGC:Eden区满
fullgc:大对象直接 到老年代,老年代空间不足,system.gc,minorgc后发现老年代大剩余空间大小小于平均每次从新生代进入老年代的值

5. GC中Stop the world(STW)
CMS会在找GCroot时和第二次查找时STW ,查找完毕就结束STW开始清理垃圾
G1会在找GCRoot时和第二次查找时STW,需要等垃圾清理完才结束

6. 各垃圾回收器的特点及区别
serial:单线程,复制算法,与其他的交互少的交互和上下文切换,快。
parnew:serial的多线程版本,只有这个能配合CMS
scanvage:吞吐量优先(用户代码执行时间/用户代码+垃圾回收执行的时间)
cms:老年代并发收集器,标记清除算法,停顿时间段,无法清理浮动垃圾,cpu敏感,线程数为(cpu数+3)/4,cpu少的用户效率较低
G1 :强化分区,弱化分代的概念,本质上是复制算法,能预测时间停顿

7. 双亲委派模型
类先由父加载器加载,如果不能加载再由子类加载。
通过类本身与加载器来确定类的唯一性,防止 类被重复加载或者被修改核心的api

8. JDBC和双亲委派模型关系
简单来说就是JDK的库里有数据库连接的接口,而具体实现是在各个数据库的jar包中,又因为最高级的那个加载器默认只加载最基础的jar包,所以只能用其他加载器去加载数据库的jar包,此时双亲委派模型已经被破坏(可能说的比较乱,这块我也不太理解,tomcat比较好懂一点)
补充:tomcat与双亲委派模型
tomcat中能加载多个项目,为了防止多个项目不同jar包冲突,就不可能让父加载器都去加载这些,只能用子加载器加载各个项目的jar包。而父级加载器加载tomcat本身所需的jar包来确保安全。
tomcat还实现了jsp的热部署,这个也是通过类加载器实现的
我们都知道jsp本质上是servlet,他被类加载器加载后,如果被修改了,此时类名还是一样,类加载器还是会从方法区直接读取已经存在的“缓存”来加载,这样我们就无法实现热部署了。那么怎么让这个“缓存“失效呢?就是用自己的一个jsp类加载器,每个加载完成之后就卸载掉,每次加载都会去读取最新的。如果此时使用双亲委派的话,需要把父类加载器卸载,tomcat直接挂啦。

9. JVM锁优化和锁膨胀过程
锁消除 不会发生竞争的情况下JVM会把锁消除
锁粗化 比如简单for循环内的synchronized会放到for循环外
偏向锁 对象头MarkWord01,还保存有持有的线程ID,这个MarKWord与无锁状态是一样的,每次线程进来只要比较每次进来用CAS的方式把线程id设置成自己的,然后直接运行即可。

轻量级锁 对象头MarkWord00,由偏向锁膨胀而来,先通过cas设置线程id,设置失败,说明已经有其他线程拿到偏向锁了,开始膨胀,刚才那个拿到那个偏向锁的线程会在自己栈帧中创建一块区域保存对象的MarkWord信息,然后用CAS指向对象的MarkWord区域。设置成功就相当于获得了轻量级锁

重量级锁 在轻量级锁CAS多次失败后会膨胀成重量级锁,此时其他线程过来的时候会直接挂起。唤醒需要由内核态转换到用户态,比较耗时。
自旋锁,就是一直尝试获取锁,建立在别的线程获取锁占用时间比较短的认知上。

Java基础
1. HashMap和ConcurrentHashMap区别(必考)
线程不安全/安全
允许key-value为null/不允许

2. ConcurrentHashMap的数据结构(必考)
1.7 segement+hashtable,put时lock自带的自旋,一定次数后阻塞。获得size的方法比较有意思,先遍历各个segement获取下面的长度和修改次数,加起来后再获取一次,相同就返回,不相同就常识多次,一定次数后加锁
1.8 node数组+hashtable+红黑树,put时cas自旋,多次后synchronized,多线程会帮助扩容
两者的get都是不用加锁的,都用volatile修饰了

3. 高并发HashMap的环是如何产生的
1.7以前头插法,在高并发的时候发生数组扩容

4. volatile作用(必考)
保证可见性(这里指主内存与工作内存间的可见性),防止指令重排(指令重排也会导致可见性问题)

5. Atomic类如何保证原子性(CAS操作)(必考)
unsafe类的CAS和volatile,注意ABA问题

6. synchronized和Lock的区别(必考)
Lock底层就是AQS,是CLH队列的增强,CLH是一个先进先出队列,lock中把每个线程映射成CLH队列的节点,CLH本身是自旋的,AQS在此基础上增加了可中断,可重入,阻塞等待而不是一直自旋,和非公平锁,还包括资源的独占和共享两个功能
怎么实现非共平锁,有什么好处?
非公平锁简单来说就是当线程即将进入队列时,先cas争取资源,若得到则运行,一定次数后仍然失败则加入队列,此时已失去非公平的手段,只能等前面节点来唤醒他。
公平锁可能会导致,前一个节点释放后,唤醒下一个节点,此时线程还在由内核向用户态转变,需要较多的时间,而非公平锁可以减少这种情况的发生。

7. 为什么要使用线程池(必考)
减少开销,方便管理

8. 核心线程池ThreadPoolExecutor的参数(必考)
核心线程、最大线程、消息队列、存活时间、拒绝策略

9. ThreadPoolExecutor的工作流程(必考)
来一个先到核心线程,核心线程满了到消息队列,消息队列满了最大线程还没满,就建非核心线程工作

10. 如何控制线程池线程的优先级

1.png

11. 线程之间如何通信
锁、信号

12. Boolean占几个字节
百度到的 1或4

13. jdk1.8/jdk1.7都分别新增了哪些特性
1.8:lanmbd表达式,default关键字,红黑树,尾插法,concurrenthashmap的node数组
1.7:不太了解,只知道1.7将String常量池(我更喜欢把它叫做String的对象池)放到堆中

14. Exception和Error
Exception可以catch后处理,比如IOexception,出错后程序仍能运行
error是非检查性异常,比如OOM,

补充:
简单说说怎么让三个线程循环打印
1.new 三个 semaphere ,一个为1,其他为0.当A线程执行时对自己的semaphere执行acquire方法,执行完毕后对下一个线程的semaphere执行release。
不多比比,上链接
如果让你用三个线程循环打印ABC,你有几种写法?

Spring
1. Spring的IOC/AOP的实现(必考)
ioc:beanfactory,在用到的时候加载到concurrenthashmap中,如果对象需要其他依赖,会递归实现里面的依赖。applicationcontext就是在容器加载的时候就把全部的bean放到concurrenthashmap中啦
aop:切面编程,一般是将可复用的方法在切点前后执行,实现方式有aspectj(静态织入),cglib和jdk动态代理
顺带一提,dubbo中用了装饰器把invoker包装成wrapper

2. 动态代理的实现方式(必考)
cglib:利用asm框架,把代理对象的class文件加载进来之后修改其字节码生成子类。
JDK:利用反射机制生成一个实现代理接口的匿名类

1.png

2.png


补充:2.9bean的创建流程
1.获取bean的名字
2.从缓存中查询是否有这个bean
3.没有的话就需要通过反射创建bean的实例(注意此时bean为空,里面东西都没注入)
4.标记这个bean已经被创建了(此时可能会有循环依赖的问题,Spring用三级缓存来解决,提前将bean曝光)
5.递归获取依赖的其他的bean
6.给当前bean绑定属性

3. Spring如何解决循环依赖(三级缓存)(必考)
构造器(初始化与赋值没法分开)与prototype(没有实现三级缓存)会报错
三级缓存分别为1.初始化完成的bean(singletonObjects)2.实例化的bean(尚未绑定属性,earlySingletonObjects)3.beanfactory(singletonFactories)
比如有两个beanA和B循环依赖
在A的实例化阶段标记,将自己曝光到第三级缓存中,发现自己依赖B,去初始化B,B初始化过程中发现自己依赖A,从第三级缓存中getObject拿到A(注意此时A只是实例化完成,并没有初始化),此时B顺利进行初始化,将自己放到一级缓存中,此时返回A中,A顺利拿到B,完成了初始化阶段,放到了一级缓存。

4. Spring的后置处理器
image.png
5. Spring的@Transactional如何实现的(必考)
也是通过AOP实现的,顺带一提,如果方法B由@Transactional修饰,而A方法没有此注解,此时A去调用方法B,@Transactional失效

6. Spring的事务传播级别

1.png

7. BeanFactory和ApplicationContext的联系和区别
beanfactory:懒加载,
applicationcontext:继承了beanfactory接口,比beanfactory功能更多,加载时全部加载

其他
1. 高并发系统的限流如何实现
2. 高并发秒杀系统的设计
3. 负载均衡如何设计


某37互娱一面节选(Lucene篇)
1.Lucene为什么比数据库快?
mysql的索引只是存储field的内容(如果过长,只是存前多少位的内容为索引)并没用分词
es存储的是分词以后的索引,每个词都在哪些文档中出现过。
如果是搜索 keyword这种基本没啥影响
但是如果是mysql的like "%word%" mysql全表查,es只需要查"word"这个词包含的文档id 速度明显不是一个级别。

2.什么是倒排索引?
简单理解就是将文章分词后,用分出来的词连接一个表,这个表里面是出现过这个词的文章列表,可以根据这种方法快速查询一个词之后定位到文章,而不用去每个文章查这个词。

3.倒排索引有哪几部分?分词属于那一部分(不确定)
暂时理解为三部分,单词id,单词,倒排列表
1.png


其他补充:
网络:
TCP和UDP的区别?
TCP保证数据安全,以流的形式传输,一对一双全工,能保证数据顺序
UDP不保证数据安全,以数据报的形式传输,一对多,不保证数据顺序

TCP是怎么保证安全的?
校验和
应答机制
超时重传
拥塞控制
流量控制

https和http的区别?
https:443端口,在TCP/IP协议上封装了一层TCL/SSL,以数字证书的形式来保证数据安全
(将用户数据hash后由公钥加密成密文,拿到报文后解密密文,并在次将数据hash,比较数据是否相同,第一次传输用RSA得到对称加密的秘钥,之后都用对称加密)
http:80端口,明文传输,无状态

get/post区别?
本质上是无区别的,
在浏览器端,get一般由url调用,顺带一提url的限制也是浏览器的原因,事实上http标准协议对url的长度没有限制,而post一般由表单调用
在restful规范中,get被认为是幂等的,用来请求数据,而post不幂等,用来实现资源的创建

Http请求的完整过程
1.DNS解析,先从浏览器缓存、内存缓存、host文件、DNS服务器一步步把url解析成ip地址
2.拿到ip地址之后如果是自己网段的,一般路由器里都有对应的mac地址,可以直接获得然后三次握手建立TCP连接,如果不是自己网段的,还需要发到网关,由arp协议得到mac地址。因为七层模型都是上层依赖下层,你想传输肯定得把网络和数据链路层搞定。
3.建立起TCP连接后就可以发送HTTP请求了,这个请求到了服务端可能会有负载均衡、重定向,
4.处理完请求后把请求返回,由浏览器解析数据时发现还有一些静态资源比如CSS JS或图片,又会发起另外的请求,这就是后话了。
5.处理完成后B/S架构不像C/S,一般都是短连接,四次挥手就关闭了。

为什么连接的时候是三次握手,关闭的时候却是四次握手
四次握手是因为对比与握手的被动接收方,他还需要一次握手传输未传输完的信息来保证信息的完整性。

cookie和session的区别
cookie保存在浏览器端,一般有4BK的大小限制,cookie会有cros的安全问题,简单来说就是别的恶心请求拿到了cookie之后每次请求都带上,解决方法是用token或者直接禁用cookie,使用token可以让特定的请求带上而不是每次请求都带上。
session保存在服务器端,需要用url或者cookie请求sessionid拿到,

xss攻击和ddos?
xss其原理是攻击者向有XSS漏洞的网站中输入恶意的 HTML 代码,当用户浏览该网站时,这段 HTML 代码会自动执行
ddos 简单来说就是大量请求去攻击一个公用接口,使服务器负载上升
什么是 DDoS 攻击?

你知道的协议有哪些,在哪个层,有什么用?
简单挑几个记吧。。 TCP IP ARP RAPR PPPOE SSL HTTP FTP SMTP

1.png

常见状态码及原因短语
1XX请求成功,正在处理
2XX请求成功,已经处理
3XX 重定向
301永久重定向
302临时重定向
4XX
400 请求语法错误
403 服务被拒绝
404页面不存在
5XX
500服务器内部错误(报错了)
502 服务不可用

计算机系统
进程和线程的区别
进程是资源分配的最小单位,进程间不共享资源,通信困难
线程是cpu执行的最小单位,线程共享本进程的资源如内存、I/O、cpu。同一时间内同一个cpu只能执行一个线程。

进程的调度算法
时间片、先来先服务、最短时间、优先级

什么是虚拟内存
虚拟内存是为了解决如今在有限的内存空间加载较大的应用程序,根据需要在磁盘和主存之间来回传送数据,通过段页表的形式,先在虚拟内存中取一段连续的内存空间,再将这段内存空间映射到主内存中,此时主内存空间的程序段可以不连续,我们可以用页表的形式找到他。

进程间的通信方式
匿名管道(fork,只能父子进程通信)
有名管道(在内核申请一块区域,任何进程都可同信)
信号(信号是进程间通信机制中唯一的异步通信机制,内核进程可以利用他通知用户空间进程发生了哪些系统事件)
信号量(本质是个计数器,用来同步)
socket(首先创建套接字,然后绑定一个端口再监听套接字,可以通过网络连接不同计算机上的进程进行通信)
共享内存区(快,需要考虑并发情况)

死锁怎么形成的,怎么解决死锁
请求保持,互斥,循环等待,不可剥夺
解决方案:设置优先级、请求一段时间后阻塞,
死锁预防(用户需要一次性请求全部资源),检测到死锁后强行剥夺进程资源

Dubbo
什么是spi?dubbo对其做了什么改动?
spi全名叫server provider interface,是一种服务发现机制,可以在运行时,通过全限定路径名,动态的加载接口的实现类。
dubbo在这个基础上做了扩展,比如说jdk的spi,他会不管你需不需要用到这些类,只要你启动就加载进来,而我们一些方法就想用到他的时候再用反射来加载,就像spring的beanfactory一样。
此外,在如果一个类需要扩展的话,dubbo用装饰者模式来实现了对类的扩展,相当于aop的实现。

服务暴露流程和引用流程?
首先dubbo有几个角色,provider,consumer,注册中心和监控中心
简单来说就是provider将接口暴露,把服务注册到注册中心,由consumer订阅注册中心,在启动的时候在注册中心找到发布的接入入口,注册到consumer服务里面,相当于创建了一个代理对象把服务间的通信封装成了一个对象的调用。底下涉及到了网络通信、协议的转换等。
具体一点:
服务暴露:一开时就是解析一些配置文件,然后有一个serviceBean来执行暴露逻辑,里面主要涉及到protocal协议类,这个servicebean在初始化完成的时候会把那些参数注入进来,然后到ioc容器初始化完成的时候,开始来暴露方法,把要注册的方法封装成一个服务的执行对象invoker,先把这个放到自己的缓存中,通过protocal协议类去把invoker通过协议暴露给外部。
服务引用:
引用与服务暴露类似,一开始也是初始化配置,然后有一个ReferenceBean来执行引用逻辑,主要利用RegistryProtocol完成provider的订阅、自己本身consumer的注册、和执行对象invoker的创建(这里订阅完成后如果有变动会调用notify方法去注册和修改缓存里的invoker),把url和invoker的映射关系加到缓存之后还没完,根据负载均衡算法拿到要执行的invoker后,动态代理生成代理类,通过代理类来完成请求远程dubbo服务并获取响应结果的功能。

dubbo的负载均衡机制?
轮询、加权轮询、一致性哈希、随机、最少活跃数

dubbo的容错机制?
广播 (一个报错就失败,用于更新各个provider的本地资源信息)
多次发送(有一个成功就行,高时效性的读)
失败后重试(幂等)
失败后报错(不幂等)
失败后不报错记录日志(审计日志)
失败后按照配置策略一段时间后重试(消息通知)
dubbo支持的协议?

灰度发布了解吗?
version标签为*,按照负载均衡的机制来找机器调方法,一部分机器更新为最新版

消息队列
消息队列的作用?
异步:比如订单服务与下单后送的优惠券服务,两者异步执行。
削峰:大量写操作,可以用消息队列削峰。
解耦:仍然是订单服务和优惠券服务,减少各个系统间的耦合,本系统只保证本系统的实现和消息队列的落地,别的系统的落地由消息队列来保证。

1.png

kafka的角色组成?
1.png

Producer:服务生产者
Consumer:服务消费者(注意下文与partition的关系)
Broker:代理,可以看作是一个kafka的实例,由多个Broker可以组成一个集群Cluster。一个Broker中还包含Topic(主题)和Partition(分区)的概念
Topic:Producer 将消息发送到特定的主题,Consumer 通过订阅特定的 Topic(主题) 来消费消息。
Partition:分区属于Topic的一部分,一个 Topic 可以有多个 Partion,在每个Broker中都有他的全部信息(高可用),而各个Consumer也可以去不同的partition去读取。
重要:如果有三个partition,四个consumer,其中一个consumer会空闲。

kafka怎么保证高可用?
简单来说,就是备份分区仅仅用作备份,不做读写。如果某个Broker挂了,会选举其他的partition来作为主分区。

如果重复消费/消息消费失败怎么办?怎么保证幂等?
此时就需要操作保证请求的幂等。 消息消费失败会有重试机制去保证他成功。
幂等的实现可以说多种多样。
1.全局唯一id
比如用户付费成功(流水表里面有唯一id),然后用消息队列调用其他业务(其他业务中会有流水表唯一id字段),可以先去查这个id存不存在,不存在就执行。
2.表中状态字段
比如订单表中有一个是否已支付的字段,去查的时候可以通过这个字段来决定是否执行。
3.唯一索引实现insert的幂等
比如已经创建流水id,可以把这个设置成唯一索引,其他再次insert时就会报错。
再比如通过全局唯一id和用户id设置成联合唯一索引,可以实现秒杀场景下,一个用户只能购买一件的需求。
3.redis实现幂等
比如发验证短信的场景,先获取一个token,保存在redis中,操作加入到消息队列,然后判断下redis中是否有这个token,有的话就消费并且把reids中的token删除,没有的话就不执行。

怎么保证消息的有序执行?
1、当消息加入到一个partition的时候,都是增量添加,所以都是有序的,所以可以设定只有一个partition。(不推荐这种方法,失去了kafka的高可用)
2、指定partition发送,Kafka 中发送 1 条消息的时候,可以指定topic, partition, key,data(数据) 4 个参数。如果你发送消息的时候指定了 partion 的话,所有消息都会被发送到指定的 partion。并且,同一个 key 的消息可以保证只发送到同一个 partition,比如我们可以把唯一订单号作为key,这样一个订单的操作就能保证顺序消费了。

消息如何保证不丢失?
kafka在partition有数据进来的时候会先缓存一部分,等数据量足够多或者等待一定时间再批量写到磁盘的消息日志上。

消息积压怎么办?
扩展机器数量,创建新的topicB ,设定10个partition,之前A的消费者逻辑改为获取到topicA的消息之后,发topicB的消息,然后新的10台机器来处理topicB的数据,这样效率是以前的3倍。


作者:就这些吗
链接:https://www.jianshu.com/p/a61f012e84d5


本帖被以下淘专辑推荐:

加微信w3aboutyun,可拉入技术爱好者群

已有(1)人评论

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /5 下一条