spark学习2

数据通过解析后被存放成case class类型,如果想要提取处理,就需要将数据转换成df toDF()

而一旦转换成DF 对应的表头字段和case class中的相对应,df的一些有趣操作,

 ratingsData.toDF().filter($"rating" > 4).select("product").rdd.map((_, 1)).reduceByKey(_ + _).sortBy(_._2, false).map(_._1).take(100),其实我是想求一个聚合函数,一时竟想不起来该怎么做了,索性转成rdd变成求wordcount

其实可以这么解决 通过注册成临时表 然后通过sql语句查询 ,df只有注册成临时表才能像sql那样查询,

spark.sql("select product,count(*) from A group by product ")简单的分组聚合累加

 或者直接通过 df提供的方法ratingsData.toDF().filter($"rating" > 4).groupBy("product").count().createTempView("A")

groupBy("product".count())免去了注册临时表的麻烦 createTempView

在此需要提示一下,临时表分为好几种,如果是global 的需要在前面加上global_temp.xxx否则会报找不到此表

       val predictResultOfTestSet = recomModel.predict(testSetOfRatingData.map {
         case Rating(user, product, rating) => (user, product)
       })
    
       val formatResultOfTestSet = testSetOfRatingData.map {
         case Rating(user, product, rating) => ((user, product), rating)
       }
