1.Kafka概念,架构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_15014327/article/details/83421740

一、为什么需要消息系统

1.解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
2.冗余
消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
3.扩展性
因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
4.灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
5.可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
6.顺序保证
在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
7.缓冲
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
8.异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

二、kafka架构

1. 拓扑结构

2. 相关概念

1.producer
消息生产者,发布消息到 kafka集群的终端或服务。
2.broker
kafka集群中包含的服务器。
3.topic
每条发布到kafka集群的消息属于的类别,即 kafka是面向topic的。
4.partition
partition是物理上的概念,每个topic包含一个或多个partition。kafka分配的单位是partition。
5.consumer
从kafka集群中消费消息的终端或服务。
6.consumer group
high-level consumer API 中,每个consumer都属于一个consumer group,每条消息只能被consumer group中的一个 consumer消费,但可以被多个consumer group消费。
7.replica
partition的副本,保障partition的高可用。
8.leader
replica中的一个角色,producer和consumer只跟leader交互。
9.follower
replica中的一个角色,从leader中复制数据。
10.controller
kafka集群中的其中一个服务器,用来进行leader election以及各种failover。
11.zookeeper
kafka通过zookeeper来存储集群的meta信息。

3. zookeeper节点

三、producer发布消息

1 写入方式

producer采用push模式将消息发布到broker,每条消息都被append到patition中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障kafka吞吐率)。

2 消息路由

producer 发送消息到 broker 时,会根据分区算法选择将其存储到哪一个 partition。其路由机制为:

1.指定了patition,则直接使用
2.未指定patition但指定key,通过对key的value进行hash选出一个patition
3.patition和key都未指定,使用轮询选出一个patition

3 写入流程

流程说明:

1. producer先从zookeeper的"/brokers/.../state"节点找到该partition的leader
2. producer将消息发送给该leader
3. leader将消息写入本地log
4. followers从leaderpull消息,写入本地log后leader发送ACK
5. leader收到所有ISR中的replica的ACK后,增加HW(highwatermark,最后commit的offset)并向producer发送ACK

四、broker保存消息

1 存储方式

物理上把topic分成一个或多个patition(对应server.properties中的num.partitions=3配置),每个patition物理上对应一个文件夹(该文件夹存储该patition的所有消息和索引文件)。

2 存储策略

无论消息是否被消费,kafka都会保留所有消息。有两种策略可以删除旧数据:

1.基于时间:log.retention.hours=168
2.基于大小:log.retention.bytes=1073741824

注意:因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高Kafka性能无关。

3 topic创建

流程说明:

1. controller在ZooKeeper的/brokers/topics节点上注册watcher。当topic被创建,则controller会通过watch得到该topic的partition/replica分配。
2. controller从/brokers/ids读取当前所有可用的broker列表,对于set_p中的每一个partition:
    2.1 从分配给该partition的所有replica(称为AR)中任选一个可用的broker作为新的leader,并将AR设置为新的ISR。
    2.2 将新的leader和ISR写入/brokers/topics/[topic]/partitions/[partition]/state。
3. controller通过RPC向相关的broker发送LeaderAndISRRequest。

4 topic删除

流程说明:

1. controller在zooKeeper的/brokers/topics节点上注册watcher。当topic被删除,则controller会通过watch得到该topic的partition/replica分配。
2. 若delete.topic.enable=false,结束;否则controller注册在/admin/delete_topics上的watch被fire,controller通过回调向对应的broker发送StopReplicaRequest。

五、kafka HA

1 replication

同一个partition可能会有多个replica(对应server.properties配置中的default.replication.factor=N)。没有replica的情况下,一旦broker宕机,其上所有patition的数据都不可被消费,同时producer也不能再将数据存于其上的patition。引入replication之后,同一个partition可能会有多个replica,而这时需要在这些replica之间选出一个leader,producer和consumer只与这个leader交互,其它replica作为follower从leader中复制数据。

Kafka分配Replica的算法如下:

1. 将所有broker(假设共n个broker)和待分配的partition排序
2. 将第i个partition分配到第(imodn)个broker上
3. 将第i个partition的第j个replica分配到第((i+j)moden)个broker上

2 leader failover

当partition对应的leader宕机时,需要从follower中选举出新leader。在选举新leader时,一个基本的原则是,新的leader必须拥有旧leadercommit过的所有消息。

kafka在zookeeper中(/brokers/.../state)动态维护了一个ISR(in-syncreplicas),由写入流程可知ISR里面的所有replica都跟上了leader,只有ISR里面的成员才能选为leader。对于f+1个replica,一个partition可以在容忍f个replica失效的情况下保证消息不丢失。

当所有replica都不工作时,有两种可行的方案:

