【后端-Kafka】1、Kafka 超详细介绍

这篇具有很好参考价值的文章主要介绍了【后端-Kafka】1、Kafka 超详细介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、概念

Kafka 是消息引擎(Messaging System),其是一组规范。企业利用这组规范在不同系统之间传递语义准确的消息,实现松耦合的异步式数据传递。

实现的目标,就是 系统A 发消息给 消息引擎,系统B 从消息引擎读取 A发送的消息。

Kafka 用二进制存储数据。其同时支持点对点模型、发布/订阅模型两种。

  • 点对点模型:也叫消息队列模型。如果拿上面那个“民间版”的定义来说,那么系统 A 发送的消息只能被系统 B 接收,其他任何系统都不能读取 A 发送的消息。日常生活的例子比如电话客服就属于这种模型:同一个客户呼入电话只能被一位客服人员处理,第二个客服人员不能为该客户服务。
  • 发布 / 订阅模型:与上面不同的是,它有一个主题(Topic)的概念,你可以理解成逻辑语义相近的消息容器。该模型也有发送方和接收方,只不过提法不同。发送方也称为发布者(Publisher),接收方称为订阅者(Subscriber)。和点对点模型不同的是,这个模型可能存在多个发布者向相同的主题发送消息,而订阅者也可能存在多个,它们都能接收到相同主题的消息。生活中的报纸订阅就是一种典型的发布 / 订阅模型。

消息引擎的作用:

  • 削峰填谷:缓冲上下游瞬时突发流量,使其更平滑。特别是对于那种发送能力很强的上游系统,如果没有消息引擎的保护,“脆弱”的下游系统可能会直接被压垮导致全链路服务“雪崩”。但是,一旦有了消息引擎,它能够有效地对抗上游的流量冲击,真正做到将上游的“峰”填满到“谷”中,避免了流量的震荡。
  • 发送方和接收方的松耦合,这也在一定程度上简化了应用的开发,减少了系统间不必要的交互。

名词术语如下:

  • 消息:Record。Kafka 是消息引擎嘛,这里的消息就是指 Kafka 处理的主要对象。
  • 主题:Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。
  • 分区:Partition。一个有序不变的消息序列。每个主题下可以有多个分区。
  • 消息位移:Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。
  • 副本:Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。
  • 生产者:Producer。向主题发布新消息的应用程序。
  • 消费者:Consumer。从主题订阅新消息的应用程序。
  • 消费者位移:Consumer Offset。表征消费者消费进度,每个消费者都有自己的消费者位移。
  • 消费者组:Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。
  • 重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

【后端-Kafka】1、Kafka 超详细介绍,后端,kafka,java,分布式

Kafka的副本为何不允许对外提供服务?

  • 如果允许follower副本对外提供读服务(主写从读),首先会存在数据一致性的问题,消息从主节点同步到从节点需要时间,可能造成主从节点的数据不一致。主写从读无非就是为了减轻leader节点的压力,将读请求的负载均衡到follower节点,如果Kafka的分区相对均匀地分散到各个broker上,同样可以达到负载均衡的效果,没必要刻意实现主写从读增加代码实现的复杂程度

Consumer Group:

  • 在一个消费者组下,一个分区只能被一个消费者消费,但一个消费者可能被分配多个分区,因而在提交位移时也就能提交多个分区的位移。
  • 如果Consumer Group内 consumer的数量 > partition 的数量,则有一个消费者将无法分配到任何分区,处于idle状态。

Producer:

  • 如果producer指定了要发送的目标分区,消息自然是去到那个分区;否则就按照producer端参数partitioner.class指定的分区策略来定;如果你没有指定过partitioner.class,那么默认的规则是:看消息是否有key,如果有则计算key的murmur2哈希值%topic分区数;如果没有key,按照轮询的方式确定分区。

监控:

  • JMXTrans + InfluxDB + Grafana(推荐)
  • Kafka manager
  • kafka eagle
  • Kafka Offset Explorer 支持Mac、Win、Linux

二、部署

2.1 磁盘

根据保留的消息数量,预估磁盘占用:

  • 新增消息数
  • 消息留存时间
  • 平均消息大小
  • 备份数
  • 是否启用压缩
假设你所在公司有个业务每天需要向 Kafka 集群发送 1 亿条消息,每条消息保存两份以防止数据丢失,另外消息默认保存两周时间。现在假设消息的平均大小是 1KB,那么你能说出你的 Kafka 集群需要为这个业务预留多少磁盘空间吗?

我们来计算一下:每天 1 亿条 1KB 大小的消息,保存两份且留存两周的时间,那么总的空间大小就等于 1 亿 * 1KB * 2 / 1000 / 1000 = 200GB。一般情况下 Kafka 集群除了消息数据还有其他类型的数据,比如索引数据等,故我们再为这些数据预留出 10% 的磁盘空间,因此总的存储容量就是 220GB。既然要保存两周,那么整体容量即为 220GB * 14,大约 3TB 左右。Kafka 支持数据的压缩,假设压缩比是 0.75,那么最后你需要规划的存储空间就是 0.75 * 3 = 2.25TB。

