Kafka系列 —— 生产实践分享

Kafka系列文章:
Kafka系列 —— 入门及应用场景 & 部署 & 简单测试
Kafka系列 —— Kafka核心概念
Kafka系列 —— Kafka常用命令
Kafka系列 —— Kafka监控
Kafka系列 —— Kafka消费语义分析&分区策略解读

本篇文章中将结合生产的案例来简述Kafka的一些生产实践

在实际生产中通常会使用Flume + Kafka + SparkStreaming去打造通用的流式处理平台,在Kafka与SparkStreaming的对接过程中,有许多有意思的点值得思考

Kafka消息量的相关解读

引入kafka的目的

避免在业务高峰的时候,数据量大,随之而来的就是计算压力也会很大,因此会引入kafka
但是当作为一个小白去设计的时候,一开始肯定不会考虑的很周全:

  • Spark Streaming去消费kafka的时候,是一窝蜂的去消费,并没有定义一套规则:比如在每个批次指定消费多少记录
    这个指定消费多少记录,是可以通过Kafka的几组参数进行控制的
每个批次的最大消息量的参数及公式

先来看一个图:
在这里插入图片描述
2500 records = 1 * 2 * 1250

  • 1:topic的partition个数
  • 2:spark streaming代码中batch时间
  • 1250:spark.streaming.kafka.maxRatePerPartition=1250
    每个partition最大的速率,在这样的一个规定下向Kafka去拿数据,最大只能拿1250条/s

注意点:
并不是说一个批次中据量越大越好
从Spark Web UI上我们可以发现有个Scheduling Delay的概念
假设1个批次有10W条记录,我们的batch时间设置的为2s,除了读取数据之外,如果含有业务逻辑的操作:比如写数据到数据库中,那么这2s能处理完吗?相当于在这2s内需要拿数据、进行业务逻辑的处理、将处理结果写入数据库;如果处理不完,那么就意味着会出现消息堆积的情况,Scheduling Delay(延迟时间)会加大
因此,我们需要去做压测,从而去寻找符合公司实际业务场景的、最大的数据量
在这里插入图片描述
如果这个曲线图一直在上升,那么就说明数据堆积了

Spark Streaming生产参数解读

spark.streaming.backpressure.enabled

场景:假设目前还剩7500条消息没有进行消费

  • 设置参数spark.streaming.backpressure.enabled=true,默认为false
    7500条不是两个批次直接消费完,而是慢慢的进行消费
  • 设置参数spark.streaming.backpressure.enabled=false
    7500条是两个批次直接消费完

问题:上述这2种场景该怎么去取舍呢?

  • spark.streaming.backpressure.enabled=true,生产常用,kafka没有堆积的情况,慢慢去消费即可(因为数据是比较正常过来的)
  • spark.streaming.backpressure.enabled=false,Kafka中有消息堆积,需要去快速的消费完可能会出现这种场景,比如:Kafka中已经堆积了1亿条数据,如果还是设置为true去慢慢消费的话,会出现问题,因此需要将该参数设置为false
    这种场景比较特殊;当出现这种情况的时候,需要我们去思考为什么会产生数据的堆积?
    曾经遇到过这种情况,不过是由于人为造成的
spark.streaming.stopGracefullyOnShutdown
  • 默认为false
    默认为false情况下的一个场景:
    手工kill掉了Spark Streaming进程,当前进程就挂了;同时,也不会去做任何操作,比如写offset啊这种,都不会去做,那么重启之后怎么办?
    这里就引入了断批还原的概念,由于涉及到公司项目的相关生产场景,不方便在博文中进行文字表述,大家可以仔细思考这种场景下的解决方案
  • 如果设置为true,那么在接收到kill的命令之后,会等待下一个批次处理完,然后再kill,比较优雅;生产上建议设置为true

offset管理

官网:http://spark.apache.org/docs/2.2.0/streaming-kafka-0-10-integration.html#obtaining-offsets
断批还原的关键点在于对于offset的维护管理,因为我们的offset是一个批次一个批次去写的,offset的3种维护方式:

  • checkpoint
    维护在HDFS上的;是每隔一段时间将内容存放到HDFS上去的,会有许多问题:
    1.小文件,会对HDFS集群造成很大的压力
    2.会丢数据
    checkpoint配合kafka能够在特定环境下保证不丢不重,注意为什么要加上特定环境呢,这里有一些坑,checkpoint是对sparkstreaming运行过程中的元数据和每次rdd的数据状态保存到一个持久化系统中,当然这里面也包含了offset,一般是HDFS,S3,如果程序挂了,或者集群挂了,下次启动仍然能够从checkpoint中恢复,从而做到生产环境的7*24高可用
    但是checkpoint的最大的弊端在于,一旦你的流式程序代码或配置改变了,或者更新迭代新功能了,这个时候,你先停旧的sparkstreaming程序,然后新的程序打包编译后执行运行,会发现两种情况:
    (1)启动报错,反序列化异常
    (2)启动正常,但是运行的代码仍然是上一次的程序的代码。
    为什么会出现上面的两种情况,这是因为checkpoint第一次持久化的时候会把整个相关的jar给序列化成一个二进制文件,每次重启都会从里面恢复,但是当你新的程序打包之后序列化加载的仍然是旧的序列化文件,这就会导致报错或者依旧执行旧代码。有的同学可能会说,既然如此,直接把上次的checkpoint删除了,不就能启动了吗?确实是能启动,但是一旦你删除了旧的checkpoint,新启动的程序,只能从kafka的smallest或者largest的偏移量消费,默认是从最新的,如果是最新的,而不是上一次程序停止的那个偏移量,就会导致有数据丢失,如果是老的,那么就会导致数据重复。不管怎么样搞,都有问题

  • Kafka自身
    官网:http://spark.apache.org/docs/2.2.0/streaming-kafka-0-10-integration.html#kafka-itself
    enable.auto.commit=true,自动提交,默认是每隔5s去提交一次
    自身维护是:提交到kafka的当前topic的内嵌的topic的:_consumer中
    enable.auto.commit=false,官方给的默认值为false
    false情况下,我们需要在业务处理后,手工提交offset,即手工使用官方API异步提交offset(生产)
    官网给的代码:
    在这里插入图片描述

  • 其它存储
    早期版本的Kafka offset是维护在zk里的
    可选的存储有:ZK、HBase、Redis
    Github:https://github.com/wangliangbd/SparkStreaming_Store_KafkaTopicOffset_To_HBase

