转载自:http://blog.csdn.net/erfucun/article/details/52312682

本博文主要内容包括:

  • 技术实现foreachRDD与foreachPartition解析
  • foreachRDD与foreachPartition实现实战

一:技术实现foreach解析:

1、首先我们看一下Output Operations on DStreams提供的API: 
 

SparkStreaming的DStream提供了一个dstream.foreachRDD方法,该方法是一个功能强大的原始的API,它允许将数据发送到外部系统。然而,重要的是要了解如何正确有效地使用这种原始方法。一些常见的错误,以避免如下: 
写数据到外部系统,需要建立一个数据连接对象(例如TCP连接到远程的服务器),使用它将数据发送到外部存储系统。为此开发者可能会在Driver中尝试创建一个连接,然后在worker中使用它来保存记录到外部数据。代码如下:


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

上面的代码是一个错误的演示,因为连接是在Driver中创建的,而写数据是在worker中完成的。此时连接就需要被序列化然后发送到worker中。但是我们知道,连接的信息是不能被序列化和反序列化的(不同的机器连接服务器需要使用不同的服务器端口,即便连接被序列化了也不能使用)

进而我们可以将连接移动到worker中实现,代码如下:


  1. dstream.foreachRDD { rdd =>
  2. rdd.foreach { record =>
  3. val connection = createNewConnection()
  4. connection.send(record)
  5. connection.close()
  6. }}

但是此时,每处理一条数据记录,就需要连接一次外部系统,对于性能来说是个严重的问题。这也不是一个完美的实现。

Spark基于RDD进行编程,RDD的数据不能改变,如果擅长foreachPartition底层的数据可能改变,做到的方式foreachPartition操作一个数据结构,RDD里面一条条数据,但是一条条的记录是可以改变的spark也可以运行在动态数据源上。(就像数组的数据不变,但是指向的索引可以改变) 
我们可以将代码做如下的改进:


  1. dstream.foreachRDD { rdd =>
  2. rdd.foreachPartition { partitionOfRecords =>
  3. val connection = createNewConnection()
  4. partitionOfRecords.foreach(record => connection.send(record))
  5. connection.close()
  6. }}

这样一个partition,只需连接一次外部存储。性能上有大幅度的提高。但是不同的partition之间不能复用连接。我们可以使用连接池的方式,使得partition之间可以共享连接。代码如下:


  1. stream.foreachRDD { rdd =>
  2. rdd.foreachPartition { partitionOfRecords =>
  3. // ConnectionPool is a static, lazily initialized pool of connections
  4. val connection = ConnectionPool.getConnection()
  5. partitionOfRecords.foreach(record => connection.send(record))
  6. ConnectionPool.returnConnection(connection)  // return to the pool for future reuse
  7. }}

二:foreachRDD与foreachPartition实现实战

1、需要注意的是: 
(1)、你最好使用forEachPartition函数来遍历RDD,并且在每台Work上面创建数据库的connection。 
(2)、如果你的数据库并发受限,可以通过控制数据的分区来减少并发。 
(3)、在插入MySQL的时候最好使用批量插入。 
(4),确保你写入的数据库过程能够处理失败,因为你插入数据库的过程可能会经过网络,这可能导致数据插入数据库失败。 
(5)、不建议将你的RDD数据写入到MySQL等关系型数据库中。

2、下面我们使用SparkStreaming实现将数据写到MySQL中:

(1)在pom.xml中加入如下依赖包


  1. <dependency>
  2. <groupId>mysql</groupId>
  3. <artifactId>mysql-connector-java</artifactId>
  4. <version>5.1.38</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>commons-dbcp</groupId>
  8. <artifactId>commons-dbcp</artifactId>
  9. <version>1.4</version>
  10. </dependency>

(2)在MySql中创建数据库和表,命令操作如下:


  1. mysql -uroot -p
  2. create database spark;
  3. use spark;
  4. show tables;
  5. create table streaming_itemcount(keyword varchar(30));