2.2 网络

根据QPS和带宽,预估服务器数量:(注意:业界带宽资源一般用Mbps而不是MBps衡量)

假设你公司的机房环境是千兆网络,即 1Gbps,现在你有个业务,其业务目标或 SLA 是在 1 小时内处理 1TB 的业务数据。那么问题来了,你到底需要多少台 Kafka 服务器来完成这个业务呢?

让我们来计算一下,由于带宽是 1Gbps,即每秒处理 1Gb 的数据,假设每台 Kafka 服务器都是安装在专属的机器上,也就是说每台 Kafka 机器上没有混布其他服务,毕竟真实环境中不建议这么做。通常情况下你只能假设 Kafka 会用到 70% 的带宽资源,因为总要为其他应用或进程留一些资源。

根据实际使用经验,超过 70% 的阈值就有网络丢包的可能性了,故 70% 的设定是一个比较合理的值,也就是说单台 Kafka 服务器最多也就能使用大约 700Mb 的带宽资源。

稍等,这只是它能使用的最大带宽资源,你不能让 Kafka 服务器常规性使用这么多资源,故通常要再额外预留出 2/3 的资源,即单台服务器使用带宽 700Mb / 3 ≈ 240Mbps。需要提示的是,这里的 2/3 其实是相当保守的,你可以结合你自己机器的使用情况酌情减少此值。

好了,有了 240Mbps,我们就可以计算 1 小时内处理 1TB 数据所需的服务器数量了。
根据这个目标,我们每秒需要处理 1TB/3600s=1024GB/3600s=1024-000MB/3600s=1024-000-0Mb/3600s=2844Mbps 的数据,除以 240Mbps,约等于 10 台服务器。如果消息还需要额外复制两份,那么总的服务器台数还要乘以 3,即 30 台。

2.3 CPU

通常情况下Kafka不太占用CPU,因此没有这方面的最佳实践出来。但有些情况下Kafka broker是很耗CPU的:

  1. server和client使用了不同的压缩算法;
  2. server和client版本不一致造成消息格式转换;
  3. broker端解压缩校验

不过相比带宽资源,CPU通常都不是瓶颈

2.4 partition 的数量

网上有一些分区制定的建议,我觉得这个粗粒度的方法就很好,值得一试:

  1. 首先你要确定你业务的SLA,比如说你希望你的producer TPS是10万条消息/秒,假设是T1
  2. 在你的真实环境中创建一个单分区的topic测试一下TPS,假设是T2
  3. 你需要的分区数大致可以等于T1 / T2

2.5 配置

各配置项含义

2.5.0 静动态配置、configs.sh、topics.sh

静态配置与动态配置

  • config/server.properties 是静态配置,其任意项的改动,均需重启 kafka broker 才能生效。
  • 除此之外还有动态配置,可通过 kafka-config.sh 或 kafka-topics.sh 设置和查看。其原理是设置到 zk 上,会覆盖静态配置。

在 官方配置文档 可看到参数类型(UpdateMode)。参数的优先级别为 per-broker参数 > cluster-wide参数 > static参数 > Kafka默认值

  • read-only:被标记为read-only的参数和原来的参数行为一样,只有重启Broker,才能令修改生效。
  • per-broker:被标记为per-broker的参数属于动态参数,当且仅当通过脚本修改它之后,只会在对应的Broker上生效。
  • cluster-wide:被标记为cluster-wide的参数也属于动态参数,当且仅当通过脚本修改它之后,会在整个集群范围内生效。

kafka-config.sh
kafka-topics.sh 配置方式如下:

./kafka-topics.sh --bootstrap-server 192.168.2.99:9092 --all --describe --topic topica # 查看某topic的所有配置
./kafka-topics.sh --zookeeper 192.168.2.99:2181 --topic topica --alter --config retention.ms=3600000 # 设置某topic的某配置
./kafka-topics.sh --bootstrap-server 192.168.2.99:9092 --entity-type topics --entity-name topica --alter --delete-config retention.ms # 删除某topic的某配置

kafka-configs.sh 配置方式如下:

./kafka-configs.sh --bootstrap-server 192.168.2.99:9092 --describe --entity-type topics --entity-name topica --all # 查看某topic的配置, 其中可看到各项的静态参数/动态参数/最终生效的参数
./kafka-configs.sh --bootstrap-server 192.168.2.99:9092 --entity-type topics --entity-name topica --alter --delete-config retention.ms  # 删除某topic的某配置
./kafka-configs.sh --bootstrap-server 192.168.2.99:9092 --entity-type brokers --entity-name 1 --alter --add-config log.retention.bytes=1024000 # 添加某broker的配置

2.5.1 broker 配置

最为核心的三个配置 broker.id、log.dir、zookeeper.connect 。Broker配置有一个博主有着很详细的解释,欢迎参考:https://www.orchome.com/472

------------------------------------------- 系统 相关 -------------------------------------------
## 每一个broker在集群中的唯一标示,要求是正数。在改变IP地址,不改变broker.id的话不会影响consumers
broker.id =1
 
