Spark Streaming官方文档翻译基本概念之输出操作

Spark Streaming官方文档翻译Spark Streaming总览
Spark Streaming官方文档翻译基本概念之初始化与Dstream
Spark Streaming官方文档翻译基本概念之输入数据流和接收器
Spark Streaming官方文档翻译基本概念之转换操作
Spark Streaming官方文档翻译基本概念之输出操作
Spark Streaming官方文档翻译基本概念之sql与Mllib
Spark Streaming官方文档基本概念之缓存与检查点
Spark Streaming官方文档翻译基本概念之累加器、广播变量和检查点
Spark Streaming官方文档翻译Spark Streaming应用之部署,升级,监控
Spark Streaming官方文档翻译Spark Streaming性能调优
Spark Streaming官方文档翻译Spark Streaming容错
Spark Streaming官方文档翻译Spark Streaming +Kafka 集成指南
Spark Streaming官方文档翻译Spark Streaming自定义接收器

基本概念

DStreams上的输出操作(Output Operations on DStreams)

输出操作允许将DStream的数据推送到外部系统,如数据库或文件系统。由于输出操作实际上允许外部系统使用转换后的数据,因此它们会触发所有DStream转换的实际执行(类似于RDDs的actions)。目前定义了以下输出操作:

输出操作意义
print()在运行流应用程序的驱动节点上打印DStream中每批数据的前十个元素。这对于开发和调试非常有用。Python API这在Python API中称为pprint()。
saveAsTextFiles(prefix, [suffix])将DStream的内容保存为文本文件。每个批处理间隔的文件名是根据前缀和后缀“prefix-TIME_IN_MS[.suffix]”生成的。
saveAsObjectFiles(prefix, [suffix])将这个DStream的内容保存为序列化的Java对象的序列文件。每个批处理间隔的文件名是根据前缀和后缀“prefix-TIME_IN_MS[.suffix]”生成的。Python API 这在Python API中不可用。
saveAsHadoopFiles(prefix, [suffix])将DStream的内容保存为Hadoop文件。每个批处理间隔的文件名是根据前缀和后缀“prefix-TIME_IN_MS[.suffix]”生成的。
foreachRDD(func)将函数func应用于从流生成的每个RDD的最通用的输出操作符。该函数应该将每个RDD中的数据推送到外部系统,例如将RDD保存到文件中,或者通过网络将其写入数据库。请注意,func函数是在运行流应用程序的驱动程序进程中执行的,并且通常会有RDD动作(action),这将强制流RDDs的计算。

使用foreachRDD的设计模式(Design Patterns for using foreachRDD)

输出操作允许将DStream的数据推送到外部系统,如数据库或文件系统。然而,理解如何正确和有效地使用这个原语是很重要的。要避免的一些常见错误如下。

通常,将数据写入外部系统需要创建一个连接对象(例如,到远程服务器的TCP连接)并使用它将数据发送到远程系统。为此,开发人员可能会无意中尝试在Spark驱动程序中创建连接对象,然后尝试在Spark worker中使用它来保存RDDs中的记录。例如(在Scala中)。

dstream.foreachRDD { rdd =>
  val connection = createNewConnection()  // executed at the driver
  rdd.foreach { record =>
    connection.send(record) // executed at the worker
  }
}

这是不正确的,因为这需要将连接对象序列化并从驱动程序发送到工作程序。这样的连接对象很少能跨机器转移。此错误可能表现为序列化错误(连接对象不可序列化)、初始化错误(连接对象需要在工作人员处初始化)等。正确的解决方案是在worker上创建连接对象。

然而,这可能会导致另一个常见错误——为每个记录创建一个新连接。例如,

dstream.foreachRDD { rdd =>
  rdd.foreach { record =>
    val connection = createNewConnection()
    connection.send(record)
    connection.close()
  }
}

通常,创建连接对象需要时间和资源开销。因此,为每个记录创建和销毁一个连接对象可能导致不必要的高开销,并可能显著降低系统的总体吞吐量。更好的解决方案是使用rdd.foreachPartition——创建一个连接对象,并使用该连接发送RDD分区中的所有记录。

dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    val connection = createNewConnection()
    partitionOfRecords.foreach(record => connection.send(record))
    connection.close()
  }
}

这会将创建连接的开销分摊到许多记录上。

最后,可以通过跨多个RDDs/batch重用连接对象来进一步优化。可以维护一个静态的连接对象池,在将多个批的RDDs推送到外部系统时可以重用这些对象,从而进一步减少开销。

dstream.foreachRDD { rdd =>
  rdd.foreachPartition { partitionOfRecords =>
    // ConnectionPool is a static, lazily initialized pool of connections
    val connection = ConnectionPool.getConnection()
    partitionOfRecords.foreach(record => connection.send(record))
    ConnectionPool.returnConnection(connection)  // return to the pool for future reuse
  }
}

请注意,池中的连接应该按需延迟创建,如果不使用一段时间就会超时。这实现了向外部系统发送数据的最高效。
其他注意事项:

  • DStreams的输出操作是延迟执行,就像RDD操作延迟执行一样。具体来说,DStream输出操作中的RDD操作强制处理接收到的数据。因此,如果您的应用程序没有任何输出操作,或者有像dstream.foreachRDD()这样的输出操作,但是其中没有任何RDD操作,那么什么也不会执行。系统将简单地接收数据并丢弃它。
  • 默认情况下,一次执行一个输出操作。它们是按照在应用程序中定义的顺序执行的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值