spark 开发中的那些事1-之编程模型

一, 简介

Spark是一个用来实现快速而通用的集群内存计算的平台。扩展了广泛使用的MapReduce计算模型,而且高效地支持更多的计算模式,包括交互式查询和流处理。
随着时间推移开始被大家逐渐熟知和普及,很长一段时间几乎就是大数据代言词。

二,前言

本文旨在帮助大家整理一些开发中常见的坑和误区,帮助大家进行整理和总结,降低刚接触的大数据开发朋友的开发难度.

对于算子和api大家在开发中参考官网文档即可(http://spark.apache.org/docs/latest/)

注意:以下所讲主要针对集群生产环境

三, spark程序架构

loading...

Spark开发,严格来说属于分布式编程, spark程序工作时通常最少2个进程, 有且只有两个角色driver,executor.

四, 常见流程(以yarn为例)

  • 1, 本地ide开发逻辑,并使用 local[*] 进行调试
  • 2, 部署集群使用spark-client进行测试(可能没有)
  • 3, 部署集群使用spark-cluster实际部署

我们一般开发一个spark程序都要走上面的3个步骤,但是有些非常奇怪的现象是:步骤1没问题但是实际到集群部署,到了2和3就出错.

常见错误和问题:

  • 读取的配置参数和预期不符合
  • submit提交后出现java.lang.NullPointerException
  • Not Serializable
  • 还有spark开发代码中为什么经常看到lazy来获取资源?

我想大部分朋友刚开始都被这些问题困扰过,如果你碰到了这些问题,我们不妨继续往下看.

五, 分布式编程

我们刚才提到了Spark程序的架构和角色,也指出spark开发其实是分布式编程.

分布式编程?

通常大家最早接触Spark开发,应该都是官网文档demo helloworld,以及文档开始的(我也是这么上车的-_-!).

通过一些非常炫酷的(算子)方法调用.恩,反正最后helloworld就这么跑出来了.于是上面提到的过程1就完成了.

第一印象: 这就是普通编程啊(这些方法算子scala就有,java8 Stream api也能搞出来),恩,这里我们已经进入陷阱了(那么上面提到部署坑你很可能即将碰到).

实际情况:

No!No!No! spark开发属于分布式编程, 我们书写每一行code/每个函数/每个Class,都应该去思考代码的实际位置,去规划你的代码结构.

举个栗子:

object Test {
	private val conn1 = crateteConn()
	private lazy val conn2 = crateteConn();
	def main() {
		val conn3 = crateteConn()   //这个code在driver角色进程上执行
		val logo = “hello-spark”
		rdd.foreachPartition(partition => {
		val conn4 = crateteConn()   //这个code在executor角色进程上执行
		partition.foreach(line => {
			println(logo)
			println(conn1)
			println(conn2)
			println(conn3)
			println(conn4)
		})
		})
	}
}

栗子中指出了conn3和conn4创建时实际运行位置. 1和2留给大家自己思考
另外这个例子中代码放在了 object(scala单例对象)中,如果放到class下面呢?

这就是为什么ide中没问题,submit就出错的大部分根源,抱歉: 这里只为让大家意识到这个和平时编程的区别,以及几个面向对象的概念,因篇幅有限没办法一一帮大家梳理.

但是spark进阶之路上一定少不了关于分布式编程的理解.在空闲情况不妨去交流和理解spark分布式编程,者对于在flink,mr,beam等其他分布式计算框架中都是通用的.

六, 执行机制

刚才我们聊了程序模型实质是分布式编程.接下来我们聊聊Spark提供的一些常见api机制.

  • Spark编程,属于标记(tranformtion)编程,由终结指令(action)触发实际任务允许.
    特点可以一个字总结,那就是.

如果你代码中有下面的句子,那么它一定是不执行的(注意:这些句子没有下文):

rdd.map(x=>println(x))
spark.sparkContext.textFile("/ideal/hadoop/spark/README.md")

而下面这个一定会执行的(你可以在控制台看到打印):

rdd.foreach(x=>println(x))

接下来我们看action算子

  • 另外注意action算子之间是串行执行的,不过通常这完全符合我们的行为预期
    如:
println(rdd.count()) //必须执行玩才执行下面的语句
rdd.foreach(x=>println(x))
  • 结合上面提到分布式编程来个比喻:

spark开发就像一家公司大了后,老板就只在纸上画出接下来使用什么资源(source)来干什么事情(tranformtion 标记).老板规划完成后,然后下指令(action)开干,然后等待结果.
工程开始后driver理解这个图纸,安排executor干活.完成后driver最后向老板汇报结果

七, 信息传递

既然是分布式模型,那么自然少不了信息沟通,这里分为信息收集到driver和信息下发到executor

1, 信息收集到driver

通常都是用spark自己提供的action算子

val cnt = rdd.count()
val data = rdd.collect()
count()计算,这个活是executor干的,但是最后的结果cnt,是被收集到driver进程中的

请谨慎使用action collect(),不明所以的情况下dirver直接oom掉

如果确实需要用collect怎么办?
推荐使用下面的api

rdd.toLocalIterator
dataframe.toLocalIterator

这里同样不推荐rdd.repartition(1)算子,容易造成executor 直接oom掉

2, 信息下发到executor

这种情况很常见几乎每个程序都会用到,具体分为启动初始化时参数传递和允许中传递:

  • 1, 利用闭包特性(高大上,驾驭有难度,各种跨进程rpc编程,分布式编程框架所使用,缺点:jvm专用,一般在启动初始化时传递)
  • 2, 利用–files 传递配置文件, executor 通过lazy读取
	private lazy val conn: Connection = {
		//1 LOAD 配置文件
		val user = "read --files的配置文件"
		DriverManager.getConnection("jdbc:",user,"passsword")
	}
特点: 亲民广泛,内容运维可以配置,就像普通程序一样带配置文件部署, executor可以轻易获取, scala和python,R都可以使用,属于启动初始化时传递信息
  • 3, 利用spark的广播功能,注意可序列化,属于允许中传递
  • 4, 利用spark-submit和yarn机制
	set=>: spark.executorEnv.[EnvironmentVariableName]=value
	jvm_read=>: val value = System.getEnv(EnvironmentVariableName)
特点: 如果参数少且简单非常推荐, 完美发挥lazy,且scala和python,R都可以使用,也属于启动初始化时传递信息

八, 总结

本文旨的告诉大家Spark开发本质是分布式编程,Spark是一个分布式编程框架(平台,语言),我们常见的很多问题以及调优等都需要大家去注意这一点.同时帮大家梳理了一些基本的知识点,当然了spark等分布式计算开发有非常多技术点,因篇幅问题在此无法一一列举,还请见谅.

九, 最后

如果大家有兴趣除了关注后续的文章,也随时欢迎和我们讨论:

您也可以关注下我的开源大数据流计算项目Sylph(https://www.oschina.net/p/sylph),里面用到flink,spark等非常多的特性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值