## kafka数据的存放地址,多个地址的话用逗号分割 /tmp/kafka-logs-1,/tmp/kafka-logs-2。## 每当创建新partition时,都会选择在包含最少 partitions的路径下进行。注:这个目录下不能有其他非kafka的目录,不然会导致kafka集群无法启动
log.dirs = /tmp/kafka-logs
 
## 提供给客户端响应的端口
port =9092
 
## 消息体的最大大小,单位是字节
message.max.bytes =1000000
 
## broker 处理消息的最大线程数,一般情况下不需要去修改
num.network.threads =3
 
## broker处理磁盘IO 的线程数 ,数值应该大于你的硬盘数
num.io.threads =8
 
## 一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改
background.threads =4
 
## 等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,算是一种自我保护机制
queued.max.requests =500
 
## broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置
host.name
 
## 打广告的地址,若是设置的话,会提供给producers, consumers,其他broker连接,具体如何使用还未深究
advertised.host.name
 
## 广告地址端口,必须不同于port中的设置
advertised.port
 
## socket的发送缓冲区,socket的调优参数SO_SNDBUFF
socket.send.buffer.bytes =100*1024
 
## socket的接受缓冲区,socket的调优参数SO_RCVBUFF
socket.receive.buffer.bytes =100*1024
 
## socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖
socket.request.max.bytes =100*1024*1024
 
------------------------------------------- LOG 相关 -------------------------------------------
## topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,文件超过指定大小会重新创建一个文件,会被topic创建时的指定参数覆盖
log.segment.bytes =1024*1024*1024
 
## 这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment,会被topic创建时的指定参数覆盖
log.roll.hours =24*7
 
## 日志清理策略 默认值是delete, 选择有delete和compact 主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖
log.cleanup.policy = delete
 
## 数据存储的最大时间 超过这个时间 会根据log.cleanup.policy设置的策略处理数据,也就是消费端能够多久去消费数据
## log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.minutes=7days

## 指定日志每隔多久检查看是否可以被删除,默认1分钟
log.cleanup.interval.mins=1
 
## topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes 。-1没有大小限制
## log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.bytes=-1
 
## 日志清除程序检查日志是否满足被删除的频率(以毫秒为单位) log.cleanup.policy中设置的策略
log.retention.check.interval.ms=300000
 
## 是否开启日志压缩
log.cleaner.enable=false
 
## 日志压缩运行的线程数
log.cleaner.threads =1
 
## 日志压缩时候处理的最大大小
log.cleaner.io.max.bytes.per.second=None
 
## 日志压缩去重时候的缓存空间 ,在空间允许的情况下,越大越好
log.cleaner.dedupe.buffer.size=500*1024*1024
 
## 日志清理时候用到的IO块大小 一般不需要修改
log.cleaner.io.buffer.size=512*1024
 
## 日志清理中hash表的扩大因子 一般不需要修改
log.cleaner.io.buffer.load.factor =0.9
 
## 当没有日志要清理时,休眠的时间
log.cleaner.backoff.ms =15000
 
## 日志清理的频率控制,越大意味着更高效的清理,同时会存在一些空间上的浪费,会被topic创建时的指定参数覆盖
log.cleaner.min.cleanable.ratio=0.5
 
## 对于压缩的日志保留的最长时间,也是客户端消费消息的最长时间,同log.retention.minutes的区别在于一个控制未压缩数据,一个控制压缩后的数据。会被topic创建时的指定参数覆盖
log.cleaner.delete.retention.ms =1day
 
## 对于segment日志的索引文件大小限制,会被topic创建时的指定参数覆盖
log.index.size.max.bytes =10*1024*1024
 
## 当执行一个fetch操作后,需要一定的空间来扫描最近的offset大小,设置越大,代表扫描速度越快,但是也更好内存,一般情况下不需要搭理这个参数
log.index.interval.bytes =4096
 
## log文件"sync"到磁盘之前累积的消息条数
## 因为磁盘IO操作是一个慢操作,但又是一个"数据可靠性"的必要手段,所以此参数的设置,需要在"数据可靠性"与"性能"之间做必要的权衡.
## 如果此值过大,将会导致每次"fsync"的时间较长(IO阻塞);如果此值过小,将会导致"fsync"的次数较多,这也意味着整体的client请求有一定的延迟.
## 物理server故障,将会导致没有fsync的消息丢失.
log.flush.interval.messages=None
 
## 检查是否需要固化到硬盘的时间间隔
log.flush.scheduler.interval.ms =3000
 
## 仅仅通过interval来控制消息的磁盘写入时机,是不足的.
## 此参数用于控制"fsync"的时间间隔,如果消息量始终没有达到阀值,但是离上一次磁盘同步的时间间隔达到阀值,也将触发.
log.flush.interval.ms = None
 
## 文件在索引中清除后保留的时间 一般不需要去修改
log.delete.delay.ms =60000
 
## 控制上次固化硬盘的时间点,以便于数据恢复 一般不需要去修改
log.flush.offset.checkpoint.interval.ms =60000
 
