面试突击:kafka
平时工作很多概念性的东西用不到,长时间不用吧,容易忘,但面试又确实是要问,只能复习面试的时候整理下来,也是再学习一遍。
真希望能找到一份,能把学习到的诸多理论知识变成实践的工作,而不是业务复杂,但技术不复杂的工作,嗨,加油吧。
讲讲你了解到kafka
由于之前公司在我去之前就确定了了kafka作为消息队列的技术选型,我只是简单的去了解了一下kafka的原理
1. 为什么选择了kafka
经过我个人的推测:
首先是因为kafka兼容性很好。之前的公司有大数据部,他们用scala开发,有量化交易部,他们用python,我们还有嵌入式部,他们用什么开发我就不知道了,因为没和他们打过交道,kafka在这种环境下的兼容性应该是比rabbitMQ或者rocketMQ好一些。
其次kafka本身性能十分优异,批处理能力很强,可靠性也很好,基本不用担心消息丢失。
所以架构选择了kafka作为消息队列。
2. 采用了发布-订阅模型
发布-订阅模型相较于普通的队列模型,可以更好的服务于多个消费者的情况。
比如我们量化交易部做了用电预测后,可以通过kafka同时推消息给我们售电部和用电部,两个部门各自接到用电预测后做不同的业务逻辑处理。
3. 高性能
首先,kafka是分布式的,在一个cluster中有多个broker,每个broker就对应了一台物理机。
kafka还有个概念是topic,生产者生产消息的时候要指定topic,消费者也是订阅topic,从topic里面拉取消息。
在这个topic内要分成多个partition,一般来说有几个broker就分几个partition,然后生产者发的消息会根据分配机制被分配到各个partition里面。
这就现实了负载均衡的功能,在高并发下平衡了多服务器的压力,保证了高吞吐量。这个架构也易于进行水平扩展,可以非常方便的动态调节。
架构图:
4. 高可用
一个partition之中会有多个replica,这多个replica中会有一个leader,剩下的replica都是follower,生产者发送消息给leader,follower会从leader拉取消息作为备份,然后leader再把消息广播给consumer
如果leader挂掉的话,会从follower里面选举出一个leader(这里和redis的哨兵模式选主节点很像很像),如果follower和leader的同步率不够高的话是不能被选举的
架构图:
5. 架构示意图
6. 推拉结合保证最大效率
生产者到topic是使用推的方式,可以第一时间将消息发出去,如果用拉的话可能发送不及时。
消费者到topic/follower到leader,是用拉的方式,用推的话如果消费能力不足可能会导致网络拥堵和服务拒绝。
kafka如何保证消息的顺序
由于kafka只能保证一个partition内的顺序,不能保证topic分发partiton的顺序,所以如果业务上真的需要保证消息的顺序,那一般有三种做法:
- 生产者在发消息的时候指定发到topic的哪个partition里(最推荐)
- 一个topic只部署一个partition(不推荐)
- 生产者在发消息的时候,在消息体内标明需确保顺序的数量及序号(更不推荐)
什么情况下kafka会丢失消息
1. 没推到
生产者推送消息至topic时,由于网络波动,或者kafka设置等原因,可能会没推送到
解决办法:
设置重试次数和重试间隔
2. kafka出故障了
比如partition虽然是有多副本的,可是由于follower是拉的机制,有可能leader挂掉的时候,所有follower没有一个全量拉取了,这就导致没拉取到的部分就丢了
解决办法:
- 设置replication.replica大一点,多增加follower
- 设置min.insync.replica大一点,这个是接收最少接接受到的副本,大于这个数才算推送成功
- 设置acks=all,代表了一个partition内所有的follower都拉到了,才算生产者推送成功(不是很推荐)
3. 消费者拿到消息后,消费者挂了
我觉得这其实算是消费者自己的问题,应该从消费者这边考虑。。。不属于kafka的问题。。。不过这边也可以关闭一个设置就是自动提交,关闭以后需要有消费者发消息给kafka后,这条消息才会被出队列,不过这也会导致消息被重复消费的问题。。。
消息分配不平均是什么原因?如何解决?
原因
在不是因为指定了topic的partition的前提下,还发生这种情况,是因为kafka对key做了某种算法的hash,然后除以broker取余来分配所在的partition的
默认的算hash算法可能会导致如果key是简单key,如1,2,3这样的key,很可能大概率会导致算出来的hash相同,导致分配到了一台机器上
解决办法
在构建key的时候后面追加时间戳