kafka概要设计
Kafka 在设计之初就需要考虑以下 个方面的问题
- 吞吐量/延时
- 消息持久化
- 负载均衡和故障转移
- 伸缩性
吞吐量/延时
通常来说,吞吐量是某种处理能力的最大值。而对于 Kafka ,它的吞吐量就是每秒能够处理的消息数或者每秒能够处理的字节数。很显然,我们自然希望消息引擎的吞吐越大越好
消息引擎系统还有一个名为延时的性能指标。它衡量的是一段时间间隔,可能是发出某个操作与接收到操作响应(response )之间的时间,或者是在系统中导致某些物理变更的起始时刻与变更正式生效时刻之间的间隔。对于 Kafka 而言,延时可以表示客户端发起请求与服务器处理请求并发送响应给客户端之间的这一段时间。显而易见,延时间隔越短越好
Kafka 是如何做到高吞吐量、低延时的呢?
首先, Kafka 的写入操作是很快的,这主要得益于它对磁盘的使用方法的不同。虽然 Kafka 会持久化所有数据到磁盘,但本质上每次写入操作其实都只是把数据写入到操作系统的页缓存(page cache )中,然后由操作系统自行决定什么时候把页缓存中的数据写回磁盘上。
这样的设计有三个主要优势
-
操作系统页缓存是在内存中分配的,所以消息写入的速度非常快
-
Kafka 不必直接与底层的文件系统打交道。所有烦琐的 1/0 操作都交由操作系统来处理
-
Kafka 写入操作采用追加写入( append )的方式,避免了磁盘随机写操作
Kafka 的消费端是如何做到高吞吐量、低延时的。
之前提到了 Kafka 是把消息写入操作系统的页缓存中的。那么同样地, Kafka 在读取消息时会首先尝试从 OS 的页缓存中读取,如果命中便把消息经页缓存直接发送到网络的 Socket 这个过程就是利用 Linux 平台的 sendfile 系统调用做到的,而这种技术就是大名鼎鼎的零拷贝( Zero Copy )技术。
消息持久化
Kafka把消息持久化到磁盘上。这样做的好处如下。
- 解辑消息发送与消息消费
通过将消息持久化使得生产者方不再需要直接和消费者方藕合,它只是简单地把消息生产出来井交由 Kafka 服务器保存即可,因此提升了整体的吞吐量。
- 实现灵活的消息处理
很多 Kafka 的下游子系统都有这样的需求一一对于已经处理过的消息可能在未来的某个时间点重新处理 次,即所谓的消息重演( message replay )。
消息持久化便可以很方便地实现这样的需求
负载均衡和故障转移
默认情况下 Kafka 的每台服务器都有均等的机会为 Kafka 的客户提供服务,可以把负载分散到所有集群中的机器上,避免出现“耗尽某台服务器”的情况发生。
Kafka 实现负载均衡实际上是通过智能化的分区领导者选举(partition leader election )来实现的。
伸缩性
不论是哪类分布式系统,集群中的每台服务器一定会维护很多内部状态。如果由服务器自己来保存这些状态信息,则必须要处理一致性的问题。相反,如果服务器是无状态的,状态的保存和管理交于专门的协调服务来做(比如 ZooKeeper ),那么整个集群的服务器之间就无须繁重的状态共享,这极大地降低了维护复杂度。 倘若要扩容集群节点,只需简单地启动新的节点机器进行自动负载均衡就可以了。
每台 Kafka 务器上的状态统一交由 ZooKeeper 保管,扩展 Kafka 集群也只需要启动新的 Kafka 服务器即可。
Kafka中的术语解释
broker
Kafka 服务器
Topic
topic 只是一个逻辑概念,代表了一类消息
Partition
topic中的数据分割为一个或多个partition。每个topic至少有一个partition。每个partition中的数据使用多个segment文件存储。,不同partition间的数据丢失了数据的顺序。如果topic有多个partition,消费数据时就不能保证数据的顺序。在需要严格保证消息的消费顺序的场景下,需要将partition数目设为1
offset
topic partition 下的每条消息都被分配一个位移值
replica
副本分为两类 领导者副本( leader replica )和追随者副本( follower replica )
Leader
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition
Follower
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower
ISR-机制解析
kafka 为了保证数据的一致性使用了 ISR 机制
ISR 的全称是:In-Sync Replicas,**ISR **是一个副本的列表,里面存储的都是能跟 leader 数据一致的副本
-
首先我们知道kafka 的数据是多副本的,每个topic 下的每个分区下都有一个 leader 和多个 follower
-
每个follower 的数据都是同步 leader的
-
那么问题就出来了,虽然每个分区都有多个副本,但是如何确定副本的数据和leader 的数据是同步的
-
确定一个副本在 ISR 列表中,有2个判断条件
条件1:根据副本和leader 的交互时间差,如果大于某个时间差 就认定这个副本不行了,就把此副本从 ISR 中剔除
rerplica.lag.time.max.ms=10000 单位ms
条件2:根据 leader 和副本的信息条数差值决定是否从 ISR 中剔除此副本,
rerplica.lag.max.messages=4000 单位条
ISR 中的副本删除或者增加都是通过一个周期调度来管理的
-
kafka 根据 ISR 机制和消息的 ack 方式保证的数据的一致性和保证幂等性(消息是否会重复消费。发送等)
min.insync.replicas=n 配置参数表示 当满足了n个副本的消息确认(n默认为1,最好大于1,因为leader 也在 ISR 列表中),才认为这条消息是发送成功的。
min.insync.replicas 参数只有配合request.required.acks =-1 时才能达到最大的可靠性
request.required.acks 的参数说明:
0:生产者只管发送,不管服务器,消费者是否收到信息
1:只有当leader 确认了收到消息,才确认此消息发送成功
-1:只有 ISR 中的n-1个副本(leader 除外所以n-1)都同步了消息 此消息才确认发送成功
注意生产者发送的消息只有在确认发送成功后才能被消费者消费
Kafka Boker配置参数
Boker的重要的配置参数
name | 默认值 | 描述 |
---|---|---|
broker.id | none | 每一个boker都有一个唯一的id作为它们的名字 |
log.dir | /tmp/kafka-logs | 指定log文件的根目录 |
num.partitions | 1 | 默认分区数 |
zookeeper.connect | localhost:2182/kafka | ZooKeeper连接字符串的格式为:hostname:port,此处hostname和port分别是ZooKeeper集群中某个节点的host和port;为了当某个host宕掉之后你能通过其他ZooKeeper节点进行连接,你可以按照一下方式制定多个hosts: hostname1:port1, hostname2:port2, hostname3:port3. ZooKeeper 允许你增加一个“chroot”路径,将集群中所有kafka数据存放在特定的路径下。当多个Kafka集群或者其他应用使用相同ZooKeeper集群时,可以使用这个方式设置数据存放路径。这种方式的实现可以通过这样设置连接字符串格式,如下所示: hostname1:port1,hostname2:port2,hostname3:port3/chroot/path 这样设置就将所有kafka集群数据存放在/chroot/path路径下。注意,在你启动broker之前,你必须创建这个路径,并且consumers必须使用相同的连接格式。 |
listeners | broker 监听器的 csv 列表,格式是 [协议]: //[主机名]:[端口],[协议]: //[主机名]:[端口]。该参数主要用于客户端连接 broker 使用 如果不指定主机名,则表示绑定默认网卡:如果主机名是 0.0.0.0 ,则表示绑定所有网卡。 Kafka 当前支持的协议类型包括 PLAINTEXT、SSL、SASL_SSL 等。对于未启用安全的 Kafka 集群,使用 PLAINTEXT协议足矣 | |
advertised.listeners | 与 listeners 类似,该参数也是用于发布给 clients 的监昕器,该参数主要用于 IaaS 环境,比如云上的机器通常都配有多块网卡。对于这种机器,用户可以设置该参数绑定公网 IP 供外部 clients 使用。然后配置上面的 listeners 来绑定私网 IP broker 间通信使用 | |
delete.topic.enable | false | 是否允许 Kafka 删除 topic |
log.retention. {hourslminuteslms} | 7 days | Kafka默认只会保存最近7天的数据,井自动删除7天前的数据 |
log.retention.bytes | -1 | 如果说上面那组参数定义了时间维度上的留存策略,那么这个参数便定义了空间维度上的留存策略,即它控制着 Kafka 集群需要为每个消息日志保存多大的数据 。对于大小超过该参数的分区日志而 Kafka 会自动清理该分区的过期日志段文件。该参数默认值是-1,表示 Kafka 永远不会根据消息日志文件总大小来删 |
min.insync.replicas | 1 | 当producer设置request.required.acks为-1时,min.insync.replicas指定replicas的最小数目(必须确认每一个repica的写数据都是成功的),如果这个数目没有达到,producer会产生异常。 |
message.max.bytes | 1000000 | afka broker 能够接收的最大消息大小,默认是 977阻,还不到lMB ,可见是非常小的 |