------------------------------------------- TOPIC 相关 -------------------------------------------
## 是否允许自动创建topic ,若是false,就需要通过命令创建topic
auto.create.topics.enable =true
 
## 一个topic ,默认分区的replication个数 ,不得大于集群中broker的个数
default.replication.factor =1
 
## 每个topic的分区个数,若是在topic创建时候没有指定的话 会被topic创建时的指定参数覆盖
num.partitions =1
## 实例 --replication-factor 3 --partitions 1 --topic replicated-topic :名称为replicated-topic的topic有一个分区,分区被复制到三个broker上。
 
----------------------------------复制(Leader、replicas) 相关 ----------------------------------
## partition leader与replicas之间通讯时,socket的超时时间
controller.socket.timeout.ms =30000
 
## partition leader与replicas数据同步时,消息的队列尺寸
controller.message.queue.size=10
 
## replicas响应partition leader的最长等待时间,若是超过这个时间,就将replicas列入ISR(in-sync replicas),并认为它是死的,不会再加入管理中
replica.lag.time.max.ms =10000
 
## 如果follower落后与leader太多,将会认为此follower[或者说partition relicas]已经失效
## 通常,在follower与leader通讯时,因为网络延迟或者链接断开,总会导致replicas中消息同步滞后
## 如果消息滞后太多,leader将认为此follower网络延迟较大或者消息吞吐能力有限,将会把此replicas迁移到其他follower中.
## 在broker数量较少,或者网络不足的环境中,建议提高此值.
replica.lag.max.messages =4000
 
##follower与leader之间的socket超时时间
replica.socket.timeout.ms=30*1000
 
## leader复制时候的socket缓存大小
replica.socket.receive.buffer.bytes=64*1024
 
## replicas每次获取数据的最大大小
replica.fetch.max.bytes =1024*1024
 
## replicas同leader之间通信的最大等待时间,失败了会重试
replica.fetch.wait.max.ms =500
 
## fetch的最小数据尺寸,如果leader中尚未同步的数据不足此值,将会阻塞,直到满足条件
replica.fetch.min.bytes =1
 
## leader 进行复制的线程数,增大这个数值会增加follower的IO
num.replica.fetchers=1
 
## 每个replica检查是否将最高水位进行固化的频率
replica.high.watermark.checkpoint.interval.ms =5000
 
## 是否允许控制器关闭broker ,若是设置为true,会关闭所有在这个broker上的leader,并转移到其他broker
controlled.shutdown.enable =false
 
## 控制器关闭的尝试次数
controlled.shutdown.max.retries =3
 
## 每次关闭尝试的时间间隔
controlled.shutdown.retry.backoff.ms =5000
 
## 是否自动平衡broker之间的分配策略
auto.leader.rebalance.enable =false
 
## leader的不平衡比例,若是超过这个数值,会对分区进行重新的平衡
leader.imbalance.per.broker.percentage =10
 
## 检查leader是否不平衡的时间间隔
leader.imbalance.check.interval.seconds =300
 
## 客户端保留offset信息的最大空间大小
offset.metadata.max.bytes
 
----------------------------------ZooKeeper 相关----------------------------------
## zookeeper集群的地址,可以是多个,多个之间用逗号分割 hostname1:port1,hostname2:port2,hostname3:port3
zookeeper.connect = localhost:2181
 
## ZooKeeper的最大超时时间,就是心跳的间隔,若是没有反映,那么认为已经死了,不易过大
zookeeper.session.timeout.ms=6000
 
## ZooKeeper的连接超时时间
zookeeper.connection.timeout.ms =6000
 
## ZooKeeper集群中ZK follower可落后与leader多久。
zookeeper.sync.time.ms =2000

2.5.2 consumer 配置

最为核心的配置是group.id、zookeeper.connect。Consumer配置有一个博主有着很详细的解释,欢迎参考:https://www.orchome.com/535

## Consumer归属的组ID,broker是根据group.id来判断是队列模式还是发布订阅模式,非常重要
 group.id=
 
## 消费者的ID,若是没有设置的话,会自增
 consumer.id=
 
## 一个用于跟踪调查的ID ,最好同group.id相同
 client.id = group id value
 
## 对于zookeeper集群的指定,可以是多个 hostname1:port1,hostname2:port2,hostname3:port3 必须和broker使用同样的zk配置## 现在最新的配置为bootstrap.servers
 zookeeper.connect=localhost:2182
 
## zookeeper的心跳超时时间,查过这个时间就认为是dead消费者
 zookeeper.session.timeout.ms =6000
 
## zookeeper的等待连接时间
 zookeeper.connection.timeout.ms =6000
 
## zookeeper的follower同leader的同步时间
 zookeeper.sync.time.ms =2000
 
## 当zookeeper中没有初始的offset时候的处理方式 。smallest :重置为最小值 largest:重置为最大值 anythingelse:抛出异常
 auto.offset.reset = largest
 
## socket的超时时间,实际的超时时间是:max.fetch.wait + socket.timeout.ms.
 socket.timeout.ms=30*1000
 