使用Java编写一个数据库连接池类


  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.util.LinkedList;
  4. /**
  5. * Created by zpf on 2016/8/26.
  6. */
  7. public class ConnectionPool {
  8. private static LinkedList<Connection> connectionQueue;
  9. static {
  10. try {
  11. Class.forName("com.mysql.jdbc.Driver");
  12. } catch (ClassNotFoundException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. public synchronized static Connection getConnection() {
  17. try {
  18. if (connectionQueue == null) {
  19. connectionQueue = new LinkedList<Connection>();
  20. for (int i = 0; i < 5; i++) {
  21. Connection conn = DriverManager.getConnection(
  22. "jdbc:mysql://Master:3306/sparkstreaming",
  23. "root",
  24. "12345");
  25. connectionQueue.push(conn);
  26. }
  27. }
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. return connectionQueue.poll();
  32. }
  33. public  static void returnConnection(Connection conn){
  34. connectionQueue.push(conn);
  35. }
  36. }

编写Spark代码:


  1. import org.apache.spark.SparkConf
  2. import org.apache.spark.streaming.{Seconds, StreamingContext}
  3. /**
  4. * Created by zpf on 2016/8/26.
  5. */
  6. object OnlineForeachRDD2DB {
  7. def main(args: Array[String]) {
  8. val conf = new SparkConf().setAppName("OnlineForeachRDD2DB").setMaster("local[2]")
  9. val ssc = new StreamingContext(conf, Seconds(5))
  10. val lines = ssc.socketTextStream("Master", 9999)
  11. val words = lines.flatMap(_.split(" "))
  12. val wordCounts = words.map(x => (x, 1)).reduceByKey(_ + _)
  13. wordCounts.foreachRDD { rdd =>
  14. rdd.foreachPartition { partitionOfRecords => {
  15. val connection = ConnectionPool.getConnection()
  16. partitionOfRecords.foreach(record => {
  17. val sql = "insert into streaming_itemcount(item,count) values('" + record._1 + "'," + record._2 + ")"
  18. val stmt = connection.createStatement
  19. stmt.executeUpdate(sql)
  20. })
  21. ConnectionPool.returnConnection(connection)
  22. }
  23. }
  24. }
  25. }
  26. }

打开netcat发送数据


  1. root@spark-master:~# nc -lk 9999
  2. spark hadoop kafka spark hadoop kafka spark hadoop kafka spark hadoop

打包运行spark代码


  1. /usr/local/spark/bin/spark-submit --driver-class-path /usr/local/spark/lib/mysql-connector-java-5.1.35-bin.jar /root/Documents/SparkApps/SparkStreamApps.jar

查看数据库中的结果:

博文内容源自DT大数据梦工厂Spark课程总结的笔记相关课程内容视频可以参考: 百度网盘链接:http://pan.baidu.com/s/1slvODe1(如果链接失效或需要后续的更多资源,请联系QQ460507491或者微信号:DT1219477246 获取上述资料)。

通过Spark Streaming的foreachRDD把处理后的数据写入外部存储系统中的更多相关文章

  1. spark streaming从指定offset处消费Kafka数据

    spark streaming从指定offset处消费Kafka数据 -- : 770人阅读 评论() 收藏 举报 分类: spark() 原文地址:http://blog.csdn.net/high ...

  2. Spark Streaming揭秘 Day10 从BlockGenerator看接收数据的生命周期

    Spark Streaming揭秘 Day10 从BlockGenerator看接收数据的生命周期 昨天主要介绍了SparkStreaming中对于Receiver的生命周期管理,下面让我们进入到Re ...

  3. Spark Streaming和Kafka整合是如何保证数据零丢失

    转载:https://www.iteblog.com/archives/1591.html 当我们正确地部署好Spark Streaming,我们就可以使用Spark Streaming提供的零数据丢 ...

  4. spark读取mongodb数据写入hive表中

    一 环境: spark-: hive-; scala-; hadoop--cdh-; jdk-1.8; mongodb-2.4.10; 二.数据情况: MongoDB数据格式{    "_i ...

  5. 数据清洗:按照进行数据清洗,并将清洗后的数据导入hive数据库中。

    虚拟机: hadoop:3.2.0 hive:3.1.2 win10: eclipse 两阶段数据清洗: (1)第一阶段:把需要的信息从原始日志中提取出来 ip:    199.30.25.88 ti ...

  6. 大数据技术之_19_Spark学习_04_Spark Streaming 应用解析 + Spark Streaming 概述、运行、解析 + DStream 的输入、转换、输出 + 优化

    第1章 Spark Streaming 概述1.1 什么是 Spark Streaming1.2 为什么要学习 Spark Streaming1.3 Spark 与 Storm 的对比第2章 运行 S ...

  7. Spark学习(4) Spark Streaming

    什么是Spark Streaming Spark Streaming类似于Apache Storm,用于流式数据的处理 Spark Streaming有高吞吐量和容错能力强等特点.Spark Stre ...

  8. Spark学习笔记——Spark Streaming

    许多应用需要即时处理收到的数据,例如用来实时追踪页面访问统计的应用.训练机器学习模型的应用, 还有自动检测异常的应用.Spark Streaming 是 Spark 为这些应用而设计的模型.它允许用户 ...

  9. Spark学习之Spark Streaming

    一.简介 许多应用需要即时处理收到的数据,例如用来实时追踪页面访问统计的应用.训练机器学习模型的应用,还有自动检测异常的应用.Spark Streaming 是 Spark 为这些应用而设计的模型.它 ...

随机推荐

  1. tensorFlow可以运行的代码

    折腾了很久,终于运行成功. 才云科技的书不错,就是需要微调一二. 心得:1,记得activate tensorflow,然后再python 2,Python的代码格式很重要,不要错误. 3,还不清楚如 ...

  2. 利用postman 实现Get和Post测试

    通过之前对金字塔结构的学习,大概了解到了金字塔模型想告诉我们的几个道理: 1.越底层,越稳定. 金字塔主要观点认为单元测试的稳定性高,需要多投入. 2.越底层,越高效. 程序的问题,最终还得落在具体的 ...

  3. 使用EFCore,手动创建SQLLite数据库

    有时候我们需要在代码中动态生成一个sqllite数据库文件,可以按照以下代码完成, static void Main(string[] args) { MyContext context = new ...

  4. vim安装

    vim的下载与安装 在线安装 sudo yum install vim 源码编译安装 1)   下载源码 git clone https://github.com/vim/vim.git 2)   编 ...

  5. npm -g -D -s的区别

    npm install module-name 全局安装 npm install module-name -D 即 npm install module-name -save-dev  保存到devD ...

  6. 运动控制之一_PID控制理论

    PID算法是早期发展起来的控制算法,该算法因其简单.鲁棒性强且可靠性高而被广泛地应用于过程控制和运动控制中. 常规的PID控制系统原理框图如下所示: PID控制系统原理图 误差信号Err(t)输入到控 ...

  7. L2-002 链表去重 (25 分)

    L2-002 链表去重 (25 分)   给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉.即对每个键值 K,只有第一个绝对值等于 K 的结点被保留.同时,所有被删除的结点须被保存在 ...

  8. session的部分理解

    定义 Session:在计算机中,尤其是在网络应用中,称为“会话控制”.Session 对象存储特定用户会话所需的属性及配置信息.这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session ...

  9. mysql 数据库

    show databases 显示库use databse 进入库(databse为库名)show tables 显示表create table a select * from b where 0=1 ...

  10. windows下python环境+selenium环境搭建

    一.安装Python 1. 下载python包,最新版本的上一个版本:https://www.python.org/downloads/windows/ 2. 安装 可选择默认安装或自定义安装,自定义 ...