阿里大牛实战概括——Kafka架构原理

对于kafka的架构原理咱们先提出几个问题?服务器

1.Kafka的topic和分区内部是如何存储的,有什么特色?网络

2.与传统的消息系统相比,Kafka的消费模型有什么优势?多线程

3.Kafka如何实现分布式的数据存储与数据读取?架构

1、Kafka架构图并发

1.kafka名词解释app

在一套kafka架构中有多个Producer,多个Broker,多个Consumer,每一个Producer能够对应多个Topic,每一个Consumer只能对应一个ConsumerGroup。分布式

整个Kafka架构对应一个ZK集群,经过ZK管理集群配置,选举Leader,以及在consumer group发生变化时进行rebalance。性能

名称优化

解释ui

Broker

消息中间件处理节点,一个Kafka节点就是一个broker,一个或者多个Broker能够组成一个Kafka集群

Topic

主题,Kafka根据topic对消息进行归类,发布到Kafka集群的每条消息都须要指定一个topic

Producer

消息生产者,向Broker发送消息的客户端

Consumer

消息消费者,从Broker读取消息的客户端

ConsumerGroup

每一个Consumer属于一个特定的Consumer Group,一条消息能够发送到多个不一样的Consumer Group,可是一个Consumer Group中只能有一个Consumer可以消费该消息

Partition

物理上的概念,一个topic能够分为多个partition,每一个partition内部是有序的

2.Topic和Partition

在Kafka中的每一条消息都有一个topic。通常来讲在咱们应用中产生不一样类型的数据,均可以设置不一样的主题。一个主题通常会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者均可以接收到生产者写入的新消息。

kafka为每一个主题维护了分布式的分区(partition)日志文件,每一个partition在kafka存储层面是append log。任何发布到此partition的消息都会被追加到log文件的尾部,在分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,也就是咱们的offset,offset是一个long型的数字,咱们经过这个offset能够肯定一条在该partition下的惟一消息。在partition下面是保证了有序性,可是在topic下面没有保证有序性。

在上图中在咱们的生产者会决定发送到哪一个Partition。

1.若是没有Key值则进行轮询发送。

2.若是有Key值,对Key值进行Hash,而后对分区数量取余,保证了同一个Key值的会被路由到同一个分区,若是想队列的强顺序一致性,可让全部的消息都设置为同一个Key。

3.消费模型

消息由生产者发送到kafka集群后,会被消费者消费。通常来讲咱们的消费模型有两种:推送模型(psuh)和拉取模型(pull)

基于推送模型的消息系统,由消息代理记录消费状态。消息代理将消息推送到消费者后,标记这条消息为已经被消费,可是这种方式没法很好地保证消费的处理语义。好比当咱们把已经把消息发送给消费者以后,因为消费进程挂掉或者因为网络缘由没有收到这条消息,若是咱们在消费代理将其标记为已消费,这个消息就永久丢失了。若是咱们利用生产者收到消息后回复这种方法,消息代理须要记录消费状态,这种不可取。若是采用push,消息消费的速率就彻底由消费代理控制,一旦消费者发生阻塞,就会出现问题。

Kafka采起拉取模型(poll),由本身控制消费速度,以及消费的进度,消费者能够按照任意的偏移量进行消费。好比消费者能够消费已经消费过的消息进行从新处理,或者消费最近的消息等等。

4.网络模型

4.1 KafkaClient --单线程Selector

单线程模式适用于并发连接数小,逻辑简单,数据量小。

在kafka中,consumer和producer都是使用的上面的单线程模式。这种模式不适合kafka的服务端,在服务端中请求处理过程比较复杂,会形成线程阻塞,一旦出现后续请求就会没法处理,会形成大量请求超时,引发雪崩。而在服务器中应该充分利用多线程来处理执行逻辑。

4.2 Kafka--server -- 多线程Selector

