1.spark安装
(1)上传,解压
(2)进入conf目录并重命名并修改spark-env.sh.template文件
cd conf/
mv spark-env.sh.template spark-env.sh
vi spark-env.sh
在该配置文件中添加如下配置
export JAVA_HOME=/usr/local/jdk1.8.0_152
export SPARK_MASTER_IP=hadoop01
export SPARK_MASTER_PORT=7077
(3)重命名并修改slaves.template文件
mv slaves.template slaves
vi slaves
在该文件中添加子节点所在的位置(Worker节点)
hadoop02
hadoop03
保存退出
将配置好的Spark拷贝到其他节点上
scp -r spark-1.6.1-bin-hadoop2.6/ root@hadoop02:$PWD
(4)在sbin目录下启动./start-all.sh
2.RDD的概念
RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,
它代表一个不可变(创建了内容不可变)、可分区、里面的元素可并行计算的集合
特点:
1.由多个分区组成。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。
2.一个计算函数用于每个分区。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。
3.RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。数据丢失时,根据依赖重新计算丢失的分区而不是整个分区。
4.一个Partitioner,即RDD的分片函数。默认是HashPartition
5.分区数据的最佳位置去计算。就是将计算任务分配到其所要处理数据块的存储位置。数据本地化
RDD中两种算子:
transformation 和 action
常用的transformation:
(1)map、flatMap、filter
(2)intersection求交集、union求并集:注意类型要一致
distinct:去重
(3)join:类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD
(4)groupByKey:在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD
但是效率reduceByKey较高,因为有一个本地combiner的过程。
(5)cartesian笛卡尔积
常用的action
(1)collect()、count()
(2)collectAsMap:将K,V格式的RDD回收到Driver端作为Map使用
(3)reduce:通过func函数聚集RDD中的所有元素
(4)take(n):取前n个;top(2):排序取前两个
(5)takeOrdered(n):排完序后取前n个
(6)countByKey:统计相同的key 出现的个数
(7)countByValue:计数RDD中相同的value 出现的次数,不必须是K,V格式的RDD
(8)first : 取出第一个元素
(9)foreach : 遍历RDD中的每个元素
(10)foreachPartition:
(11)takeSample : takeSample(withReplacement,num,seed) 随机抽样将数据结果拿回Driver端使用,
返回Array。withReplacement:有无放回抽样num:抽样的条数 seed:种子
spark启动流程
1.调用start-all.sh脚本,首先启动Master服务
2.脚本开始解析slaves配置文件,解析到启动Worker的响应节点
开始向启动Worker的节点发送启动命令来启动Worker
3.Worker开始向Master发送注册信息
4.Master接收到注册信息后并保存到内存和磁盘,保存后向Worker发送Mster的url
5.Worker接收大Master的url并保存,调用一个定时器,定时的向Master心跳
6.Master收到Worker的心跳后,找到相应的WorkerInfo,把最后一次心跳时间更改为当前时间
Spark SQL
Spark SQL是Spark用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用。
与RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。
Spark SQL 基础代码:
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}
object SparkSql {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("SparkSql").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val linesRDD: RDD[Array[String]] = sc.textFile("D:/person/person.txt").map(_.split(","))
//导入隐式转换,如果不导入无法将RDD转换成DataFrame
//将RDD转换成DataFrame
import sqlContext.implicits._
val personRDD: RDD[Persons] = linesRDD.map(x => Persons(x(0).toInt,x(1),x(2).toInt))
val personDF: DataFrame = personRDD.toDF
personDF.show()
personDF.select("name").show()
personDF.select(personDF.col("age")+1).show()
//显示Schema信息
personDF.printSchema()
personDF.filter(personDF.col("age") < 30).show()
personDF.groupBy("age").count().show()
//注册临时表
personDF.registerTempTable("person")
//sql
val df: DataFrame = sqlContext.sql("select * from person where age < 30 order by age asc")
df.write.mode("append").json("hdfs://hadoop01:9000/out-2018920-1")
//mode
//- `overwrite`: overwrite the existing data.
// - `append`: append the data.
// - `ignore`: ignore the operation (i.e. no-op).
// - `error`: default option, throw an exception at runtime.
}
}
case class Persons(id: Int, name: String, age: Int)
SparkStreaming:
用于流式数据的处理
特点:1.易用2.容错3.易整合到spark体系
DStream上的原语与RDD的类似,分为Transformations(转换)和Output Operations(输出)两种
transformations:
map(func)
flatMap(func)
filter(func)
repartition(numPartitions)
union(otherStream)
count()
reduce(func)
countByValue()
reduceByKey(func, [numTasks])
join(otherStream, [numTasks])
cogroup(otherStream, [numTasks])
transform(func)
updateStateByKey(func)
特殊的Transformations:
UpdateStateByKey,Transform,Window
窗口操作:一段时间内数据发生变化
两个重要参数:窗口长度,滑动间隔
注意窗口长度和滑动间隔必须是批次间隔的倍数
Output Operations on DStreams:
可以将DStream的数据输出到外部的数据库或文件系统,当某个Output Operations原语被调用时(与RDD的Action相同),
streaming程序才会开始真正的计算过程。
print()
saveAsTextFiles(prefix, [suffix])
saveAsObjectFiles(prefix, [suffix])
saveAsHadoopFiles(prefix, [suffix])
foreachRDD(func)
模板代码:
//创建SparkConf并设置为本地模式运行
//注意local[2]代表开两个线程
val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
//设置DStream批次时间间隔为2秒
val ssc = new StreamingContext(conf, Seconds(2))
//通过网络读取数据
val lines = ssc.socketTextStream("192.168.10.101", 9999)
//将读到的数据用空格切成单词
val words = lines.flatMap(_.split(" "))
//将单词和1组成一个pair
val pairs = words.map(word => (word, 1))
//按单词进行分组求相同单词出现的次数//结果不累加
val wordCounts = pairs.reduceByKey(_ + _)
//打印结果到控制台
wordCounts.print()
//开始计算
ssc.start()
//等待停止
ssc.awaitTermination()
结果每次在Linux段输入的单词次数都被正确的统计出来,但是结果不能累加!如果需要累加需要使用updateStateByKey(func)来更新状态
//updateStateByKey(func),结果可以累加但是需要传入一个自定义的累加函数
val conf: SparkConf = new SparkConf().setAppName("SparkStreamACCWordCount").setMaster("local[2]")
//批次间隔
val ssc = new StreamingContext(conf,Milliseconds(5000))
//最好目录设在hdfs
ssc.checkpoint("hdfs://hadoop01:9000/out-2018-8-1")
//获取数据
val dStream: ReceiverInputDStream[String] = ssc.socketTextStream("hadoop01",6666)
//将获取到的数据生成一个一个元组
val tupled: DStream[(String, Int)] = dStream.flatMap(_.split(" ")).map((_,1))
//调用updateStateByKey来进行聚合
val sumed: DStream[(String, Int)] = tupled.updateStateByKey(func,new HashPartitioner(ssc.sparkContext.defaultParallelism),true)
sumed.print()
ssc.start()
ssc.awaitTermination()
}
/**
* 该函数中,参数只有一个Iterator,其中有三个类型,分别代表
* String代表元组中的每一个单词作为key
* seq[Int]代表当前批次相同单词出现的次数:Seq(1,1,1,1)
* Option[Int]代表上一批次相同单词累加的结果,有可能有值也有可能没有值
* 所以用Option封装,此时取值最好用getElse方法
*
*/
val func = (it: Iterator[(String, Seq[Int], Option[Int])]) =>{
it.map(x =>{
(x._1,x._2.sum + x._3.getOrElse(0))
})
}
需求:获取kafka的数据并做批次累加功能的单词计数
注意1.怎么设置获取kafka的一些配置
2.怎么应用updateStateByKey
val conf: SparkConf = new SparkConf().setAppName("LoadKafkaDataAndWordCount").setMaster("local[2]")
val ssc = new StreamingContext(conf,Seconds(5))//批次间隔5秒
//实现按批次累加功能,需要checkpoint
ssc.checkpoint("hdfs://hadoop01:9000/cp-20180802-1")
//设置请求kafka的一些配置信息
val Array(zkQuorum,group,topics,numThreads) = args
//把每个topic放到一个map里
val topicMap: Map[String, Int] = topics.split(",").map((_,numThreads.toInt)).toMap
//调用kafka工具类获取kafka集群的数据
val data: ReceiverInputDStream[(String, String)] =
KafkaUtils.createStream(ssc,zkQuorum,group,topicMap)
//因为key对应的是offset值,不需要,只留下数据
val lines: DStream[String] = data.map(_._2)
//计算单词计数
val tupled: DStream[(String, Int)] = lines.flatMap(_.split(" ")).map((_,1))
val sumed: DStream[(String, Int)] =
tupled.updateStateByKey(func,new HashPartitioner(ssc.sparkContext.defaultParallelism),true)
sumed.print()//打印
ssc.start()//线程执行
ssc.awaitTermination()//线程等待
}
//Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)
val func = (it: Iterator[(String,Seq[Int],Option[Int])]) => {
it.map{
case (x,y,z) => {
(x,y.sum + z.getOrElse(0))
}
}
}
SparkStreaming获取Kafka的两种方式:
reciever:
是利用 Kafka 消费者高级 API 在 Spark 的工作节点上创建消费者线程,订阅 Kafka 中的消息,
数据会传输到 Spark 工作节点的执行器中,但是默认配置下这种方法在 Spark Job 出错时会导致数据丢失,
如果要保证数据可靠性,需要在 Spark Streaming 中开启Write Ahead Logs(WAL),
也就是上文提到的 Kafka 用来保证数据可靠性和一致性的数据保存方式。
可以选择让 Spark 程序把 WAL 保存在分布式文件系统(比如 HDFS)
direct:
不需要建立消费者线程,使用 createDirectStream 接口直接去读取 Kafka 的 WAL,将 Kafka 分区与 RDD 分区做一对一映射,
相较于第一种方法,不需再维护一份 WAL 数据,提高了性能。读取数据的偏移量由 Spark Streaming 程序通过检查点机制自身处理,
避免在程序出错的情况下重现第一种方法重复读取数据的情况,消除了 Spark Streaming 与 ZooKeeper/Kafka 数据不一致的风险。
保证每条消息只会被 Spark Streaming 处理一次。