Spark核心编程1

  • 概念导入

spark方法不同于scala,以及为了提高高并发和高吞吐的数据处理,封装3大数据结构:RDD  累加器  广播变量

  • 分布式计算模拟

1. 分布式计算模拟案例1(一个driver 和一个executor)

package com.byxrs.spark_core.test_distributed

import java.net.Socket

/**
 * 分布式小demo
 *
 */
object Driver {
  def main(args: Array[String]): Unit = {
    //链接服务器
    val client = new Socket("localhost", 9999)
    val stream = client.getOutputStream
    stream.write(2)
    stream.flush()
    stream.close()
    client.close()
  }
}
package com.byxrs.spark_core.test_distributed

import java.net.{ServerSocket, Socket}

/**
 *
 * 服务器
 *
 */
object Executor {
  def main(args: Array[String]): Unit = {
    //启动服务器,接收数据
    val server = new ServerSocket(9999)
    println("服务器已启动,等待接收数据")
    //等待客户端链接
    val client: Socket = server.accept()
    val in = client.getInputStream
    val i: Int = in.read()
    println("接收的数据为:" + i)
    in.close()
    client.close()
    server.close()
  }
}

运算结果是:

服务器已启动,等待接收数据
接收的数据为:2

2. 分布式计算模拟案例2(一个driver 和一个executor)

package com.byxrs.spark_core.test_distributed

class Task extends Serializable {
  val datas = List(1, 2, 3, 4)

  //匿名函数
  val logic: (Int) => Int = _ * 2

  def compute():List[Int]={
    datas.map(logic)
  }
}
package com.byxrs.spark_core.test_distributed

import java.io.ObjectInputStream
import java.net.{ServerSocket, Socket}

/**
 *
 * 服务器
 *
 */
object Executor {
  def main(args: Array[String]): Unit = {
    //启动服务器,接收数据
    val server = new ServerSocket(9999)
    println("服务器已启动,等待接收数据")
    //等待客户端链接
    val client: Socket = server.accept()
    val in = client.getInputStream
    val objIn = new ObjectInputStream(in)
    val task = objIn.readObject().asInstanceOf[Task]
    val ints = task.compute()
    println("计算结果为:" + ints)
    in.close()
    client.close()
    server.close()
  }
}
package com.byxrs.spark_core.test_distributed

import java.io.ObjectOutputStream
import java.net.Socket

/**
 * 分布式小demo
 *
 */
object Driver {
  def main(args: Array[String]): Unit = {
    //链接服务器
    val client = new Socket("localhost", 9999)
    val stream = client.getOutputStream
    val objOut = new ObjectOutputStream(stream)
    val task = new Task
    objOut.writeObject(task)
    stream.flush()
    stream.close()
    client.close()
  }
}

运行结果为:

服务器已启动,等待接收数据
计算结果为:List(2, 4, 6, 8)

3. 分布式计算模拟案例3(一个driver 和两个executor)

 

package com.byxrs.spark_core.test_distributed

import java.io.ObjectInputStream
import java.net.{ServerSocket, Socket}

object Executor {
  def main(args: Array[String]): Unit = {
    //启动服务器,接收数据
    val server = new ServerSocket(9999)
    println("8888服务器已启动,等待接收数据")
    //等待客户端链接
    val client: Socket = server.accept()
    val in = client.getInputStream
    val objIn = new ObjectInputStream(in)
    val task: SubTask = objIn.readObject().asInstanceOf[SubTask]
    val ints = task.compute()
    println("9999计算结果为:" + ints)
    in.close()
    client.close()
    server.close()
  }
}
package com.byxrs.spark_core.test_distributed

import java.io.ObjectInputStream
import java.net.{ServerSocket, Socket}

object Executor2 {
  def main(args: Array[String]): Unit = {
    //启动服务器,接收数据
    val server = new ServerSocket(8888)
    println("8888服务器已启动,等待接收数据")
    //等待客户端链接
    val client: Socket = server.accept()
    val in = client.getInputStream
    val objIn = new ObjectInputStream(in)
    val task :SubTask= objIn.readObject().asInstanceOf[SubTask]
    val ints = task.compute()
    println("8888计算结果为:" + ints)
    in.close()
    client.close()
    server.close()
  }
}
package com.byxrs.spark_core.test_distributed

import java.io.ObjectOutputStream
import java.net.Socket

/**
 * 分布式小demo
 *
 */
object Driver {
  def main(args: Array[String]): Unit = {
    //链接服务器
    val client1= new Socket("localhost", 9999)
    val client2 = new Socket("localhost", 8888)
    val task = new Task

    val stream1 = client1.getOutputStream
    val objOut1 = new ObjectOutputStream(stream1)
    val subTask1 = new SubTask
    subTask1.logic = task.logic
    subTask1.datas = task.datas.take(2)
    objOut1.writeObject(subTask1)
    stream1.flush()
    stream1.close()
    client1.close()
    
    val stream2 = client2.getOutputStream
    val objOut2 = new ObjectOutputStream(stream2)
    val subTask2 = new SubTask
    subTask2.logic = task.logic
    subTask2.datas = task.datas.takeRight(2)
    objOut2.writeObject(subTask2)
    stream2.flush()
    stream2.close()
    client2.close()
  }
}

运行结果:
9999计算结果为:List(2, 4)

8888计算结果为:List(6, 8)

 

  • RDD 弹性分布式数据集(spark中最基本的数据处理模型)

