一、什么是kafka
kafka是一种消息中间件。
在了解kafka之前,我们先来了解下什么是消息中间件。
消息中间件是在消息的传输过程中保存消息的容器,作用就是中介的作用。
消息中间件就像是快递员,把东西给我,告诉我送给谁,你去忙你的吧。
1、采用异步处理模式
消息发送者可以发送一个消息而无需等待响应。消息发送者将消息发送到一条虚拟的通道(主题或队列)上,消息接收者订阅或是监听该通道。一条消息可能最终转发给一个或多个消息接收者,这些接受者都无需对消息发送者做出同步回应,整个过程是异步的。
2、应用程序和应用程序调用关系为松耦合关系
发送者和接受者都不必了解对方、只需要确认消息。
发送者和接受者不必同时在线。
比如在线交易系统为了保证数据的最终一致,在支付系统处理完成后会把支付结果放到消息中间件里,通知订单系统修改支付状态,两个系统通过消息中间件解耦。
kafka对消息保存是根据Topic进行归类。
发送消息者称为Producer,消息接收者称为Consumer。
kafka集群有多个kafka实例组成,每个实例称为broker。
二、topic/logs
一个Topic可以认为是一类消息,每个topic将被分成多个partition(区),每个partition在存储面是append log文件。。
例如生生环境:
/日志存放路径/topic名字/00000000000000000000.index
/日志存放路径/topic名字/00000000000000000000.log
任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它唯一标记一消息。kafka并没有提供其他额外的索引机制(index)来存储offset,因为在kafka中几乎不允许对消息进行"随机读写"。
kafka:即使消息被消费,消息仍然不会被立即删除。日志文件将会根据broker中的配置要求,保留一定的时间之后删除。比如log文件保留2天,那么两天后,文件会被清除,无论其中的消息是否被消费。kafka通过这种简单的手段来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO开销。
对于consumer而言,它需要保存消费消息的offset,对于offset的保存和使用,由consumer来控制;当consumer正常消费消息时,offset将会"线性"的向前驱动,即消息将依顺序被消费。事实上,consumer可以使用任意顺序消费消息,它只需要将offset重置为任意值(offset将会保存在zookeeper中)。
kafka集群几乎不需要维护任何consumer和producer状态信息。这些信息有zookeeper保存。因此producer和consumer的客户端实现非常轻量级,他们可以随意离开,而不会对集群造成额外的影响。
partitions的设计目的有多个。最根本原因是kafka基于文件存储。通过分区,可以将日志内容分散到多个server上,来避免文件尺寸达到单机磁盘的上限。每个partition都会被当前server(kafka实例)保存;可以将一个topic切分成任意多个partitions提高保存/消费的效率。此外,越多的partitions意味着更多的consumer,有效提升并发消费的能力。
三、Distribution(分布式)
一个Topic的多个partitions被分布在kafka集群的多个server上;每个server(kafka实例)负责partition中消息的读写操作。此外,kafka还可以配置partitions需要备份的个数(replicas),每个partition将会被备份到多台机器上,以提高可用性。
基于replicated方案,那么就意味着需要对多个备份进行调度;每个partition都有一个server为"leader";leader负责所有的读写操作,如果leader失效,那么将会有其他follower来接管(成为新的leader);follower只是单调的和leader跟进,同步消息即可。由此可见作为leader的server承载了全部的请求压力,因此从集群的整体考虑,有多个partitions就意味着有多少个"leader",kafka会将"leader"均衡的分散在每个实例上,来确保整体的性能稳定。
四、Producers生产者
Producer将消息发布到指定的Topic中,同时Producer也能决定将此消息归属于哪个partition。比如基于"round-robin"方式或者通过其他的一些算法等。
kafka的配置文件中配置了partition的数量和replication的数量。但是这个是默认值,创建topic时不指定partition数量的话才会使用这个默认值。
例子:创建一个叫做"test"的topic,它只要一个分区,一个副本。
> bin/kafka-topics.sh --create --zookeeper 10.0.x.x(zk的地址):2181/kafkagroup --replication-factor 1 --partitions 1 --topic test
可以通过list命令查看创建的topic:
> bin/kafka-topics.sh --list --zookeeper 10.0.x.x:2181/kafkagroup
Test
负载均衡:producer将会和Topic下所有partition leader保持socket连接;消息由producer直接通过socket发送到broker,中间不会经过任何"路由层"。事实上,消息被路由到哪个partition上,由客户端决定。比如可以采用"random"、“key-hash”、"轮询"等,如果一个topic中有多个partitions,那么在producer端实现"消息均衡分发"是必要的。
其中partition leader的位置(host:port)注册在zookeeper中,producer作为zookeeper client,已经注册了watch用来箭筒partition leader的变更事件。
异步发送:将多条消息暂时在客户端buffer起来,并将它们批量的发送到broker,小数据IO太多,会拖慢整体的网络延迟,批量延迟发送事实上提升了网络效率。不过这也有一定的隐患,比如说producer失效时,那些尚未发送的消息将会丢失。
五、Consumers消费者
本质上kafka只支持Topic。每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer。发送到topic的消息,只会被订阅此Topic的每个group中的一个consumer消费。
如果所有的consumer都具有相同的group,这种情况和queue模式很像。消息将会在consumers之间负载均衡。
如果所有的consumer都具有不同的group,那就是"发布-订阅"。消息将会广播给所有消费者。
在kafka中,一个partition中的消息只会被group中的一个consumer消费。每个group中consumer消息消费互相独立。我们可以认为一个group是一个"订阅"者,一个Topic中的每个partition只会被一个"订阅者"中的一个consumer消费,不过consumer可以消费多个partition中的消息。kafka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的。事实上,从Topic角度来说,消息仍不是有序的。
kafka的设计原理决定,对于同一个topic同一个group中不能有多于partition个数的consumer同时消费,否则将意味着某些consumer将无法得到消息。
Guarantees
1)发送到partitions中的消息将会按照它接收的顺序追加到日志中
2)对于消费者而言,它们消费消息的顺序和日志中消息顺序一致
3)如果Topic的"replicationfactor"为N,那么允许N-1个kafka实例失效
六、Zookeeper对kafka的作用
kafka使用zookeeper来存储一些meta信息,并使用了zookeeper watch机制来发现meta信息的变更并作出相应的动作(比如说consumer失效、触发负载均衡等)
zookeeper对kafka的作用可以概括为:
1)Producer端使用zookeeper用来"发现"broker列表,以及和Topic下每个partition leader建立socket连接并发送消息。
2)Broker端使用zookeeper用来注册broker信息,以及监测partition leader存活性。
3)Consumer端使用zookeeper用来注册consumer信息,其中包括consumer消费的partition列表等。同时也用来发现broker列表,并和partition leader建立socket连接,并获取消息。
kafka如果要正常运行,必须配置zookeeper,否则无论是kafka集群还是客户端的生产者和消费者都无法正常的工作。