1、Kafka消息队列是拉消息呢?还是推消息?
1.1 推模式
- 优点: 实时性高,且对消费者来讲,使用起来更为方便。
- 缺点: 难以适应高消费速率 ,当生产者生产消息的速度特别快的时候,消费者来不及消费会爆仓。并且不同的消费者的消费速率是不同的 ,作为Broker很难平衡推送速率,如果强行平衡的话 ,本身的复杂度难以避免要增加。
- 适用情况:消息量不大 ,对实时性要求比较高的情况。
1.2 拉模式
- 优点:消费者可以根据自身的情况来拉取消息,broker相对轻松很多,更适合进行消息的批量发送。
- 缺点:消息延迟,得每间隔一段时间,去拉取一下消息,这个时间间隔也不能太短,否则就变成broker攻击了。
如何选择呢 ?
参考大佬的选择 :RocketMQ和Kafka都选择使用拉模式 。
个人感觉拉模式也更好一些。消息队列本身都具有消息持久化的功能。它存储好消息之后,你需要的话,直接来拿就好。broker作为一个中心点,能轻量就轻量。
不过,可以通过长轮询来减轻拉模式的缺点:消费者去 Broker 拉消息,定义了一个超时时间,也就是说消费者去请求消息,如果有的 话马上返回消息,如果没有的话消费者等着直到超时,然后再次发起拉消息请求。 并且 Broker 也得配合,如果消费者请求过来,有消息肯定马上返回,没有消息那就建立一个延迟操作,等条件满足了再返回。
kafka的事务消息
常见的分布式事务
-
2PC : 二阶段提交,强一致性,同步阻塞,只适用于数据库层面上的事务(如果要在数据库中写一条数据的同时,上传一张图片,2PC无法保证事务)。
-
TCC: 可以保证业务层面上的事务。Try-Confirm - Cancel 三个步骤。先占坑,如果占到了坑,在执行真正的业务操作,如果某个组件失败了 ,那就统一cancel。
-
事务消息:事务开始的时候会先发送一个 半消息给 Broker。 半消息的意思就是这个消息此时对 Consumer 是不可见的,而且也不是存在真正要发送的队列中,而 是一个特殊队列。发送完半消息之后再执行本地事务,再根据本地事务的执行结果来决定是向 Broker 发送提交消息,还 是发送回滚消息。 如果发送提交或者回滚消息失败了 Broker 会定时的向 Producer 来反查这个事务是否成功,具体的就是 Producer 需要暴露一 个接口,通过这个接口 Broker 可以得知事务到底有没有执行成功,没成功就返回未知,因为有可能事 务还在执行,会进行多次查询。
-
本地消息表:利用关系型数据库的事务能力,在进行本地事务操作中,加入本地消息表的写入,将业务执行的情况和消息表的操作放到同一个事务中。这样子本次事务成功的话 ,消息插入也一定能成功,然后再调用其他服务,若其他服务成功了 ,就修改本地消息表的状态。如果失败了,会有一条后台线程扫描这些操作失败的服务,然后一直调用,直到超过设置好的重试次数后,人工介入处理。
kafka中的事务消息
kafka事务消息主要是用来处理一次事务中需要发送多个消息的情况,保证多个消息之间的事务约束,也就是要么全成功,要么全失败。 Kafka 的事务基本上是配合其幂等机制来实现 Exactly Once 语义的
在开始事务的时候,生产者会向事务协调者发起请求表示事务开启,事务协调者会将这个消息记录到特 殊的日志-事务日志中,然后生产者再发送真正想要发送的消息。Kafka 会像对待正常消息一样处理这些事务消息,由消费端来过滤这个消息
然后发送完毕之后生产者会向事务协调者发送提交或者回滚请求,由事务协调者来进行两阶段提交,如 果是提交那么会先执行预提交,即把事务的状态置为预提交然后写入事务日志,然后再向所有事务有关 的分区写入一条类似事务结束的消息,这样消费端消费到这个消息的时候就知道事务好了,可以把消息 放出来了。
最后协调者会向事务日志中再记一条事务结束信息,至此 Kafka 事务就完成了。
kafka的索引设计
Kafka中的索引为稀疏索引,节省内存空间。
- 位移索引:保存位移值与对应磁盘物理位置的关系 , 8个字节(4个字节保存相对位移值,4个字节保存磁盘位置)(保存相对位移值,可以节省空间,且索引更短,每页能放置的索引数更多,可以减少磁盘访问的IO操作次数)。
- 时间戳索引:保存时间戳与对应位移值的关系 , 12个字节 (用来定期删除索引的依据)
- 事务索引:开启了事务才会用到,暂时不看
冷热分区查询:查询热数据部分(尾部),遍历的page永远固定,可以避免缺页中断。
kafka中对二分查找的优化 :
普通的二分查找,可能会造成不必要的缺页中断(需要查找的页长时间没有使用,所以从缓存中被删除了)。
优化: 将索引项分为热区和冷区,查询热数据部分的时候,遍历的page永远是固定的 ,这样就能避免缺页中断。
kafka如何查询数据(日志)
是先通过offset找到索引所在的文件,然后通过二分法找到离目标最近的索引,再顺序遍历消息文件找到目标文件。这波操作时间复杂度为 O(log2n)+O(m) ,n是索引文件里索 引的个数,m为稀疏程度。
kafka中的Controller
负责主题管理、分区管理、分区leader选举、监听broker变化、向其他broker提供元数据服务。(依赖Zookeeper实现)
没了 Zookeeper 的 Kafka 把元数据就存储到自己内部了,利用之前的 Log 存储机制来保存元数据。
Kafka的网络通讯模型
Reactor : Broker 中有个 Acceptor(mainReactor) 监听新连接的到来,与新连接建连之后轮询选 择一个 Processor(subReactor) 管理这个连接。
而 Processor 会监听其管理的连接,当事件到达之后,读取封装成 Request ,并将 Request 放入共享 请求队列中。
然后IO线程池不断的从该队列中取出请求,执行真正的处理。处理完之后将响应发送到对应的 Processor 的响应队列中,然后由 Processor 将 Response 返还给客户端。(和Netty的流程9成相似度,1成留给我的无知)。
(部分参考:《yes的练级攻略》)
kafka抛弃Zookeeper? 然后配置中心怎么搞?
在原始的情况下 ,kafka强烈依赖于zookeeper , 其broker的元信息、主题数据、分区数据等都在zookeeper中注册,zookeeper提供服务发现、订阅、故障处理、选举、扩容等功能。
但是为什么要抛弃zookeeper?需要同时照顾到kafka和zookeeper两个集群,且zookeeper强一致性,如果zookeeper集群的某个节点发生数据变更了 ,需要其他节点全部都更新完成之后,才能正常工作,性能较差。且zookeeper不适合存储很多的信息,当写入的数据量大了的时候,其稳定性和性能会下降。所以需要去掉对zookeeper的依赖。
现在怎么办? 利用log存储机制来保存元数据,使用现有的消息存储机制稍加改造,完成需要的功能。(基于Raft协议,可以完成选举,还没看)
kafka如何保证高可用?
- 备份机制:一个leader,多个follower , 且统一partition的消息应均匀分布在多个broker上,一个broker宕机,不至于全不能用。
- ISR机制:ISR中的副本都是与Leader同步的副本,不在ISR中的副本被认为不同步。
- Unclear领导者选举:当Kafka中
unclean.leader.election.enable配置为true(默认值为false)且ISR中所有副本均宕机的情况下,才允许ISR外的副本被选为Leader,此时会丢失部分已应答的数据 - ACK机制: 生产者发送消息中包含acks字段,该字段代表Leader应答生产者前Leader收到的应答数
- 故障恢复机制: 当Broker发生故障后,由Controller负责选举受影响Partition的新Leader并通知到相关Broker。但是Controller也会发生故障的 ,所以需要把broker都在zookeeper的controller节点上注册一个watcher。
kafka为什么这么快?
-
顺序写入:kafka设计了一种分段式、只追加的日志,将读写顺序限制为顺序读写。
-
页缓存 : kafka的数据并不是实时写入硬盘,而是使用现在分页存储,利用内存来提高io效率,将对磁盘的访问变为对内存的访问。
-
零拷贝: - mmap 将磁盘文件映射到内存,支持读和写,对内存的操作会反映在磁盘文件上。- sendfile 是将读到内核空间的数据,转到 socket buffer,进行网络发送。
-
日志的批量处理: kafka的网络瓶颈一般是网络而非磁盘,所以kafka将多个日志读写记录合并为一个批次,压缩后发送,节省带宽。
差不多了吧 ,想到了再补……