转载自: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. Codeforces Round #479 (Div. 3) F. Consecutive Subsequence (简单dp)

    题目:https://codeforces.com/problemset/problem/977/F 题意:一个序列,求最长单调递增子序列,但是有一个要求是中间差值都是1 思路:dp,O(n)复杂度, ...

  2. k-means缺陷

    k均值算法非常简单且使用广泛,但是存在的缺陷有: 1. K值需要预先给定: 属于预先知识,很多情况下K值的估计非常困难. 2. K-Means算法对初始选取的聚类中心点是敏感的: 不同的随机种子点得到 ...

  3. 微信小程序(mpvue)—解决视频播放bug的一种方式

    // 第一页 <div @click="play(video.src, video.width, video.height)"></div> methods ...

  4. 北大poj- 1007

    DNA排序 逆序数可以用来描述一个序列混乱程度的量.例如,“DAABEC”的逆序数为5,其中D大于他右边的4个数,E大于他右边的1个数,4+1=5:又如,“ZWQM”的逆序数为3+2+1+0=6. 现 ...

  5. SHELL输出带颜色字体

    输出特效格式控制:\033[0m  关闭所有属性  \033[1m   设置高亮度  \03[4m   下划线  \033[5m   闪烁  \033[7m   反显  \033[8m   消隐  \ ...

  6. 运维seq语法

    seq-print a sequence of numbers 用于产生从某个数到另外一个数之间的所有整数 语法:seq 开始列  指定步长  结束列 参数: -f :指定输出格式,允许使用print ...

  7. PaperBye-一个可以自动改重的免费论文查重网站

    推荐一个自动降重的免费论文查重软件,可自动降低论文重复率,一边修改,一边查重,免费查重网址:https://www.paperbye.com

  8. MS SQL 全局临时表的删除

    本来已经搜索到怎么删除了 如下: IF OBJECT_ID( 'tempdb..##TEMP_COPTD') IS NOT NULL Begin DROP TABLE ##TEMP_COPTD End ...

  9. 安装MySql 8.x版本客户端连接失败解决方案

    ---恢复内容开始--- 安装完8.0.11MySql数据库及客户端工具Navicat Premium 12(具体安装方法不详细赘述,自行百度,推荐网址:https://blog.csdn.net/c ...

  10. Ubuntu 将其他盘挂载到/home的子目录下

    Ubuntu 14.04 将其他盘挂载到/home的子目录下当安装完Ubuntu系统,由于当时没有注意,分配的分区空间太小.经过一段时间安装了各式各样的软件后,常常会遇到/home目录下空间不够的情况 ...