Kafka复习计划 - Kafka基础知识以及集群参方案和参数

前言

本次复习主要是看的胡夕老师的Kafka核心技术与实战,讲得很好,主要是查缺补漏。

Kafka是一种消息引擎系统。负责的事情也很简单:

  1. 系统A将消息发送给Kafka
  2. 系统B从Kafka中读取系统A发送的消息。

Kafa对于传输的消息编码格式的选择:纯二进制的字节序列。

Kafka中传输消息的模型有两种:

  • 点对点模型(消息队列模型):系统A发送的消息只能被系统B接收。
  • 发布 / 订阅模型:拥有主题Topic概念,可以有多个发布者Publisher向相同的主题发送消息。可以同时被多个订阅者Subscriber接收。

为什么要使用Kafka?(或者A系统为什么不能直接把消息发送给B系统?)回答:削峰填谷

所谓的“削峰填谷”就是指缓冲上下游瞬时突发流量,使其更平滑。对于那种发送能力很强的上游系统,如果没有消息引擎的保护,“脆弱”的下游系统可能会直接被压垮导致全链路服务“雪崩”。

Kafka和其他MQ的区别在哪?比如RabbitMQKafka如何选择?

  1. RabbitMQ属于比较传统的消息队列系统,支持标准的消息队列协议:AMQP、STOMP、MQTT等。
  2. 因此倘若具体的应用需要支持这些协议,那么选择RabbitMQ
  3. RabbitMQ还支持比较复杂的消费路由,而Kafka不支持。

备注:消息传输协议Http这一类网络通信协议是不同的!一般两个进程之间进行数据流的交互有三种方式:

  • 通过数据库的读写:进程1写数据到数据库,进程2从数据库中读数据。
  • 通过服务调用RPC调用或者Rest调用。HTTP协议通常就作为rest方式的底层通讯协议。
  • 通过消息传递的方式:进程1发送消息给中间件,例如Kafka。进程2从中间件中读取消息。而消息传输协议就运用与此。

那竟然RPC调用和发MQ其本质都是不同进程之间数据流的一个交互,那么MQRPC的区别又是什么?

  1. MQ有自己的buffer,能够对抗过载(overloaded)。
  2. MQ支持重试retry
  3. MQ允许发布/订阅功能。

一. Kafka 相关术语

Kafka属于分布式的消息引擎系统。主要提供一套完备的消息发布和订阅解决方案。

首先来说下Kafka的几个最最基础的概念:

  • 消息(Record):Kafka处理的主要对象。
  • 主题(Topic):承载消息的逻辑容器,一般用来区分具体的业务。
  • 生产者(Producer):向主题发布信息的应用程序。可以向一个或者多个主题发送消息。
  • 消费者(Consumer):从主题订阅信息的应用程序。可以同时订阅多个主题的消息。
  • 消费者组(Consumer Group):为了提高吞吐量,将多个消费者实例共同组成一个组,消费同一个主题。但是主题中的每个分区只会被组内的一个消费者实例消费。

其中,生产者和消费者这样的应用程序,我们统一称之为客户端Clients。那么随之对应的,也有服务端这么一个概念。

1.1 实现高可用的手段

Kafka的服务器端由称之为Broker的进程构成。Broker有什么作用呢?

  1. Broker负责接收和处理客户端发送过来的请求。
  2. 负责对消息进行持久化。

高可用手段一:一般Kafka将不同的Broker分散运行在不同的机器上。这样哪怕某台机器宕机,那么其他的机器的Broker也能够对外提供服务。


高可用手段二:备份机制(Replication)。

备份机制的本质就是对数据进行拷贝Kafka中,对这些数据拷贝称之为副本replica),副本分为两种:

  • 领导者副本:Leader Replica负责对外提供服务,与客户端进行交互。
  • 追随者副本:Follower Replica只负责更新领导者副本中的数据。

生产者总是向领导者副本写消息,而消费者也总是从领导者副本中读取消息。追随者副本,注意:不提供对外服务,不提供对外服务,不提供对外服务,重要的事情说三遍。只负责与领导者副本保持同步。