## socket的接受缓存空间大小
 socket.receive.buffer.bytes=64*1024
 
##从每个分区获取的消息大小限制
 fetch.message.max.bytes =1024*1024
 
## 是否在消费消息后将offset同步到zookeeper,当Consumer失败后就能从zookeeper获取最新的offset
 auto.commit.enable =true
 
## 自动提交的时间间隔
 auto.commit.interval.ms =60*1000
 
## 用来处理消费消息的块,每个块可以等同于fetch.message.max.bytes中数值
 queued.max.message.chunks =10
 
## 当有新的consumer加入到group时,将会reblance,此后将会有partitions的消费端迁移到新的consumer上,如果一个consumer获得了某个partition的消费权限,## 那么它将会向zk注册"Partition Owner registry"节点信息,但是有可能此时旧的consumer尚没有释放此节点, 此值用于控制,注册节点的重试次数.
 rebalance.max.retries =4
 
## 每次再平衡的时间间隔
 rebalance.backoff.ms =2000
 
## 每次重新选举leader的时间
 refresh.leader.backoff.ms
 
## server发送到消费端的最小数据,若是不满足这个数值则会等待,直到满足数值要求
 fetch.min.bytes =1
 
## 若是不满足最小大小(fetch.min.bytes)的话,等待消费端请求的最长等待时间
 fetch.wait.max.ms =100
 
## 指定时间内没有消息到达就抛出异常,一般不需要改
 consumer.timeout.ms = -1

2.5.3 producer 配置

比较核心的配置:bootstrap.servers、acks、producer.type、serializer.class。Producer配置有一个博主有着很详细的解释,欢迎参考:https://www.orchome.com/511

## 消费者获取消息元信息(topics, partitions and replicas)的地址,配置格式是:host1:port1,host2:port2,也可以在外面设置一个vip## 无需添加所有的集群地址,kafka会根据提供的地址发现其他的地址(你可以多提供几个,以防提供的服务器关闭) bootstrap.servers=

## 消息的确认模式
  ## 0:不保证消息的到达确认,只管发送,低延迟但是会出现消息的丢失,在某个server失败的情况下,有点像TCP
  ## 1:发送消息,并会等待leader 收到确认后,一定的可靠性
  ## -1:发送消息,等待leader收到确认,并进行复制操作后,才返回,最高的可靠性
 acks = 0

## 消息发送的最长等待时间
 request.timeout.ms = 10000

## socket的缓存大小
 send.buffer.bytes=100*1024

## key的序列化方式,若是没有设置,同serializer.class
 key.serializer.class

## 分区的策略,默认是取模
 partitioner.class=kafka.producer.DefaultPartitioner

## 消息的压缩模式,默认是none,可以有gzip和snappy
 compression.codec = none

## 可以针对默写特定的topic进行压缩
 compressed.topics=null

## 消息发送失败后的重试次数
 message.send.max.retries = 3

## 每次失败后的间隔时间
 retry.backoff.ms = 100

## 生产者定时更新topic元信息的时间间隔 ,若是设置为0,那么会在每个消息发送后都去更新数据
 topic.metadata.refresh.interval.ms = 600 * 1000

## 用户随意指定,但是不能重复,主要用于跟踪记录消息
 client.id=""

-------------------消息模式 相关 -----------------------------------
## 生产者的类型 async:异步执行消息的发送 sync:同步执行消息的发送
 producer.type=sync

## 异步模式下,那么就会在设置的时间缓存消息,并一次性发送
 queue.buffering.max.ms = 5000

## 异步的模式下 最长等待的消息数
 queue.buffering.max.messages = 10000

## 异步模式下,进入队列的等待时间 若是设置为0,那么要么进入队列,要么直接抛弃
 queue.enqueue.timeout.ms = -1

## 异步模式下,每次发送的最大消息数,前提是触发了queue.buffering.max.messages或是queue.buffering.max.ms的限制
 batch.num.messages=200

## 消息体的系列化处理类 ,转化为字节流进行传输
 serializer.class = kafka.serializer.DefaultEncoder

三、命令

各命令脚本使用说明
2.2以上用–bootstrap-server,2.2以下用–zookeeper

3.0 --bootstrap-server 和 --zookeeper 的区别

  • 在旧版本的Kafka(0.9.0)中,Kafka用于将数据存储在Kafka服务器上,所有与偏移量相关的信息(如当前分区偏移量)都存储在zookeeper中。因此,消费者运行它需要数据和元数据。所以为了获取元数据,它必须调用zookeeper。这就是为什么它同时使用zookeeper和卡夫卡。

  • 在Kafka的新版本中(即0.10.0及以上版本),它将所有主题元数据信息(总分区及其当前偏移量)存储在同一Kafka服务器上的__consumer_offset主题中。所以现在只有Kafka broker需要与zookeeper通信,消费者从Kafka broker本身获得所有数据和元数据,所以它现在不再需要与zookeeper通信。
    参考
    参考

3.1 查看版本

$ ./kafka-topics.sh --version
2.8.0 (Commit: jutyurhfdjhsrhrjfgsd)