1. 是spark最小计算单元,应该只是简单的业务逻辑,不应该包含复杂的业务逻辑,复杂逻辑既多个RDD。RDD(包含数据和逻辑的结构)在driver中,拆分成task再分发飞executor来做计算;

2. 如何组合RDD

RDD数据处理类似IO流,属于装饰者模式,数据是惰性加载,需要使用时才会执行;

RDD是不保存数据的。

3.RDD概念和特点

概念:抽象类,弹性的,不可变,可分区,元素可并行计算的集合;

流程:为了给task分配不同的数据,需要在RDD中进行数据分区,当读取数据进来的时候直接把不同的数据放置在不同的分区,再把分区发给task即可,最后由executor执行。

分区作用是提高并行计算能力(区别并发和并行概念),是spark最小计算单元,非常重要!

RDD是个抽象类,需要子类具体实现。

abstract class RDD[T: ClassTag](
    @transient private var _sc: SparkContext,
    @transient private var deps: Seq[Dependency[_]]
  ) extends Serializable with Logging {}


比如flatmap方法中,new MapPartitionsRDD就是一个具体的子类:
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] = withScope {
    val cleanF = sc.clean(f)
    new MapPartitionsRDD[U, T](this, (context, pid, iter) => iter.flatMap(cleanF))
  }

MapPartitionsRDD类是继承RDD的
private[spark] class MapPartitionsRDD[U: ClassTag, T: ClassTag](
    var prev: RDD[T],
    f: (TaskContext, Int, Iterator[T]) => Iterator[U],  // (TaskContext, partition index, iterator)
    preservesPartitioning: Boolean = false)
  extends RDD[U](prev) {

存储的弹性:内存和磁盘的自动切换,spark优化了性能采用的是内存来计算,所以效率比mapreduce更好一些,但是内存有极限,某种情况下可以将数据暂时放到磁盘中,在计算的过程中会自动切换,我们开始无需关注,感兴趣的可以深究其原因。

容错的弹性:数据丢失可以自动回复。假设我们读取1,2,3,4这一行内容到RDD中,RDD分为两个区,一个区放1,2  ,另外一个区放3,4 ,两个区的数据分别发给不同的task,而task由executor来执行,当一个区的数据进行executor的时候因为某些原因失败了,spark会重新读取数据放到这个区中(至于怎么知道哪些数据会放到这个区中的疑惑,RDD内部机制都已经帮我们处理好了),这保证数据不会丢失。

计算的弹性:计算出错重试机制,在分布式计算中这样的机制显得很重要,不会因为某个节点计算出错就造成整个项目停止掉。

分片分区的弹性:可根据需要重新分片,可以使得资源更加合理使用,比如原来有2个executor,后面增加到4个executor,这样重新分区来计算更加资源最大化。

分布式:数据分别存在大数据集群不同节点上,RDD就是用来计算在大数据集群方面不同节点的数据。

数据集:RDD是封装计算逻辑,并不保存数据,读取数据经过一个RDD,进行处理数据流转到下个RDD,上个RDD的数据已经不存在了。

数据抽象:RDD是抽象类,需要子类具体实现。

不可变:RDD封装了计算逻辑,是不可改变的,要改变只能是产生新的RDD,在新的RDD中封装另外的计算逻辑。

可分区,并行计算:事先在RDD准备好分区,读取数据的时候既把数据放到分区中,后面单独计算,不互相影响。

 

4.RDD五大核心属性

* Internally, each RDD is characterized by five main properties:
 *
 *  - A list of partitions
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for
 *    an HDFS file)

4.1 分区列表

RDD数据结构存在分区列表,用于执行任务时并行计算,在分布式计算中显得极其重要。

protected def getPartitions: Array[Partition]

4.2 分区计算函数

spark计算时,使用分区函数对每一个分区进行计算,task里面数据不一样,但是计算逻辑一样。

 /**
   * :: DeveloperApi ::
   * Implemented by subclasses to compute a given partition.
   */
  @DeveloperApi
  def compute(split: Partition, context: TaskContext): Iterator[T]

4.3 RDD的依赖关系

RDD是计算模型的封装,当需要将多个计算模型进行组合时,就需要产生相互的依赖关系,RDD是一层层的装饰者模式的依赖关系

/**
   * Implemented by subclasses to return how this RDD depends on parent RDDs. This method will only
   * be called once, so it is safe to implement a time-consuming computation in it.
   */
  protected def getDependencies: Seq[Dependency[_]] = deps

4.4 分区器

数据为KV类型的数据时,可以通过设定分区器进行自定义数据分区;

是用来将读取的数据进行进行分区,它具有一定规则

Option是scala中为了防止空指针的情况设置的一种类型,既意味着partitioner可以不进行设置。

/** Optionally overridden by subclasses to specify how they are partitioned. */
  @transient val partitioner: Option[Partitioner] = None

4.5 首先位置(判断计算发送到哪个节点的效率最优)

当计算分区的时候会有个首选位置,executor和数据是分布在集群中的不同节点nodeManager,具体是不知道在哪里,就会有疑惑怎么知道将某个分区数据(如1,2)的task发给哪个executor,底层机制会有一种判断nodeManager是否包含某个分区的数据,如果包含则会将task发送到该nodeManager下的executor来执行计算,这样保证效率最优。

 /**
   * Optionally overridden by subclasses to specify placement preferences.
   */
  protected def getPreferredLocations(split: Partition): Seq[String] = Nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值