紧接着,为了避免单台Broker上无法容纳更多的数据。Kafka对副本进行分割,也就是所谓的分区Partitioning)。

Kafka中将每个主题划分为多个分区。每个分区包含一组有序的消息日志。生产者生产的每条消息只会被发送到一个分区中。

1.2 Kafka的三层消息架构

有了主题、副本、分区概念之后,Kafka可以分为三层的消息架构:

  • 主题层:每个主题分为M个分区,每个分区又可以分成N个副本。
  • 分区层:每个分区的N个副本中,包含1个领导者副本,负责对外提供服务N-1个追随者副本,负责提供冗余数据
  • 消息层:分区中包含若干条消息,每条消息的位移从0开始并依次递增。

在这里插入图片描述

1.3 Kafka如何持久化数据

Kafka使用消息日志来保存数据:一种在磁盘上只能通过追加写(Append-only 的方式来记录消息的物理文件。

追加写的好处:

  1. 将缓慢的随机IO操作,改为性能较好地顺序IO写操作。

同时,Kafka通过日志段(Log Segment)机制定期的删除消息来回收磁盘空间。

  • Kafka底层,一个日志会进一步细分为多个日志段。
  • 消息在追加写的时候会记录到当前最新的日志段中。
  • 当写满一个日志段后,Kafka就会自动分配一个新的日志段,并将老的日志段封存。
  • 通过定时任务来检查老的日志段是否能够被删除,达到磁盘空间回收的目的。

其他的概念:

  • 重平衡(Rebalance):消费者组内某个消费者实例挂掉后,将分区的所有权从该消费者转移到另一个消费者。
  • 消费者位移(Consumer Offset):每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上。也就是所谓的消费者位移。

1.4 常见问题

为什么Kafka不支持主从分离?

  1. 我们知道RedisMysql都支持主从的一个读写分离。主从分离只是一种架构设计,往往适用于那种读多写少的应用场景。Kafka的主要应用场景是消息引擎,用于频繁地生产/消费消息不属于典型的读多写少场景,因此读写分离方案在这个场景下就不太合适。
  2. Kafka的副本机制方面,使用的是异步消息的拉取。存在leaderfollower之间的不一致性。 若采用读写分离。必然需要去处理数据的不一致性问题。例如:如何实现read-your-writes、如何保证单调读,如何处理消息因果序颠倒的问题等。
  3. 此外,Kafka的一个数据性质和Mysql不一样Kafka中的数据存在着消费的概念,可以看做流数据,当然并不是说消费了这个数据就不在了,对应的Kafka中有着消费者位移的概念,可以看做消费的进度在哪了。而数据库就不存在这样的概念。
  4. Kafka中的分区可以分散到各个Broker上,也能做到负载均衡的效果。也没必要通过主从读写分离的方式来负载均衡。

read-your-writes是什么?

  1. 当生产者成功向Kafka中写入消息的时候。马上使用消费者去读取刚刚生产的消息。
  2. 那么此刻我希望能读到我刚刚写的最新的数据。
  3. 这里再说一下关于主从问题,倘若Kafka允许从副本提供对外服务,那么由于主从之间数据更新的异步性,无法保证客户端从追随者副本中读取到的信息是最新的。也就无法保证read-your-writes

单调读的概念?

  • 对于一个消费者而言,在多次消费的时候,不会存在某一个消息时而存在时而不存在的情况。

二. Kafka集群部署方案

首先从操作系统的角度来看:目前比较常见的操作系统有三种:LinuxWindowsmacOS

2.1 操作系统的选择

考虑操作系统和Kafka之间的适配性,Linux是更好的选择,主要从以下三点来说:

  • IO模型的使用(复习Kafka的时候,特地去复习了下IO模型:IO知识复习)。
  • 数据网络传输效率。
  • 社区支持度。

首先对于IO模型的使用:

  1. Kafka的客户端底层使用了Javaselector
  2. selectorLinux上的实现机制就是epoll,而在Windows平台上的实现机制是select
  3. 因此在这一点上,Kafka部署在Linux上是有优势的,能够获得更高效的IO性能。epoll优势更大。

其次对于网络传输效率方面的考虑:若KafkaLinux上部署,能够享受到零拷贝技术带来的快速数据传输特性。

关于零拷贝的复习:Linux - 零拷贝技术

最后再是社区的支持度:一般人都会选择Linux来搭建。 社区目前对 Windows 平台上发现的 Kafka Bug 不做任何承诺。

2.2 磁盘的选择

我们都知道,固态硬盘比普通的磁盘要快,这里至于为什么快,我感觉也没必要去深入了解,知道就好。而Kafka集群所需要的存储空间还是比较大的,需要考虑这么几个因素:

  1. 新增的消息数。
  2. 消息留存时间。
  3. 平均消息的大小。
  4. 副本的数量。
  5. 消息是否启用压缩功能。

而固态硬盘的优势大,但是成本要高,机械硬盘的成本低,但是容易损坏。这里给个参考。

有条件的情况下,无脑选固态硬盘。
若条件比较苛刻,选择机械硬盘的问题不大。理由如下:

  1. Kafka虽然大量使用磁盘空间,但是使用的方式大部分是顺序读写IO。因此一定程度上规避了机械磁盘的劣势(随机IO读写慢)。
  2. Kafka有自己的冗余机制(副本)来提高可靠性,一定程度上弥补了硬盘容易损坏的缺点。

2.3 重要集群参数

2.3.1 Broker端参数

针对日志存储的相关参数:

  • log.dirs没有默认值(因此必须手动指定),用于指定Broker需要使用的若干个文件目录路径,每个路径之间用 逗号 分割。 最好将目录挂载到不同的物理磁盘上,这样就有两个好处:
  1. 提升读写性能:多块物理磁盘同时读写比单块磁盘要有更高的吞吐量。
  2. 实现故障转移
  • log.dir:表示单个路径,用于补充上一个参数用的。

Kafka的运行一般还依赖于zookeeperzookeeper用于协调管理并保存Kafka集群的所有元数据信息,例如集群中有哪些BrokerTopic、分区等信息。

针对zookeeper的相关参数:

  • zookeeper.connect:用于指定zookeeper的客户端地址,zk1:2181,zk2:2181,同样可以配置多个zookeeper(集群)

注意,若有两套Kafka集群共用一套zookeeper集群,可以使用chroot别名来配置,格式如下:

# kafka1 集群
zk1:2181,zk2:2181,zk3:2181/kafka1
# kafka2 集群
zk1:2181,zk2:2181,zk3:2181/kafka2

只需要在zookeeper集群的最后添加 / [kafka集群名] 即可。


针对Broker之间连接和通信的参数:

  • listeners:用于告诉外部连接者需要通过什么协议访问指定的主机名和端口开放的Kafka服务。
  • advertised.listeners:对外发布的监听器,即面向外网。如果客户端仅仅在内网中使用,那么就不需要配置该参数。

每个监听器对于的格式为若干个逗号分割的三元组:<协议名称,主机名,端口号>,例如:

CONTROLLER: //localhost:9092

针对Topic的相关参数:

  • auto.create.topics.enable:是否允许自动创建Topic最好设置为false,避免因为操作失误创建错误名称的主题。
  • unclean.leader.election.enable:是否允许Unclean Leader选举。
  • auto.leader.rebalance.enable:是否允许定期进行Leader选举(在满足一定的条件下)。建议将其改成false,因为将其设置为true会导致一个正常的Leader副本被其他的副本替换本质上的性能收益几乎没有,反而替换一次的Leader代价却很高。因此生产环境中建议设置为false

关于Unclean Leader选举:

背景:Kafka中,每个分区都有多个副本来提供高可用和备份,但是只有一个副本能作为Leader。因此副本之间需要选举出一个Leader来,但并非所有副本都具有资格参与竞选,只有保存数据比较多的副本才有资格。

那么当拥有竞选资格的副本都挂了的前提下,此时还需不需要进行Leader选举? 该参数用于这样的情形:

  • unclean.leader.election.enable = false:即使发生上述情况,依旧不会竞选新Leader后果:该分区不可用。
  • unclean.leader.election.enable = true:若发生上述情况,此时Kafka允许从落后的副本中竞选出新副本。后果:可能有数据丢失的情况(落后副本本身保存的数据就不全)

针对数据留存的相关参数:

  • log.retention.{hour|minutes|ms}:控制一条消息数据能够被保存多长的时间,优先级:ms > minutes > hour
  • log.retention.bytes:指定Broker为消息保存的总磁盘容量大小。默认值为 -1 ,意思是条件允许的情况下,可以在该Broker上保存任意量的数据。一般来说使用场景在于:限制某个用户使用的磁盘空间。
  • message.max.bytes:控制Broker能够接收的最大消息大小。默认1000012 bytes,还不到1MB实际生产上的消息大小往往可能会超过1MB

注意:上述提及的所有参数,都不要使用默认值的参数,最好要自己手动配置。
注意:上述提及的所有参数,都不要使用默认值的参数,最好要自己手动配置。
注意:上述提及的所有参数,都不要使用默认值的参数,最好要自己手动配置。

2.3.2 Topic级别参数

前提:Topic级别参数的优先级 > Broker端参数(全局)

重要的三个参数(和上面全局的配置非常相似):

  • retention.ms:规定了该Topic消息被保存的时长,默认7天,可覆盖全局值。
  • retention.bytes:规定了需要为该Topic预留多大的磁盘空间。
  • max.message.bytes:能够正常接收该Topic的最大消息大小。

在创建Topic的时候,可以通过以下命令来执行:

bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic transaction \
--partitions 1 --replication-factor 1 --config retention.ms=15552000000 \
--config max.message.bytes=5242880

在修改Topic的时候,可以通过以下命令来执行:

bin/kafka-configs.sh --zookeeper localhost:2181 --entity-type topics \
--entity-name transaction --alter --add-config max.message.bytes=10485760

2.3.3 JVM参数

参考配置如下:

# 先设置对于的环境变量
export KAFKA_HEAP_OPTS=--Xms6g --Xmx6g
export KAFKA_JVM_PERFORMANCE_OPTS= -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true
# 启动Kafka
bin/kafka-server-start.sh config/server.properties

2.3.4 操作系统参数

Kafka中影响相对较大的OS参数有四个:

  • 文件描述符限制。
  • 文件系统类型:指的是诸如ext3、ext4或者XFS这样的日志文件系统,生产上推荐使用XFS
  • Swappiness:将其设置为一个较小的值,倘若设置为0,当物理内存耗尽时,操作系统会触发 OOM killer 这个组件,它会随机挑选一个进程然后将其停止,不会留下任何的预警
  • 提交时间。

重点说下文件描述符限制。文件描述符限制实际上调大此值并不会造成什么严重的影响。当我们遇到这样的报错信息:Too many open files,就说明咱们的文件描述符限制太小了,需要将其调大。例如:

ulimit -n 1000000

然后说下提交时间(Flush刷新落盘时间)。

首先kafka中,何时认为数据已经写入成功?只要数据被写入到操作系统中的页缓存(Page Cache)上就可以了,即内核缓冲区中的一部分。而并不是说等数据被写入磁盘才认定为成功。

写入到页缓存中的数据,操作系统会根据LRU算法定期地将其写入到物理磁盘上,而这个定期就是由提交时间来确定的,默认是5秒,一般情况下,我们可以适当的正常这个时间。

哪怕页缓存中的数据在写入到磁盘之前宕机了,发生了数据的丢失,但是鉴于Kafka的多副本机制,对于数据丢失的情况有所改善,因此这里可以稍微增加点提交时间,提升性能也是可以的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值