可以进入kafka/libs文件夹查看jar包,像kafka_2.12-1.1.0.jar,其中2.12是Scala版本,1.1.0是Kafka版本。
$ cd kafka/libs
若其中有kafka_2.12-2.8.0.jar,则版本为2.8.0

3.2 找kafka和zookeeper的ip/port

root@master~# kubectl get svc -n kafka -o wide
NAME        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE   SELECTOR
bootstrap   ClusterIP   10.109.83.55    <none>        9092/TCP            15d   app=kafka
broker      ClusterIP   None            <none>        9092/TCP            15d   app=kafka
outside-0   NodePort    10.108.34.0     <none>        32400:32400/TCP     15d   app=kafka,kafka-broker-id=0
outside-1   NodePort    10.104.65.215   <none>        32401:32401/TCP     15d   app=kafka,kafka-broker-id=1
outside-2   NodePort    10.99.118.241   <none>        32402:32402/TCP     15d   app=kafka,kafka-broker-id=2
pzoo        ClusterIP   None            <none>        2888/TCP,3888/TCP   15d   app=zookeeper,storage=persistent
zoo         ClusterIP   None            <none>        2888/TCP,3888/TCP   15d   app=zookeeper,storage=persistent-regional
zookeeper   ClusterIP   10.103.22.130   <none>        2181/TCP            15d   app=zookeeper

3.3 topic

3.3.1 查看

# 创建 topic
./kafka-topics.sh --zookeeper 10.103.22.130:2181 --create --topic ttt1 --partitions 1 --replication-factor 1

# get topic 列表 
./kafka-topics.sh  --list --bootstrap-server 192.168.2.165:9092
./kafka-topics.sh --zookeeper 10.103.22.130:2181/kafka --list
输出如下:
__consumer_offsets
topic1
kafka-image-topic2

# get topic 详情
./kafka-topics.sh --zookeeper 10.103.22.130:2181 --describe --topic topic-a

3.3.2 修改

# 修改分区级别参数(如增加partition)
./kafka-topics.sh --zookeeper 10.103.22.130:2181 --alter --topic ttt1 --partitions 2
输出如下:
WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected
Adding partitions succeeded

# 删除topic(前提是把`kafka配置文件server.properties`中的`delete.topic.enable`设置为true)
./kafka-topics.sh --zookeeper 192.168.2.165:2181 --delete --topic <topic_name>
# 设置消息大小上限
./kafka-configs.sh --zookeeper 10.103.22.130:2181 --entity-type topics --entity-name ttt1 --alter --add-config max.message.bytes=10485760
输出如下
Completed Updating config for entity: topic 'ttt1'.

3.4 producer

# 查看生产
./kafka-console-producer.sh --broker-list 192.168.2.158:9092 --topic topic-a

# 生产消息
./kafka-console-producer.sh --topic topic-a --bootstrap-server broker:9092
> {"Ts":1677507305663,"Data":"99990000","Info":1080}}

参考

3.5 consumer

# 输入
./kafka-console-consumer.sh --bootstrap-server 192.168.2.111:9092  --from-beginning --topic topic-a --property print.timestamp=true

#  --property print.key=true 会打印消息的 key
或 ./kafka-console-consumer.sh --zookeeper 127.0.0.1:2181/kafka --topic topic-a --property print.key=true

3.6 其他

3.6.1 查 offset

如果希望根据时间,找offset,可以有如下方法:

  • 找到指定时间的 *.index 和 *.log 文件,文件名即为 offset。
-rw-r--r--  1 root root       43 Nov 28  2021 partition.metadata
-rw-r--r--  1 root root       10 Nov  8 12:30 00000000000000380329.snapshot
-rw-r--r--  1 root root 10485756 Nov  8 12:31 00000000000000380329.timeindex
-rw-r--r--  1 root root 10485760 Nov  8 12:31 00000000000000380329.index
drwxr-xr-x  2 root root     4096 Nov  8 12:31 ./
-rw-r--r--  1 root root     3622 Nov  8 12:33 00000000000000380329.log
drwxr-xr-x 77 root root     4096 Nov  8 12:34 ../

3.6.2 查积压

# 输入
cd ~/kafka/kafka/bin
watch -n 1 ./kafka-consumer-groups.sh --bootstrap-server 192.168.2.111:9092 --describe --group topic-a

# 输出:其中lag有值表示积压。
Note: This will only show information about consumers that use the Java consumer API (non-ZooKeeper-based consumers).
TOPIC     PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG   CONSUMER-ID                 HOST           CLIENT-ID
topic-a   0          159955          159955          0     UBUNTU.local-fe18cab4026a    

3.6.3 磁盘空间:数据保留的 retention 配置

broker 级别:即某 broker 的 每 log.retention.check.interval.ms 时间检查一次,若 log.retention.bytes 或 log.retention.ms/log.retention.miutes/log.retention.hours 二者任一项满足,则删除数据