Kafka与SparkStreaming对接的2种方式

[待更新…]

Kafka调优参数详解

Kafka的调优,从三个方面入手去考虑:Producer、Broker、Consumer

Producer(Flume/Maxwell/…)

生产上使用的Kafka是0.10.x版本,因此对应查看相应官网:
http://kafka.apache.org/0100/documentation.html#producerconfigs

  • acks
    生产上设置为all,默认为1
    1个topic有3个partition,每个partition的副本数为3;其中,只有作为leader的那个副本才具备读写的功能,其余2个不作为leader的副本都不对外提供读和写的,仅仅只用于复制,如下图:
    在这里插入图片描述
    因此,当一条数据过来,我们复制三份,才能保证是最安全的
    all代表:三个副本都写完了,而且各自都返回了1个ack的确认值,这样producer才觉得消息是完完全全的发送过去了
    This is the strongest available guarantee 官网的描述,说白了就是数据不会丢

  • buffer.memory
    比如说flume向kafka发送数据,当发送的过快时,我们需要将这份数据先缓存起来再发(类似于MapReduce的shuffle过程,并不是直接写到磁盘里面,而是先写到内存的buffer缓冲池,达到阈值之后再刷到磁盘上去)

  • compression.type

  • retries
    生产上设置为100,默认为0
    如果发送消息失败的话,要重试多少次;如果不去重试,就很有可能引起消息的发送失败
    注意:
    需要设置参数max.in.flight.requests.per.connection = 1
    如果没有设置这个参数,将会改变发送的消息的顺序关系
    举例:
    现在有2条消息都发送到partition0,第1条发送失败了,第2条发送成功了;现在第1条发送失败的要重试了,原本的顺序关系是1、2;
    而现在由于第1条发送失败了,第2条成功,第1条需要进行重试,这个时候就会引起发送消息的顺序问题,因此必须设置这个参数
    在上文中我们提到过:单分区内是要保证有序的,而对于多分区是无法保证有序的
    面试的时候尽量不要自己主动提排序的问题,因为很可能会给自己挖坑,但是大致的也是需要懂的

  • batch.size
    对于数据可以是来一个批次,将该批次内的数据都给发送过去;这样做可以降低请求数

  • linger.ms
    在工作中用的不是很多,默认值为0
    比如flume往kafka发送消息,发送的过程中会去延迟一会、等待一会
    第1个批次和第2个批次的间隔时间给拉大一点,每个批次里的数据堆积的多点了再发送,这样做能够降低负载,但是会增加延迟
    Setting linger.ms=5, for example, would have the effect of reducing the number of requests sent but would add up to 5ms of latency to records sent in the absense of load.
    官网对于该参数的一个案例解释:如果设置5ms,可以降低请求,但是我们会添加5ms的延迟
    对于一般的交易系统来说,默认这个参数是不用的

  • max.request.size
    指的是请求最大的字节,该参数用于控制生产者发送的请求大小;它可以指能发送的单个消息的最大值,也可以指单个请求里所有消息总的大小

  • request.timeout.ms
    需要大于replica.lag.time.max.ms(Broker中的参数)
    指定了生产者在发送数据时等待服务器返回响应的时间

  • metadata.fetch.timeout.ms
    指定了生产者在获取元数据(比如目标分区的首领是谁)时等待服务器返回响应的时间;如果等待响应超时,那么生产者要么重试发送数据,要么返回一个错误 (抛出异常或执行回调)

  • max.block.ms
    该参数指定了在调用send()方法或使用parttitionFor()方能获取元数据时生产者的阻塞 时间;当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方屈就会阻塞在阻塞时间达到max.block.ms时,生产者会抛出超时异常

  • timeout.ms
    该参数是针对acks的,默认才30s,一旦遇到网络抖动肯定是不够的,因此需要调大
    指定了broker等待同步副本返回消息确认的时间,与asks的配置相匹配一一如果在指定时间内没有收到同步副本的确认,那么broker就会返回 一个错误

注意: 对于调优,带有timeout的参数肯定需要先进行全局搜索一下(不管是什么框架)

Broker(CDH)
Consumer(Spark Streaming)

涉及到的调优参数可以参考这个issue:https://issues.apache.org/jira/browse/SPARK-22968

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值