-
SparkContext是pyspark的编程入口,作业的提交,任务的分发,应用的注册都会在SparkContext中进行。一个SparkContext实例代表着和Spark的一个连接,只有建立了连接才可以把作业提交到集群中去。实例化了SparkContext之后才能创建RDD和Broadcast广播变量。
-
Sparkcontext获取,启动
pyspark --master spark://hadoop-maste:7077
之后,可以通过SparkSession获取Sparkcontext对象从打印的记录来看,SparkContext它连接的Spark集群的master地址是spark://hadoop-maste:7077
另外一种获取SparkContext的方法是,引入pyspark.SparkContext进行创建。新建parkContext.py文件,内容如下:from pyspark import SparkContext from pyspark import SparkConf conf = SparkConf() conf.set('master','local') sparkContext = SparkContext(conf=conf) rdd = sparkContext.parallelize(range(100)) print(rdd.collect()) sparkContext.stop()
运行之前先将spark目录下的conf配置目录中的log4j.properties配置文件中的日志级别改为如下:
这样后台打印的日志不至于太多印象查看!重启Spark集群
运行spark-submit sparkContext.py
上面代码中的Sparkconf对象是Spark里面用来配置参数的对象,接下来我们会详细讲解到。 -
accumulator是Sparkcontext上用来创建累加器的方法。创建的累加器可以在各个task中进行累加,并且只能够支持add操作。该方法
支持传入累加器的初始值。这里以Accumulator累加器做1到50的加法作为讲解的例子。
新建accumulator.py文件,内容如下:from pyspark import SparkContext,SparkConf import numpy as np conf = SparkConf() conf.set('master','spark://hadoop-maste:7077') context = SparkContext(conf=conf) acc = context.accumulator(0) print(type(acc),acc.value) rdd = context.parallelize(np.arange(101),5) def acc_add(a): acc.add(a) return a rdd2 = rdd.map(acc_add) print(rdd2.collect()) print(acc.value) context.stop()
使用
spark-submit accumulator.py
运行 -
addFile方法添加文件,使用SparkFiles.get方法获取文件
这个方法接收一个路径,该方法将会把本地路径下的文件上传到集群中,以供运算过程中各个node节点下载数据,路径可以是本地路径也可是是hdfs路径,或者一个http,https,或者tfp的uri。如果上传的是一个文件夹,则指定recursize参数为True.上传的文件使用SparkFiles.get(filename)的方式进行获取。如果使用本地路径,则要求所有节点本地相同的的路径中都有该文件,各个节点在各自的本地目录中寻找该文件。
新建addFile.py文件,内容如下:
from pyspark import SparkFiles import os import numpy as np from pyspark import SparkContext from pyspark import SparkConf tempdir = '/root/workspace/' path = os.path.join(tempdir,'num_data') with open(path,'w') as f: f.write('100') conf = SparkConf() conf.set('master','spark://hadoop-maste:7077') context = SparkContext(conf=conf) context.addFile(path) rdd = context.parallelize(np.arange(10)) def fun(iterable): with open(SparkFiles.get('num_data')) as f: value = int(f.readline()) return [x*value for x in iterable] print(rdd.mapPartitions(fun).collect()) context.stop()
运行
spark-submit addFile.py
这个例子是使用的本地的文件,接下来我们尝试一下hdfs路径下的文件。
新建hdfs_addFile.py文件,内容如下:from pyspark import SparkFiles import numpy as np from pyspark import SparkContext from pyspark import SparkConf conf = SparkConf() conf.set('master','spark://hadoop-maste:7077') context = SparkContext(conf=conf) path = 'hdfs://hadoop-maste:9000/datas/num_data' context.addFile(path) rdd = context.parallelize(np.arange(10)) def fun(iterable): with open(SparkFiles.get('num_data')) as f: value = int(f.readline()) return [x*value for x in iterable] print(rdd.mapPartitions(fun).collect()) context.stop()
#查看hdfs的根目录 hdfs dfs -ls / #在根目录下创建/datas文件夹 hdfs dfs -mkdir /datas # 将本地目录中的num_data文件put至hdfs目录/datas/下 hdfs dfs -put num_data /datas/num_data # 查看是否拷贝成功 hdfs dfs -ls /datas # 打印hdfs目录中/datas/num_data中的内容 hdfs dfs -cat /datas/num_data
运行spark-submit hdfs_addFile.py
需要注意的是addFile默认识别本地路径,若是hdfs路径,需要指定hdfs://hadoop-maste:9000
协议、uri及端口信息。
再来看一下读取网络文件信息。在182.150.37.49这台机器上,我安装了httpd服务器。httpd的配置及安装参考我的这个笔记:【链接】
进入到安装了httpd服务器的机器的/var/www/html/目录,在这个目录中新建num_data文件,文件内容为100
然后编写http_addFile.py文件,在代码中读取刚才新建的httpd服务器上的num_data文件。
内容如下:
from pyspark import SparkFiles
import numpy as np
from pyspark import SparkContext
from pyspark import SparkConf
conf = SparkConf()
conf.set('master','spark://hadoop-maste:7077')
context = SparkContext(conf=conf)
path = 'http://192.168.0.6:808/num_data'
context.addFile(path)
rdd = context.parallelize(np.arange(10))
def fun(iterable):
with open(SparkFiles.get('num_data')) as f:
value = int(f.readline())
return [x*value for x in iterable]
print(rdd.mapPartitions(fun).collect())
context.stop()
运行sparksubmit http_addFile.py
从上面的三个例子中,可以看出addFile方法的强大之处。借助该方法,在pyspark里任务运行中可以读取几乎任何位置的文件来参与计
算!
5. applicationId,用于获取注册到集群的应用的id
from pyspark import SparkContext ,SparkConf
import numpy as np
conf = SparkConf()
conf.set('master','spark://hadoop-maste:7077')
context = SparkContext(conf=conf)
rdd = context.parallelize(np.arange(10))
print('applicationId:',context.applicationId)
print(rdd.collect())
context.stop()
- binaryFiles读取二进制文件。
该方法用于读取二进制文件例如音频、视频、图片,对于每个文件器返回一个tuple,tuple的第一个元素为文件的路径,第二个参数为二
进制文件的内容。
我在hdfs的/datas/pics文件目录下上传了两张图片,使用binaryFiles读取/datas/pics目录中的二进制图片数据
新建binaryFiles.py文件,内容如下:
from pyspark import SparkContext ,SparkConf
import numpy as np
conf = SparkConf()
conf.set('master','spark://hadoop-maste:7077')
context = SparkContext(conf=conf)
rdd = context.binaryFiles('/datas/pics/')
print('applicationId:',context.applicationId)
result = rdd.collect()
for data in result:
print(data[0],data[1][:10])
context.stop()
运行spark-submit binaryFiles.py
该方法对于读取二进制文件的数据非常方便,特别是处理图片、音视频的时候。
- broadcast广播变量
SparkContext上的broadcast方法用于创建广播变量,对于大于5M的共享变量,推荐使用广播。广播机制可以最大限度的减少网络IO,从而提升性能。接下来例子中,广播一个‘hello’字符串,在各个task中接收广播变量,拼接返回。新建broadcast.py文件,内容如下。
from pyspark import SparkContext ,SparkConf
import numpy as np
conf = SparkConf()
conf.set('master','spark://hadoop-maste:7077')
context = SparkContext(conf=conf)
broad = context.broadcast(' hello ')
rdd = context.parallelize(np.arange(27),3)
print('applicationId:',context.applicationId)
print(rdd.map(lambda x:str(x)+broad.value).collect())
context.stop()
运行spark-submit broadcast.py
从结果来看,分布式运行中的每个任务中都接收到了广播的变量hello.
- defaultMinPartitions.py 获取默认最小的分区数
from pyspark import SparkContext ,SparkConf
import numpy as np
conf = SparkConf()
conf.set('master','spark://hadoop-maste:7077')
context = SparkContext(conf=conf)
print('defaultMinPartitions:',context.defaultMinPartitions)
context.stop()
-
emptyRDD创建一个空的RDD ,该RDD没有分区,也没有任何数据
sc = sparkContext sc.emptyRDD() rdd.collect() rdd.getNumPartitions()
-
getConf()方法返回作业的配置对象信息
sc.getConf().toDebugString()
-
getLocalProperty和setLocalProperty获取和设置在本地线程中的属性信息。通过setLocalProperty设置,设置的属性只能对当前线程提交的作业起作用,对其他作业不起作用。key:value类型的属性
-
setLogLevel设置日志级别,通过这个设置将会覆盖任何用户自定义的日志等级设置。取值有:ALL, DEBUG, ERROR, FATAL, INFO,OFF, TRACE, WARN通过对比两种不同的日志级别的输出,可以看出不同的日志级别的日志输出量是不同的,可以通过这一点选择合适的日志级别进行调试。
-
getOrCreate得到或者是创建一个SparkContext对象,该方法创建的SparkContext对象为单例对象。该方法可以接受一个Sparkconf对象。
-
hadoopFile读取‘老’的hadoop接口提供hdfs文件格式.
`sc.hadoopFile(’/datas/num_data’, inputFormatClass=‘org.apache.hadoop.mapred.TextInputFormat’, keyClass=‘org.apache.hadoop.io.Text’, valueClass='第一个参数为文件路径,第二个参数为输入文件的格式,第三个参数为键的格式,第四个参数为值的格式
sc.hadoopFile("/datas/num_data", inputFormatClass="org.apache.hadoop.mapred.TextInputFormat",
keyClass="org.apache.hadoop.io.Text", valueClass="org.apache.hadoop.io.LongWritable").collect()
读取出来默认会把行号作为键!
- textFile和saveAsTextFile读取位于HDFS上的文本文件
这个方法读取位于hdfs上的文本类型的文件最简单
-
parallelize使用python集合创建RDD,可以使用range函数,当然也可也使用numpy里面的arange方法来创建
-
saveAsPickleFile和pickleFile将RDD保存为python中的pickle压缩文件格式。
sc.parallelize(range(100),3).saveAsPickleFile('/datas/pickles/bbb', 5) sorted(sc.pickleFile('/datas/pickles/bbb', 3).collect())
sorted方法中使用关键字参数reverse,设置为True
-
range(start, end=None, step=1, numSlices=None)按照提供的起始值和步长,创建RDD
numSlices用于指定分区数rdd = sc.range(1,100,11,3) rdd.collect() dir(rdd)
-
runJob(rdd, partitionFunc, partitions=None, allowLocal=False)在给定的分区上运行指定的函数
partitions用于指定分区的编号,是一个列表。若不指定分区默认为所有分区运行partitionFunc函数rdd = sc.range(1,1000,11,10) sc.runJob(rdd,lambda x:[a for a in x])
指定在0,1,4,6分区上运行a**2函数
rdd = sc.range(1,1000,11,10) sc.runJob(rdd,lambda x:[a**2 for a in x],partitions=[0,1,4,6])
-
setCheckpointDir(dirName)设置检查点的目录,检查点用于异常发生时错误的恢复,该目录必须为HDFS目录。
设置检查点目录为/datas/checkpoint/sc.setCheckpointDir('/datas/checkpoint/') rdd = sc.range(1,1000,11,10) rdd.checkpoint() rdd.collect()
运行完成之后,查看hdfs 的/datas/checkpoint目录
发现有运行过程中的数据被保存下来,在Spark程序运行过程中若发生异常,将会使用检查点数据来恢复异常。
-
sparkUser获取运行当前作业的用户名
sc.sparkUser()
-
startTime返回作业启动的时间
sc.startTime
它返回的是Long类型的毫秒时间值,可借助在线时间转换工具查看具体时间
-
statusTracker()方法用于获取StatusTracker对象,通过该对象可以获取活动的Jobs的id,活动的stage 的id。job的信息,stage的信息。可以使用这个对象来实时监控作业运行的中间状态数据。
t = sc.statusTracker() dir(t)
-
stop()方法用于停止SparkContext和cluster的连接。一般在书写程序最后一行都要加上这句话,确保作业运行完成之后连接和cluster集群断开。
sc.stop()
-
uiWebUrl返回web的url
sc.uiWebUrl
-
union(rdds)用合并多个rdd为一个rdd
rdd1 = sc.parallelize(range(5),4) rdd2 = sc.parallelize(range(10),3) rdd3 = sc.union([rdd1,rdd2]) rdd3.collect()
获取rdd的分区数:
rdd.getNumPartitions()
-
version获取版本号
sc.version