在kafka服务端采用的是多线程的Selector模型,Acceptor运行在一个单独的线程中,对于读取操做的线程池中的线程都会在selector注册read事件,负责服务端读取请求的逻辑。成功读取后,将请求放入message queue共享队列中。而后在写线程池中,取出这个请求,对其进行逻辑处理,即便某个请求线程阻塞了,还有后续的县城从消息队列中获取请求并进行处理,在写线程中处理完逻辑处理,因为注册了OP_WIRTE事件,因此还须要对其发送响应。

5.高可靠分布式存储模型

在Kafka中保证高可靠模型的依靠的是副本机制,有了副本机制以后,就算机器宕机也不会发生数据丢失。

5.1高性能的日志存储

kafka一个topic下面的全部消息都是以partition的方式分布式的存储在多个节点上。同时在kafka的机器上,每一个Partition其实都会对应一个日志目录,在目录下面会对应多个日志分段(LogSegment)。LogSegment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每一个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充,以下,假设有1000条消息,每一个LogSegment大小为100,下面展示了900-1000的索引和Log:

因为kafka消息数据太大,若是所有创建索引,即占了空间又增长了耗时,因此kafka选择了稀疏索引的方式,这样的话索引能够直接进入内存,加快偏查询速度。

简单介绍一下如何读取数据,若是咱们要读取第911条数据首先第一步,找到他是属于哪一段的,根据二分法查找到他属于的文件,找到0000900.index和00000900.log以后,而后去index中去查找 (911-900) =11这个索引或者小于11最近的索引,在这里经过二分法咱们找到了索引是[10,1367]而后咱们经过这条索引的物理位置1367,开始日后找,直到找到911条数据。

上面讲的是若是要找某个offset的流程,可是咱们大多数时候并不须要查找某个offset,只须要按照顺序读便可,而在顺序读中,操做系统会对内存和磁盘之间添加page cahe,也就是咱们日常见到的预读操做,因此咱们的顺序读操做时速度很快。可是kafka有个问题,若是分区过多,那么日志分段也会不少,写的时候因为是批量写,其实就会变成随机写了,随机I/O这个时候对性能影响很大。因此通常来讲Kafka不能有太多的partition。针对这一点,RocketMQ把全部的日志都写在一个文件里面,就能变成顺序写,经过必定优化,读也能接近于顺序读。

能够思考一下:1.为何须要分区,也就是说主题只有一个分区,难道不行吗?2.日志为何须要分段

5.2副本机制

Kafka的副本机制是多个服务端节点对其余节点的主题分区的日志进行复制。当集群中的某个节点出现故障,访问故障节点的请求会被转移到其余正常节点(这一过程一般叫Reblance),kafka每一个主题的每一个分区都有一个主副本以及0个或者多个副本,副本保持和主副本的数据同步,当主副本出故障时就会被替代。

在Kafka中并非全部的副本都能被拿来替代主副本,因此在kafka的leader节点中维护着一个ISR(In sync Replicas)集合,翻译过来也叫正在同步中集合,在这个集合中的须要知足两个条件:

节点必须和ZK保持链接

在同步的过程当中这个副本不能落后主副本太多

另外还有个AR(Assigned Replicas)用来标识副本的全集,OSR用来表示因为落后被剔除的副本集合,因此公式以下:ISR = leader + 没有落后太多的副本; AR = OSR+ ISR;

这里先要说下两个名词:HW(高水位)是consumer可以看到的此partition的位置,LEO是每一个partition的log最后一条Message的位置。HW能保证leader所在的broker失效,该消息仍然能够重新选举的leader中获取,不会形成消息丢失。

当producer向leader发送数据时,能够经过request.required.acks参数来设置数据可靠性的级别:

1(默认):这意味着producer在ISR中的leader已成功收到的数据并获得确认后发送下一条message。若是leader宕机了,则会丢失数据。

0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种状况下数据传输效率最高,可是数据可靠性确是最低的。

-1:producer须要等待ISR中的全部follower都确认接收到数据后才算一次发送完成,可靠性最高。可是这样也不能保证数据不丢失,好比当ISR中只有leader时(其余节点都和zk断开链接,或者都没追上),这样就变成了acks=1的状况。

Java架构/分布式:685167672(大牛交流群)没有开发经验勿加

相关文章
相关标签/搜索