scala的强大之处case class模式匹配 如果对应与这样则转换成指定的格式 testSetOfRatingData.map {
         case Rating(user, product, rating) => (user, product) 其实不需要这么麻烦,

map(x=>(x.user,x.product))即可

rdd的cache 和persist   

从源码看cache是直接调用的persist,cache的默认存储级别为memory,而persist可以通过传入存储级别改变

spark-env调优 

spark_worker_cores 总共的核数允许spark ,默认全部

spark_worker_momoey 默认总内存减去1G

master_port 7077 webui 8080

spark中的一些概念:Aplication应用就是一个spark-submit提交的一个任务被称之为Application应用

stage划分 宽依赖 窄依赖 如果父rdd进入一个窄依赖否则宽依赖

用户提交的应用程序代码在spark中运行起来就是一个driver,

spark on yarn有两种方式 cluster client cluster一般用于生产环境,client用于交互测试。主要区别是 ,client的driver运行在客户端进程中,后者的driver运行在nodemanager的applicationmaster中,driver负责分发作业的,由于client模式driver在客户端上,客户端可能与executor,不再同一局域网中,通信会很慢

提交作业时yarn会将spark jars分发到yarn的container中,这十分耗费资源,故而我们可以将spark jars放在一个yarn可以访问到的目录中,具体做法如下:

vi spark-default.cong  添加

 spark.yarn.jars  hdfs://ip:8021/somepath/*

保存生效,并将所有的spark jars上传至hdfs://ip:9000/somepath/即可。
 

spark对于同时运行的作业数目有一定的限制,该限制由参数"spark.port.maxRetries"决定,该参数默认值为16,表明同一时间运行的作业最多为17个(不是16个哦),生产上16肯定太小,我们可以更改这一数字,具体做法如下:

vi spark-default.cong  添加

spark.port.maxRetries  ${业务所需要的数值

https://blog.csdn.net/qq_33624952/article/details/79341034

这里写图片描述

YARN 是一个资源管理、任务调度的框架,主要包含三大模块:ResourceManager(RM)、 NodeManager(NM)、ApplicationMaster(AM)。 ResourceManager 负责所有资源的监控、分配和管理; ApplicationMaster 负责每一个具体应用程序的调度和协调; NodeManager 负责每一个节点的维护。 对于所有的 applications,RM 拥有绝对的控制权和对资源的分配权。而每个 AM 则会和 RM 协商资源,同时和 NodeManager 通信来执行和监控 task

Resourcemanager:

 ResourceManager 负责整个集群的资源管理和分配,是一个全局的资源管理系统。  NodeManager 以心跳的方式向 ResourceManager 汇报资源使用情况(目前主要是 CPU 和 内存的使用情况)。RM 只接受 NM 的资源回报信息,对于具体的资源处理则交给 NM 自己 处理。  YARN Scheduler 根据 application 的请求为其分配资源,不负责 application job 的 监控、追踪、运行状态反馈、启动等工作。

Nodemanager:

 NodeManager 是每个节点上的资源和任务管理器,它是管理这台机器的代理,负责该节 点程序的运行,以及该节点资源的管理和监控。YARN 集群每个节点都运行一个 NodeManager。  NodeManager 定时向 ResourceManager 汇报本节点资源(CPU、内存)的使用情况和 Container 的运行状态。当 ResourceManager 宕机时 NodeManager 自动连接 RM 备用节 点。  NodeManager 接收并处理来自 ApplicationMaster 的 Container 启动、停止等各种请 求。

ApplicationMaster:

用 户 提 交 的 每 个 应 用 程 序 均 包 含 一 个 ApplicationMaster , 它 可 以 运 行 在 ResourceManager 以外的机器上。  负责与 RM 调度器协商以获取资源(用 Container 表示)。  将得到的任务进一步分配给内部的任务(资源的二次分配)。  与 NM 通信以启动/停止任务。  监控所有任务运行状态,并在任务运行失败时重新为任务申请资源以重启任务。

RM 只负责监控 AM,并在 AM 运行失败时候启动它。RM 不负责 AM 内部任务的容错,
任务的容错由 AM 完成。

用户向YARN中提交应用程序,其中包括ApplicationMaster程序、启动ApplicationMaster的命令、用户程序等。 步骤2 ResourceManager为该应用程序分配第一个Container,并与对应的Node-Manager通信,要求它在这个Container中启动应用程序的ApplicationMaster。 步骤3 ApplicationMaster首先向ResourceManager注册,这样用户可以直接通过ResourceManager查看应用程序的运行状态,然后它将为各个任务申请资源,并监控它的运行状态,直到运行结束,即重复步骤4~7。 步骤4 ApplicationMaster采用轮询的方式通过RPC协议向ResourceManager申请和领取资源。 步骤5 一旦ApplicationMaster申请到资源后,便与对应的NodeManager通信,要求它启动任务。 

yarn资源调度器:fifo 先进先出不适合共享集群,大应用会占用太长时间,

Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能 力。通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源,这样整个集 群就可以通过设置多个队列的方式给多个组织提供服务了。除此之外,队列内部又可以垂直 划分,这样一个组织内部的多个成员就可以共享这个队列资源了,在一个队列内部,资源的 调度是采用的是先进先出(FIFO)策略。

 在 Fair 调度器中,我们不需要预先占用一定的系统资源,Fair 调度器会为所有运行的 job 动态的调整系统资源。如下图所示,当第一个大 job 提交时,只有这一个 job 在运行, 此时它获得了所有集群资源;当第二个小任务提交后,Fair 调度器会分配一半资源给这个 小任务,让这两个任务公平的共享集群资源。 需要注意的是,在下图 Fair 调度器中,从第二个任务提交到获得资源会有一定的延 迟,因为它需要等待第一个任务释放占用的 Container。小任务执行完成之后也会释放自 己占用的资源,大任务又获得了全部的系统资源。最终效果就是 Fair 调度器即得到了高的 资源利用率又能保证小任务及时完成。

  Spark集群部署后,需要在主节点和从节点分别启动Master进程和Worker进程,对整个集群进行控制。在一个Spark应用的执行过程中,Driver和Worker是两个重要角色。Driver程序是应用逻辑执行的起点,负责作业的调度,即Task任务的分发,而多个Worker用来管理计算节点和创建Executor并行处理任务。在执行阶段,Driver会将Task和Task所依赖的file和jar序列化后传递给对应的Worker机器,同时Executor对相应数据分区的任务进行处理。

  Spark的架构中的基本组件:

  • Cluster Manager:在standalone模式中即为Master主节点,控制整个集群,监控worker。在YARN模式中为资源管理器
  • Worker:从节点,负责控制计算节点,启动Executor或者Driver。在YARN模式中为NodeManager,负责计算节点的控制。
  • Driver:运行Application的main()函数并创建SparkContext。
  • Executor:执行器,在worker node上执行任务的组件、用于启动线程池运行任务。每个Application拥有独立的一组Executor。
  • SparkContext:整个应用的上下文,控制应用的生命周期。
  • RDD:Spark的基础计算单元,一组RDD可形成执行的有向无环图RDD Graph。
  • DAG Scheduler:根据作业(task)构建基于Stage的DAG,并提交Stage给TaskScheduler。
  • TaskScheduler:将任务(task)分发给Executor执行。
  • SparkEnv:线程级别的上下文, 存储运行时的重要组件的引用。
  • Application:Application都是指用户编写的Spark应用程序,其中包括一个Driver功能的代码和分布在集群中多个节点上运行的Executor代码
  • Driver:Spark中的Driver即运行上述Application的main函数并创建SparkContext,创建SparkContext的目的是为了准备Spark应用程序的运行环境,在Spark中有SparkContext负责与ClusterManager通信,进行资源申请、任务的分配和监控等,当Executor部分运行完毕后,Driver同时负责将SparkContext关闭,通常用SparkContext代表Driver
  • Executor:某个Application运行在worker节点上的一个进程,该进程负责运行某些Task,并且负责将数据存到内存或磁盘上,每个Application都有各自独立的一批Executor,在Spark on Yarn模式下,其进程名称为CoarseGrainedExecutor Backend。一个CoarseGrainedExecutor Backend有且仅有一个Executor对象,负责将Task包装成taskRunner,并从线程池中抽取一个空闲线程运行Task,这个每一个CoarseGrainedExecutor Backend能并行运行Task的数量取决于分配给它的cup个数
  • Cluster Manager:指的是在集群上获取资源的外部服务。目前有三种类型
  1.  standalone:spaark原生的资源管理,由Master负责资源的分配
  2. Apache Mesos:与hadoop MR兼容性良好的一种资源调度框架
  3. Hadoop Yarn:主要指Yarn中的ResourceManager
  • Worker:集群中任何可以运行Application代码的节点,在Standalone模式中指的是通过slave文件配置的Worker节点,在Spark on Yarn模式下就是NoteManager节点
  • Task:被送到某个Executor上的工作单元,但HadoopMR中的MapTask和ReduceTask概念一样,是运行Application的基本单位,多个Task组成一个Stage,而Task的调度和管理等是由TaskScheduler负责
  • Job:包含多个Task组成的并行计算,往往由Spark Action触发生成,一个Application中往往会产生多个Job
  • Stage:每个Job会被拆分成多组Task,作为一个TaskSet,其名称为Stage,Stage的划分和调度是有DAGScheduler来负责的,Stage有非最终的Stage(Shuffle Map Stage)和最终的Stage(Result Stage)两种,Stage的边界就是发生Shuffle的地方
  • DAGScheduler:根据Job构建基于Stage的DAG(Directed Acyclic Graph有向无环图),并提交Stage给TASKScheduler。其划分Stage的根据是RDD之间的依赖的关系找出开销最小的调度方法,如下图

spark2.0 新的api SparkSession 取代   

 val spark = SparkSession.builder.appName("Simple Application").getOrCreate()

spark.read.textFile("xxx") 返回值为dataset 

一些transformation map filter flatmap mappartitions reducebykey 

action reduce collect count take

七种存储级别

spark checkpoint 两种方式一种 mertadata checkpoint类似于hdfs 记录日志以防不测

data checkpoint 直接将数据记录到hdfs

重头戏:kafka和sparkStreaming集成:

因为kafka有了新的消费者api在version0.8 和 0.10,所以有两种方式去集成kafka,感觉kafka很麻烦,搞那么多干嘛,自己给自己找麻烦,版本不匹配,是个问题,幸亏cdh解决了这个问题,目前kafka 0.8 版本集成已经被spark2.3.0以上的抛弃了,

  spark-streaming-kafka-0-8 spark-streaming-kafka-0-10
Broker Version 0.8.2.1 or higher 0.10.0 or higher
API Maturity Deprecated Stable
Language Support Scala, Java, Python Scala, Java
Receiver DStream Yes No
Direct DStream Yes Yes
SSL / TLS Support No Yes
Offset Commit API No Yes
Dynamic Topic Subscription No Yes

0.8版本的已经被spark2.3.0及以上给抛弃了,

但还是有两种方法去集成,第一种:receiver方式和kafka的高级api,第二种方式spark1.3提出更高的性能

方法一 基于receiver的方法,receiver 用kafka高级消费者api实现,但是这种方法会丢失数据,宋轶还需要开启wal机制(write ahead log)预写日志,从spark1.2引入,该机制为receiver接受到的数据全部被写入配置好的checkpoint目录中,避免数据丢失,配置该项需要

streamingContext的checkpoint() 设置一个checkpoint目录,然后spark.streaming.receiver.writeAheadLog.enable参数设置为true

但是wal的缺点:

会导致Receiver的吞吐量大幅下降,因为单位时间内有相当一部分时间将数据写入与写日志

解决办法:
如果又希望开启预写日志机制,确保数据零损失,又不希望影响系统的吞吐量,那么可以创建多个输入DStream启动多个Reciver,然后将这些receiver接收到的数据使用ssc.union()方法将这些dstream中的数据进行合并 。此外在启用了预写日志机制之后,推荐将复制持久化机制禁用掉,因为所有数据已经保存在容错的文件系统中了,不需要再用复制机制进行持久化,保存一份副本了,只要将输入的DStream的持久化机制设置一下即可

 groupId = org.apache.spark
 artifactId = spark-streaming-kafka-0-8_2.11
 version = 2.4.0
 import org.apache.spark.streaming.kafka._

 val kafkaStream = KafkaUtils.createStream(streamingContext,
     [ZK quorum], [consumer group id], [per-topic number of Kafka partitions to consume])

 第二种是direct 方式,没有receiver,通过轮循定期的方式去查询kafka每个topic+partition的最后偏移量,kafka simple api ,就像是读取本地文件,相比与receive方式,有以下几个好处

1 更加简单的并行度,direct会创建和kafkapartition数量一致的rdd 分区去消费,一对一的消费

2 高效 3执行一次语义:receiver方式用kafka高级api,存储消费记录在zookeeper上,可能会导致多次消费,所以第二种方式,direct方式,用simple api将offest存在checkpoint上,

 groupId = org.apache.spark
 artifactId = spark-streaming-kafka-0-8_2.11
 version = 2.4.0

    val spark = SparkSession.builder()

      .master("local[2]")

      .appName("streaming").getOrCreate()

    val sc =spark.sparkContext

    val ssc = new StreamingContext(sc, Seconds(5))

    // Create direct kafka stream with brokers and topics

    val topicsSet =Set("weblogs")

    val kafkaParams = Map[String, String]("metadata.broker.list" -> "node5:9092")

    val kafkaStream = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](

      ssc, kafkaParams, topicsSet)

    val lines = kafkaStream.map(x => x._2)

    val words = lines.flatMap(_.split(" "))

 10版本的kafka broker

和8版本的direct方式差不多,但是因为kafka new api还是有一点不同,

groupId = org.apache.spark
artifactId = spark-streaming-kafka-0-10_2.11
version = 2.4.0

已经将kafka依赖添加进来了,不要在手动添加kafka-client等依赖, 

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.kafka010._
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe

val kafkaParams = Map[String, Object](
  "bootstrap.servers" -> "localhost:9092,anotherhost:9092",
  "key.deserializer" -> classOf[StringDeserializer],
  "value.deserializer" -> classOf[StringDeserializer],
  "group.id" -> "use_a_separate_group_id_for_each_stream",
  "auto.offset.reset" -> "latest",
  "enable.auto.commit" -> (false: java.lang.Boolean)
)

val topics = Array("topicA", "topicB")
val stream = KafkaUtils.createDirectStream[String, String](
  streamingContext,
  PreferConsistent,
  Subscribe[String, String](topics, kafkaParams)
)

stream.map(record => (record.key, record.value))

如果要保证exactly once 需要存储kafka offest ,有三种方式

1存在checkpoint中,

2kafka itself kafka自己维护了一个自动提交offest的方法,但是这种方式可能会出现一下情况,成功提交offest,但是spark operate没有进行处理,所以这也是为什么需要sets “enable.auto.commit” to false,但是可以自己手动提交offest的记录

猜你喜欢

转载自blog.csdn.net/qq_38250124/article/details/88322970
今日推荐