Kafka学习(1)入门

214 阅读6分钟

消息引擎(消息队列)是后端最常用的中间件,其主要功能有解耦、异步、削峰。而Kafka是Apache开发的最常用的消息引擎之一。

安装

直接到Kafka的官网下载二进制包然后解压就行了,Kafka需要Java的运行环境。首先启动Zookeeper作为Kafka的服务协调工具,之后启动Kafka就行了。

bin/zookeeper-server-start.sh config/zookeeper.properties
bin/kafka-server-start.sh config/server.properties

服务启动之后就要创建一个Topic来进行消息的发送和接收:

bin/kafka-topics.sh --create --zookeeper localhost:2181 --topic test --partitions 1 --replication-factor 1
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test

最后就可以通过生产者和消费者来进行消息的发送和接收:

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning

消息引擎范式

消息引擎有两种范式:消息队列模型和发布/订阅模型。

消息队列模型常用于进程间通信(IPC),发送者将消息发送到队列指定位置,接收者从指定位置读取消息。一旦消息被消费,就会从队列中移除,这也就意味着一个消息只能被一个消费者消费。

发布/订阅模型则相反,发布者发布消息到Topic之后,所有订阅了这个Topic的订阅者就都可以读取到这个消息。

Kafka概要设计

性能

Kafka的写入操作是很快的,主要是因为Kafka的写入实际上是写入到操作系统的页缓存中去的,这样做有一下几个好处:

  • 页缓存是在内存中的,写入很快
  • Kafka不必进行繁琐的IO操作,底层细节都交给操作系统
  • Kafka的写入是使用追加(append)方式的,避免了磁盘随机写入

也正是因为采用追加写入的方式,且不允许修改之前的消息,所以其属于典型的磁盘顺序访问型操作。而磁盘顺序写入的性能其实并不差,因此Kafka的消息发送吞吐量是很高的,实际使用中能达到每秒几万甚至几十万条消息写入。

对于Kafka的消费端,因为写入是写入到OS的页缓存中的,所以读取时也会先从页缓存中读取,如果命中就可以直接将消息从页缓存发送到网络Socket中去。这个过程是通过Linux的sendfile系统调用来实现的,这种技术就是非常有名的零拷贝(zero copy)技术。通过这种技术就可以避免消息从页缓存拷贝到用户态再拷贝回内核Socket缓冲区的代价。同时DMA可以直接从页缓存中读取数据,也就是说不需要将数据从页缓存拷贝到Socket缓冲区中。

消息持久化

Kafka是需要将消息持久化到磁盘中去,这样做主要有以下两个好处:

  • 解耦消息发送和接收:生产者只需要将消息交给Kafka而不需要考虑消费者的状态,提高了系统的吞吐量。
  • 实现灵活的消息处理:消费者可以对已经处理过的消息再次处理,也就是消息重演。

Kafka的消息持久化和普通的系统不一样。普通系统一般会尽可能使用内存,当内存不足时再一次性刷入磁盘;而Kafka则相反,所有数据会被立即写入到文件系统中持久化,当写入成功之后才会返回成功的消息。这样既可以实时保存数据,又可以减少Kafka的内存消耗,将更多空间给页缓存使用。通过这种持久化策略,Kafka就能够做到高可靠。

负载均衡和故障转移

Kafka通过智能化的分区领导者选举来实现负载均衡,可以在集群上所有机器上以均等机会分散各个partition的leader,从而整体上实现负载均衡。

而故障转移是通过Zookeeper实现的,每台Kafka服务器都会以会话形式注册到Zookeeper上。当服务器故障时,会话就会超时失效,Kafka集群就会选举另一台服务器来代替这台服务器,从而实现了高可用。

伸缩性

Kafka服务器的状态都统一交给Zookeeper服务器保管,自身只保存很轻量的内部状态,集群中维护一致性的代价是很低的。因此扩展Kafka集群就只需要很简单地启动一台Kafka服务器就行了。

基本概念和术语

Broker:即上文中提到的Kafka服务器

消息:消息由首部和Key,Value组成,首部包含时间戳、版本号、CRC等字段。Key用于对消息做partition时使用,决定消息被保存在topic的哪个partition中;Value即消息的实际数据;时间戳用于流式处理以及其他依赖时间的处理语义。

Topic:对消息进行逻辑上的分类,用来区分实际业务。

Partition:partition是不可修改的有序消息序列。每个Topic由多个partition构成,没有太多业务含义,主要是为了提升系统吞吐量。

Offset:partition上的每条消息都会被分配一个唯一序列号offset,一般是从0开始的整数。使用<topic, partition, offset>三元组就可以定位一条消息。

Replica:消息不能够只保存一份,否则存放消息的partition的服务器挂掉了话消息就丢失了,因此必须要有冗余,备份日志就被称为副本replica。副本分为领导者副本和追随者副本,只有领导者副本对外提供服务。Kafka保证同一partition的replica不会放在同一个broker上。

LeaderFollower:leader对外提供服务,而follower只是被动追随leader的状态,保持和leader的同步。follower的唯一作用就是leader的候补,当leader挂掉之后就会从follower中选举出新的leader。

ISR:即in-sync replica,与leader replica保持同步的replica集合。对于有N个replica的partition,并不是说挂掉N-1个replica仍能保证不丢失数据。只有ISR中的replica才能称为leader,也只有当ISR中的所有replica都接收到同一条消息,这条消息才能称为已提交状态。Kafka承诺只要ISR中至少存在一个replica,“已提交” 状态的消息就不会丢失。正常情况下所有replica都应该在ISR集合中,但因为某些原因一部分replica会落后leader进度。当落后到一定程度时,kafka就会将其踢出ISR,当进度再次跟上时就会再加入ISR。