topic 级别也可单独设置:即某 topic 的每 retention.check.interval.ms 时间检查一次,若 retention.bytes 或 retention.ms/retention.miutes/retention.hours 二者任一项满足,则删除数据

四、go sarama库

Shopify/sarama库

4.1 consumer

可以用 NewConsumerGroup() 创建,也可以先 NewClient() 再 NewConsumerGroupFromClient() 创建。

我们需要实现 Setup()、ConsumeClaim()、CleanUp() 三个回调函数,sarama 库会调度上述函数。

如果需要重置 Offset,可以在 Setup() 内通过 ResetOffset() 实现。

完整代码如下:

package kafka

import (
	"context"
	"github.com/Shopify/sarama"
	log "github.com/siruspen/logrus"
	"lonk/configs"
	"strconv"
	"time"
)

func StartConsumerGroup(ctx context.Context, conf *configs.KafkaInputConfig, msgHandler KafkaMsgHandler) {
	cli, err := newConsumerGroup(conf) // 新建一个 client 实例
	if err != nil {
		log.Fatalf("[newConsumerGroup] conf: %v, err: %v", conf, err)
	}
	k := kafkaConsumerGroup{
		msgHandler:                msgHandler,
		ready:                     make(chan bool, 0), // 标识 consumer 是否 ready
		partitionInitialOffsetMap: conf.PartitionInitialOffsetMap,
	}
	go func() {
		defer cli.Close()
		for {
			// Consume().newSession().newConsumerGroupSession() 先调用 Setup(); 再开多个协程(每个协程都用for循环持续调用consume().ConsumeClaim()来处理消息); Consume() 内部的 <-sess.ctx.Done() 会阻塞
			if err := cli.Consume(ctx, []string{conf.Topic}, &k); err != nil {
				log.Errorln("Error from consumer", err)
			}
			if ctx.Err() != nil { // 若 ctx.cancel() 而会引发 cli.Consume() 内部对 ctx.Done() 的监听,从而结束 cli.Consume() 的阻塞, 并
				return
			}
			k.ready = make(chan bool, 0) // 当 rebalanced 时 cli.Consume() 会退出且 ctx.Err() == nil, 则重置此 channel, 继续在下一轮 for 循环调用 Consume()
		}
	}()
	<-k.ready // 直到 close(consumer.ready) 解除阻塞
	log.Infoln("Sarama consumer up and running!...")
}

func newConsumerGroup(conf *configs.KafkaInputConfig) (sarama.ConsumerGroup, error) {
	sConf := sarama.NewConfig()
	sConf.Version = sarama.V2_8_0_0
	sConf.Consumer.Offsets.Initial = sarama.OffsetOldest
	sConf.Consumer.Offsets.Retention = 7 * 24 * time.Hour
	cli, err := sarama.NewClient(conf.Brokers, sConf)
	if err != nil {
		log.Fatalf("[NewClient] conf: %v, err: %v", sConf, err)
	}
	consumerGroup, err := sarama.NewConsumerGroupFromClient(conf.Group, cli)
	if err != nil {
		log.Fatalf("[NewConsumerGroupFromClient] conf: %v, err: %v", sConf, err)
	}
	return consumerGroup, nil
}

// Consumer represents a Sarama consumer group consumer
type kafkaConsumerGroup struct {
	msgHandler                func(message *sarama.ConsumerMessage)
	ready                     chan bool
	partitionInitialOffsetMap map[string]int64
}

// Setup 回调函数 is run at the beginning of a new session, before ConsumeClaim
func (k *kafkaConsumerGroup) Setup(session sarama.ConsumerGroupSession) error {
	for topic, partitions := range session.Claims() {
		for _, partition := range partitions {
			initialOffset, ok := k.partitionInitialOffsetMap[strconv.Itoa(int(partition))]
			if !ok {
				log.Fatalf("invalid topic:%v, partition: %v", topic, partition)
			}
			log.Infof("Sarama Consumer is resetting offset to %v:%v:%v", topic, partition, initialOffset)
			session.ResetOffset(topic, partition, initialOffset, "")
		}
	}
	close(k.ready) // 启动后此处会标记 ready, 使 StartKafkaConsumer() 不再阻塞
	return nil
}

// Cleanup 回调函数 is run at the end of a session, once all ConsumeClaim goroutines have exited
func (k *kafkaConsumerGroup) Cleanup(sarama.ConsumerGroupSession) error {
	log.Infoln("Sarama Consumer is cleaning up!...")
	return nil
}

// ConsumeClaim 回调函数 must start a consumer loop of ConsumerGroupClaim's Messages().
func (k *kafkaConsumerGroup) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
	// NOTE: Do not move the code below to a goroutine. The `ConsumeClaim` itself is called within a goroutine, see: https://github.com/Shopify/sarama/blob/master/consumer_group.go#L27-L29
	for {
		select {
		case message := <-claim.Messages():
			k.msgHandler(message)
			session.MarkMessage(message, "")
		// Should return when `session.Context()` is done. If not, will raise `ErrRebalanceInProgress` or `read tcp <ip>:<port>: i/o timeout` when kafka rebalance. see: https://github.com/Shopify/sarama/issues/1192
		case <-session.Context().Done():
			return nil
		}
	}
}

