Spark Streaming数据限流简述
Spark Streaming对实时数据流进行分析处理,源源不断的从数据源接收数据切割成一个个时间间隔进行处理;
流处理与批处理有明显区别,批处理中的数据有明显的边界、数据规模已知;而流处理数据流并没有边界,也未知数据规模;
由于流处理的数据流特征,使之数据流具有不可预测性,而且数据处理的速率还与硬件、网络等资源有关,在这种情况下如不对源源不断进来的数据流速率进行限制,那当Spark节点故障、网络故障或数据处理吞吐量下来时还有数据不断流进来,那将有可能将出现OOM进而导致Spark Streaming程序崩溃;
在Spark Streaming中不同的数据源采用不同的限速策略,但无论是Socket数据源的限流策略还是Kafka数据源的限流策略其速率(rate)的计算都是使用PIDController算法进行计算而得来;
下面从源码的角度分别介绍Socket数据源与Kafka数据源的限流处理。
速率限制的计算与更新
Spark Streaming的流处理其实是基于微批处理(MicroBatch)的,也就是说将数据流按某比较小的时间间隔将数据切割成为一段段微批数据进行处理;

StreamingContext调用Start()启动的时候会将速率控制器(rateController)添加到StreamingListener监听器中;
当每批次处理完成时将触发监听器(RateController),使用该批处理的处理结束时间、处理延迟时间、调度延迟时间、记录行数调用PIDRateEstimator传入PID算法中(PID Controller)计算出该批次的速率(rate)并更新速率限制(rateLimit)与发布该限制速率;
override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted) {
val elements = batchCompleted.batchInfo.streamIdToInputInfo
for {
processingEnd <- batchCompleted.batchInfo.processingEndTime
workDelay <- batchCompleted.batchInfo.processingDelay
waitDelay <- batchCompleted.batchInfo.schedulingDelay
elems <- elements.get(streamUID).map(_.numRecords)
} computeAndPublish(processingEnd, elems, workDelay, waitDelay)
}
private def computeAndPublish(time: Long, elems: Long, workDelay: Long, waitDelay: Long): Unit =
Future[Unit] {
val newRate = rateEstimator.compute(time, elems, workDelay, waitDelay)
newRate.foreach { s =>
rateLimit.set(s.toLong)
publish(getLatestRate())
}
}
Socket数据源限流
批次的限制速率上面已经算出,这里说的是接收Socket过来的数据时的数据限流;
SocketInputStream类receive方法接收到数据后将数据存入 BlockGenerator的Buffer中,在写入Buffer前调用限流器 (RateLimiter)对写入数据进行限流;
RateLimiter限流器使用了Google开源的 Guava中内置的RateLimiter限流器,该类只是对Guava限流器的简单封装;
在Spark Streaming中可通过使用两个参数配置初始速率与最大速率spark.streaming.receiver.maxRate、spark.streaming.backpressure.initialRate;亦可配置PIDController算法相关的四个参数值;
RateLimiter限流器是基于令牌桶的算法基本原理比较简单,以一个恒定的速率生成令牌放入令牌桶中,桶满则停止,处理请求时需要从令牌桶中取出令牌,当桶中无令牌可取时阻塞等待,此算法用于确保系统不被洪峰击垮。
private lazy val rateLimiter = GuavaRateLimiter.create(getInitialRateLimit().toDouble)
/**
* Push a single data item into the buffer.
*/
def addData(data: Any): Unit = {
if (state == Active) {
//调用限流器等待
waitToPush()
synchronized {
if (state == Active) {
currentBuffer += data
} else {
throw new SparkException(
"Cannot add data as BlockGenerator has not been started or has been stopped")
}
}
} else {
throw new SparkException(
"Cannot add data as BlockGenerator has not been started or has been stopped")
}
}
def waitToPush() {
//限流器申请令牌
rateLimiter.acquire()
}
Guava库中RateLimiter限流器基本使用:
//创建限流器,每秒产生令牌数1
RateLimiter rateLimiter=RateLimiter.create(1);
for (int i = 0; i < 10; i++) {
//获得一个令牌,未申请到令牌则阻塞等待
double waitTime = rateLimiter.acquire();
System.out.println(String.format("id:%d time:%d waitTime:%f",i,System.currentTimeMillis(),waitTime));
}
Kafka数据源限流的实现
在Spark Streaming Kafka包拉取Kafka数据会进行如下动作:
1、取Kafka中最新偏移量、分区
2、通过rateController限制每个分区可拉取的最大消息数
3、在DirectKafkaInputDStream中创建KafkaRDD,在其中调用相关对象拉取数据
通过如上步骤也可用看出,只要限制了Kafka某个分区的偏移量(offset)范围也就可限制从Kafka拉取的消息数量,从而达到限流的目的,Spark streaming kafka也是通过此实现的;
计算每个分区速率限制,有如下步骤:
1、通过seekToEnd获取最新可用偏移量与当前偏移量对比获得当前所有分区延迟偏移量
单个分区偏移量延迟=最新偏移量记录-当前偏移量记录
2、获取配置项中每个分区最大速率
(spark.streaming.kafka.maxRatePerPartition),背压率计算,计算每个分区背压率计算公式为:
单个分区背压率=单个分区偏移量延迟/所有分区总延迟*速率限制
速率限制(rateLimit):为通过PIDController动态计算得来
如有配置每个分区最大速率则取配置项最大速率与背压率两者中的最小值,未配置则取背压率作为每个分区速率限制;
3、将批次间隔(batchDuration)*每个分区速率限制=每个分区最大消息数
4、取当前分区偏移量+分区最大消息数 与 最新偏移量两者当中最小的,由此来控制拉取消息速率;
如当前偏移量+分区最大消息数 大于 最新偏移量则取 最新偏移量否则取 当前偏移量+分区最大消息数作为拉取Kafka数据的Offset范围;
// 限制每个分区最大消息数
protected def clamp(
offsets: Map[TopicPartition, Long]): Map[TopicPartition, Long] = {
maxMessagesPerPartition(offsets).map { mmp =>
mmp.map { case (tp, messages) =>
val uo = offsets(tp)
tp -> Math.min(currentOffsets(tp) + messages, uo)
}
}.getOrElse(offsets)
}
不管是Kafka数据源还是Socket数据源Spark Streaming中都使用了PIDController算法用于计算其速率限制值,两者的差别也只是因为两种数据源的获取方式数据特征而决定的。Socket数据源使用了Guava RateLimiter、而Kafka数据源自己实现了基于Offsets的限流;
以上说介绍的框架版本为:Spark Streaming 版本为2.3.2与spark-streaming-kafka-0-10_2.11;
参考资料:
http://kafka.apache.org
http://spark.apache.org
文章首发地址:Solinx
https://mp.weixin.qq.com/s/yHStZgTAGBPoOMpj4e27Jg
Spark Streaming数据限流简述的更多相关文章
- Spark Streaming:大规模流式数据处理的新贵(转)
原文链接:Spark Streaming:大规模流式数据处理的新贵 摘要:Spark Streaming是大规模流式数据处理的新贵,将流式计算分解成一系列短小的批处理作业.本文阐释了Spark Str ...
- Spark Streaming:大规模流式数据处理的新贵
转自:http://www.csdn.net/article/2014-01-28/2818282-Spark-Streaming-big-data 提到Spark Streaming,我们不得不说一 ...
- Spark Streaming 数据接收过程
SparkStreaming 源码分析 一节中从源码角度,描述了Streaming执行时代码的调用过程.下边就接收转化阶段过程再简单分析一下,为分析backpressure作准备. SparkStre ...
- Spark Streaming数据清理内幕彻底解密
本讲从二个方面阐述: 数据清理原因和现象 数据清理代码解析 Spark Core从技术研究的角度讲 对Spark Streaming研究的彻底,没有你搞不定的Spark应用程序. Spark Stre ...
- Spark Streaming源码解读之流数据不断接收全生命周期彻底研究和思考
本期内容 : 数据接收架构设计模式 数据接收源码彻底研究 一.Spark Streaming数据接收设计模式 Spark Streaming接收数据也相似MVC架构: 1. Mode相当于Rece ...
- Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十二)Spark Streaming接收流数据及使用窗口函数
官网文档:<http://spark.apache.org/docs/latest/streaming-programming-guide.html#a-quick-example> Sp ...
- Spark Streaming源码解读之流数据不断接收和全生命周期彻底研究和思考
本节的主要内容: 一.数据接受架构和设计模式 二.接受数据的源码解读 Spark Streaming不断持续的接收数据,具有Receiver的Spark 应用程序的考虑. Receiver和Drive ...
- 大数据技术之_19_Spark学习_04_Spark Streaming 应用解析 + Spark Streaming 概述、运行、解析 + DStream 的输入、转换、输出 + 优化
第1章 Spark Streaming 概述1.1 什么是 Spark Streaming1.2 为什么要学习 Spark Streaming1.3 Spark 与 Storm 的对比第2章 运行 S ...
- Dream_Spark-----Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码
Spark 定制版:005~贯通Spark Streaming流计算框架的运行源码 本讲内容: a. 在线动态计算分类最热门商品案例回顾与演示 b. 基于案例贯通Spark Streaming的运 ...
随机推荐
- Linux压缩归档管理
什么是压缩? 压缩就是用时间换取空间,CPU的时间换取磁盘的空间,下载传输的过程中可以节约带宽. zip/unzip zip支持多平台,支持归档压缩.文件经它压缩后会另外产生具有".zip& ...
- Starting MySQL... ERROR! The server quit without updating PID file (/home/mysql-5.6.43/data/localhost.localdomain.pid).
启动MySQL出现如下错误 May :: localhost mysqld: Starting MySQL... ERROR! The server quit without updating PID ...
- 自动化测试中,cookie的调用方法。
以cookie为例 方法一: 将返回的cookie写到setUp()中,每次执行用例之前就会调用一次. 如: class AA(unittest.TestCase): def setUp(self): ...
- 手动使用I2C协议写入24C02C
刚尝试用AT89C52单片机使用IIC总线协议读写AT24C02C,我忽然想能否用手动调整开关的方式写入AT24C02C?于是,便尝试了一下,结果果然成功了. 关于IIC总线,这篇文章写的很详细:ht ...
- jQuery--dataTable 前端分页与后端分页 及遇到的问题
(1)区别 前端分页:一次性把所有数据全都放在前端,由前端进行处理:适合请求的数据量不大的情况 后端分页:服务器模式,所有的分页,搜索,排序等操作在服务器端完成,然后前端去请求数据:适合量大的情况 ( ...
- Netty——知识点总结
引言 Netty blablabla…… Netty 知识点
- dict的使用
Python字典是可变类型数据,可以存储任意对象,如字符串,数字,元组,列表等. 字典的创键 字典有键key和值value组成,使用键值对链接:,字典也称为关联数组或哈希表. dict_person ...
- 外部SRAM的种类
外部SRAM注意事项 为使外部SRAM器件达到出最佳性能,建议遵循以下原则: 使用与连接的主系统控制器的接口数据带宽相同的SRAM. 如果管脚使用或板上空间的限制高于系统性能要求,可以使用较连接的控制 ...
- css基本概念与css核心语法介绍
css基本概念 css是什么?不需要了解太多文字类介绍,记住css是层叠样式表,HTML是页面结构,css负责页面样式,javascrt负责静态页面的交互.CSS 能够对网页中元素位置的排版进行像素级 ...
- 经济学人精读笔记9:打出租out了,“飞的”时代要来了!
经济学人精读笔记9:打出租out了,"飞的"时代要来了! 标签(空格分隔): 经济学人 Part 1 Flying taxis are taking off to whisk pe ...