1. 等待ISR中的任一个replica活过来,并选它作为leader。可保障数据不丢失,但时间可能相对较长。
2. 选择第一个活过来的replica(不一定是ISR成员)作为leader。无法保障数据不丢失,但相对不可用时间较短。

3 broker failover

流程说明:

1.controller在zookeeper的/brokers/ids/[brokerId]节点注册Watcher,当broker宕机时zookeeper会firewatch
2.controller从/brokers/ids节点读取可用broker
3.controller决定set_p,该集合包含宕机broker上的所有partition
4.对set_p中的每一个partition
    4.1从/brokers/topics/[topic]/partitions/[partition]/state节点读取ISR
    4.2决定新leader
    4.3将新leader、ISR、controller_epoch和leader_epoch等信息写入state节点
5.通过RPC向相关broker发送leaderAndISRRequest命令

4 controller failover

当controller宕机时会触发controllerfailover。每个broker都会在zookeeper的"/controller"节点注册watcher,当controller宕机时zookeeper中的临时节点消失,所有存活的broker收到fire的通知,每个broker都尝试创建新的controller path,只有一个竞选成功并当选为controller。

当新的controller当选时,会触发KafkaController.onControllerFailover方法,在该方法中完成如下操作:

1.读取并增加ControllerEpoch。
2.在reassignedPartitionsPatch(/admin/reassign_partitions)上注册watcher。
3.在preferredReplicaElectionPath(/admin/preferred_replica_election)上注册watcher。
4.通过partitionStateMachine在brokerTopicsPatch(/brokers/topics)上注册watcher。
5.若delete.topic.enable=true(默认值是false),则partitionStateMachine在DeleteTopicPatch(/admin/delete_topics)上注册watcher。
6.通过replicaStateMachine在BrokerIdsPatch(/brokers/ids)上注册Watch。
7.初始化ControllerContext对象,设置当前所有topic,“活”着的broker列表,所有partition的leader及ISR等。
8.启动replicaStateMachine和partitionStateMachine。
9.将brokerState状态设置为RunningAsController。
10.将每个partition的Leadership信息发送给所有“活”着的broker。
11.若auto.leader.rebalance.enable=true(默认值是true),则启动partition-rebalance线程。
12.若delete.topic.enable=true且DeleteTopicPatch(/admin/delete_topics)中有值,则删除相应的Topic。

六、consumer消费消息

1 consumerAPI
kafka提供了两套consumerAPI:
The high-level Consumer API、The Simple Consumer API。其中high-level consumer API提供了一个从kafka消费数据的高层抽象,而SimpleConsumerAPI则需要开发人员更多地关注细节。

1.1 The high-level consumer API
high-level consumer API提供了consumer group的语义,一个消息只能被group内的一个consumer所消费,且consumer消费消息时不关注offset,最后一个offset由zookeeper保存。

使用high-level consumer API可以是多线程的应用,应当注意:

1. 如果消费线程大于 patition 数量,则有些线程将收不到消息
2. 如果 patition 数量大于线程数,则有些线程多收到多个 patition 的消息
3. 如果一个线程消费多个 patition,则无法保证你收到的消息的顺序,而一个 patition 内的消息是有序的

1.2 The SimpleConsumer API
如果你想要对patition有更多的控制权,那就应该使用Simple Consumer API,比如:

1.多次读取一个消息
2.只消费一个patition中的部分消息
3.使用事务来保证一个消息仅被消费一次

但是使用此API时,partition、offset、broker、leader等对你不再透明,需要自己去管理。你需要做大量的额外工作:

1.必须在应用程序中跟踪offset,从而确定下一条应该消费哪条消息
2.应用程序需要通过程序获知每个Partition的leader是谁
3.需要处理leader的变更

使用Simple Consumer API的一般流程如下:

1.查找到一个“活着”的broker,并且找出每个partition的leader
2.找出每个partition的follower
3.定义好请求,该请求应该能描述应用程序需要哪些数据
4.fetch数据
5.识别leader的变化,并对之作出必要的响应

2 consumer group

kafka的分配单位是patition。每个consumer都属于一个group,一个partition只能被同一个group内的一个consumer所消费(也就保障了一个消息只能被group内的一个consuemr所消费),但是多个group可以同时消费这个partition。

kafka的设计目标之一就是同时实现离线处理和实时处理,根据这一特性,可以使用spark/Storm这些实时处理系统对消息在线处理,同时使用Hadoop批处理系统进行离线处理,还可以将数据备份到另一个数据中心,只需要保证这三者属于不同的consumer group。如图:

3 消费方式
consumer采用pull模式从broker中读取数据。

push模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据consumer的消费能力以适当的速率消费消息。

转自:http://www.cnblogs.com/cyfonly/p/5954614.html

猜你喜欢

转载自blog.csdn.net/qq_15014327/article/details/83421740