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

一, 简介

Spark是一个用来实现快速而通用的集群内存计算的平台。扩展了广泛使用的MapReduce计算模型,而且高效地支持更多的计算模式,包括交互式查询和流处理。 Spark目前已经成为大数据计算的事实标准。 官网文档(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”
		val spark = ...
		val rdd = ...
		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 api开发中分布式弹性数据集大部分算子传入的闭包函数,都是在executor上执行。一个executor上多个并行task会同时执行这些闭包函数。 执行到action算子时会触发一个job,job和job之间默认是串行的。

  • 结合上面提到分布式编程来个比喻:

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的广播功能,注意可序列化,属于运行中传递(常用来做map-side-join优化)
  • 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等非常多的特性

转载于:https://my.oschina.net/idealhp/blog/2934028

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值