4.2 producer

参考:官方 consumerGroup 的 example
参考:sarama consumer group 的使用
参考:sarama partition consumer 根据 time 指定 offset文章来源地址https://www.toymoban.com/news/detail-663940.html

到了这里,关于【后端-Kafka】1、Kafka 超详细介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 分布式消息服务kafka

    什么是消息中间件? 消息中间件是分布式系统中重要的组件,本质就是一个具有接收消息、存储消息、分发消息的队列,应用程序通过读写队列消息来通信。 例如:在淘宝购物时,订单系统处理完订单后,把订单消息发送到消息中间件中,由消息中间件将订单消息分发到下

    2024年02月01日
    浏览(48)
  • 【分布式应用】kafka集群、Filebeat+Kafka+ELK搭建

    主要原因是由于在高并发环境下,同步请求来不及处理,请求往往会发生阻塞。比如大量的请求并发访问数据库,导致行锁表锁,最后请求线程会堆积过多,从而触发 too many connection 错误,引发雪崩效应。 我们使用消息队列,通过异步处理请求,从而缓解系统的压力。消息队

    2024年02月16日
    浏览(53)
  • 【分布式技术】消息队列Kafka

    目录 一、Kafka概述 二、消息队列Kafka的好处 三、消息队列Kafka的两种模式 四、Kafka 1、Kafka 定义 2、Kafka 简介 3、Kafka 的特性 五、Kafka的系统架构 六、实操部署Kafka集群  步骤一:在每一个zookeeper节点上完成kafka部署 ​编辑 步骤二:传给其他节点 步骤三:启动3个节点 kafka管理

    2024年01月23日
    浏览(55)
  • 【flink番外篇】4、flink的sink(内置、mysql、kafka、redis、clickhouse、分布式缓存、广播变量)介绍及示例(1) - File、Socket、console

    一、Flink 专栏 Flink 专栏系统介绍某一知识点,并辅以具体的示例进行说明。 1、Flink 部署系列 本部分介绍Flink的部署、配置相关基础内容。 2、Flink基础系列 本部分介绍Flink 的基础部分,比如术语、架构、编程模型、编程指南、基本的datastream api用法、四大基石等内容。 3、

    2024年02月01日
    浏览(46)
  • 分布式 - 消息队列Kafka:Kafka 消费者的消费位移

    01. Kafka 分区位移 对于Kafka中的分区而言,它的每条消息都有唯一的offset,用来表示消息在分区中对应的位置。偏移量从0开始,每个新消息的偏移量比前一个消息的偏移量大1。 每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一

    2024年02月12日
    浏览(50)
  • 分布式 - 消息队列Kafka:Kafka消费者的分区分配策略

    Kafka 消费者负载均衡策略? Kafka 消费者分区分配策略? 1. 环境准备 创建主题 test 有5个分区,准备 3 个消费者并进行消费,观察消费分配情况。然后再停止其中一个消费者,再次观察消费分配情况。 ① 创建主题 test,该主题有5个分区,2个副本: ② 创建3个消费者CustomConsu

    2024年02月13日
    浏览(47)
  • 分布式 - 消息队列Kafka:Kafka生产者架构和配置参数

    生产者发送消息流程参考图1: 先从创建一个ProducerRecord对象开始,其中需要包含目标主题和要发送的内容。另外,还可以指定键、分区、时间戳或标头。在发送ProducerRecord对象时,生产者需要先把键和值对象序列化成字节数组,这样才能在网络上传输。 接下来,如果没有显式

    2024年02月13日
    浏览(50)
  • 分布式 - 消息队列Kafka:Kafka生产者发送消息的方式

    不管是把Kafka作为消息队列、消息总线还是数据存储平台,总是需要一个可以往Kafka写入数据的生产者、一个可以从Kafka读取数据的消费者,或者一个兼具两种角色的应用程序。 Kafka 生产者是指使用 Apache Kafka 消息系统的应用程序,它们负责将消息发送到 Kafka 集群中的一个或多

    2024年02月13日
    浏览(44)
  • 分布式 - 消息队列Kafka:Kafka消费者和消费者组

    1. Kafka 消费者是什么? 消费者负责订阅Kafka中的主题,并且从订阅的主题上拉取消息。与其他一些消息中间件不同的是:在Kafka的消费理念中还有一层消费组的概念,每个消费者都有一个对应的消费组。当消息发布到主题后,只会被投递给订阅它的每个消费组中的一个消费者

    2024年02月13日
    浏览(45)
  • 分布式 - 消息队列Kafka:Kafka 消费者消费位移的提交方式

    最简单的提交方式是让消费者自动提交偏移量,自动提交 offset 的相关参数: enable.auto.commit:是否开启自动提交 offset 功能,默认为 true; auto.commit.interval.ms:自动提交 offset 的时间间隔,默认为5秒; 如果 enable.auto.commit 被设置为true,那么每过5秒,消费者就会自动提交 poll() 返

    2024年02月12日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包