hadoop记录篇13-spark计算引擎

简介

Apache Spark 是一个快速的,分布式集群计算系统.它提供了高等级的针对 Java, Scala, Python and R的API接口, 他还是一个优秀的图处理引擎. 它还支持一套高级的工具集: Spark SQL,Sql和结构化数据处理;

集群模式

Component

spark应用程序以独立进程的集合的方式运行在集群中,通过driver program中的SparkContext对象进行协调。

SparkContext能够连接多种不同类型的cluster manager(如standalone,mesos,yarn),这些cluster manager都是用来分配资源给spark应用程序的。一旦建立连接,spark就会获取到集群节点中的executor。executor以进程的形式运行在work node上,用来执行计算以及存储数据,因此多个work node就会有多个executor。driver program将spark应用程序的代码先交给SparkContext然后再发送给executor。最后,SparkContext将task发送到executor去执行。
在这里插入图片描述
架构中的一些注意事项:

  1. 每一个spark应用程序都会获取属于自己的executors进程,在整个应用程序执行的过程中,executor以多线程的形式执行task。这样有利于应用程序间的隔离,不管从调度层面来讲(每个driver调度属于自己的tasks)还是从executor层面(不同spark应用程序的tasks实际上运行在不同的jvm上)。但是,应用程序间隔离也意味着数据无法在spark应用程序(SparkContext的实例)间传递,除非将数据写到外部存储系统中。
  2. 底层的cluster manager感知不到spark的存在。只要spark能够获取到executor进程(同一个spark应用程序下的executor进程能够相互通信),即使cluster manager属于不同类型(如 mesos,yarn),spark应用程序都能够相对容易地运行起来。
  3. driver program在整个生命周期中都必须监听,接受来自属于自己的executor的连接请求(可以在network config section中的spark.driver.port项进行配置)。因此,driver program必须要保持和其他work node的网络是可以连通的。(这里可能存在跨网段问题?)
  4. 因为集群上的tasks受到driver的调度,所以driver需要够靠近work node运行,在同一个局域网中是最好的。如果想要远程向集群发送请求,那么通过RPC将请求发送给driver,由driver在近work node端提交给我们的操作,比在远work node端运行一个driver来的好一些。

Cluster Manager Types

  • Standalone,附带简单cluster manger的spark,可以轻松设置集群(单机版)
  • Apache Mesos,一个通用的cluster manger,也可以运行Hadoop MapReduce和服务应用程序
  • Hadoop YARN,Hadoop 2.x中的资源管理器
  • Kubernetes,一个开源系统,用于自动化容器化应用程序的部署,扩展和管理。

Submitting Applications

spark可以通过spark-submit脚本将应用程序提交到任何不同类型的集群中。

Monitoring

每个driver program都拥有自己的web UI,端口一般为4040,web包含运行中的tasks,executors,storage usage信息。 通过http://:4040可以访问到。

Job Scheduling

spark可以控制跨应用程序(从cluster manager的层面)的资源分配以及应用程序内(如果有多个计算发生在同一个SparkContext中)的资源分配。

Glossary

术语含义
Application用户构建在spark上的程序,在集群中包括一个driver programe和多个executor
Application jar一个装着用户的spark应用程序的jar包。有时候用户想要创建一个包含spark应用程序及其依赖项的“uber jar”。用户的jar不应该(never)包含Hadoop或Spark库,因为这些库将在运行时添加。
Driver program是一个进程,用来运行应用程序的main方法,并且创建SparkContext
Cluster manager是一个外部服务,用来获取集群中的资源(有多种模式)
Deploy mode用来区分dirver进程运行的位置。在cluster模式中,spark框架在集群内部启动driver;在client模式中,由submitter在集群外部启动driver
Worker node集群中任何可以运行spark应用程序代码的节点
Executor一个由spark应用程序创建的,位于worker node上的进程,用来执行task,将数据保存在内存或者磁盘上。每个spark应用程序都有属于自己的多个executor
Task一个工作单元,会被送往executor去执行
Job一个由多个task组成的并行计算,通过Spark的action类算子进行触发,可以在driver日志中观察到
Stage每个job可以被切分成更小的,多个task组成的集合,集合之间相互依赖(类似于mapreduce中的map stage和reduce stage)。可以在driver日志中观察到。 **

RDD编程向导

预览

在较高的层次上,每个Spark应用程序都包含一个驱动程序,该程序运行用户的主要功能并在群集上执行各种并行操作。 Spark提供的主要抽象是弹性分布式数据集(RDD),它是跨群集节点分区的元素的集合,可以并行操作。 RDD是通过从Hadoop文件系统(或任何其他Hadoop支持的文件系统)中的文件或驱动程序中的现有Scala集合开始并对其进行转换而创建的。 用户还可以要求Spark在内存中保留RDD,允许它在并行操作中有效地重用。 最后,RDD会自动从节点故障中恢复。

Spark中的第二个抽象是可以在并行操作中使用的共享变量。 默认情况下,当Spark并行运行一个函数作为不同节点上的一组任务时,它会将函数中使用的每个变量的副本发送给每个任务。 有时,变量需要跨任务共享,或者在任务和驱动程序之间共享。 Spark支持两种类型的共享变量:广播变量,可用于缓存所有节点的内存中的值;累加器,它们是仅“添加”到的变量,例如计数器和总和。

本指南以Spark支持的每种语言显示了这些功能。 如果你启动Spark的交互式shell,最简单的方法就是 - 用于Scala shell的bin / spark-shell或用于Python的bin / pyspark。
以下使用java演示

导入依赖

    <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.12</artifactId>
            <version>3.1.1</version>
        </dependency>

初始化spark

Spark程序必须做的第一件事是创建一个SparkContext对象,它告诉Spark如何访问集群。 要创建SparkContext,首先需要构建一个包含有关应用程序信息的SparkConf对象。

每个JVM只能激活一个SparkContext。 在创建新的SparkContext之前,必须先stop()。

SparkConf conf = new SparkConf().setAppName("HelloWorld").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);

appName参数是应用程序在集群UI上显示的名称。 master是Spark,Mesos或YARN群集URL,或者是以本地模式运行的特殊“本地”字符串。 实际上,在群集上运行时,您不希望在程序中对master进行硬编码,而是使用spark-submit启动应用程序并在那里接收它。 但是,对于本地测试和单元测试,您可以传递“local”以在进程中运行Spark。

Resilient Distributed Datasets (RDDs)

Spark围绕弹性分布式数据集(RDD)的概念展开,RDD是一个可以并行操作的容错的容错集合。 创建RDD有两种方法:并行化驱动程序中的现有集合,或引用外部存储系统中的数据集,例如共享文件系统,HDFS,HBase或提供Hadoop InputFormat的任何数据源。

Parallelized Collections (并行集合)

通过在驱动程序(Scala Seq)中的现有集合上调用SparkContext的parallelize方法来创建并行化集合。 复制集合的元素以形成可以并行操作的分布式数据集。 例如,以下是如何创建包含数字1到5的并行化集合:

List<String> data = Arrays.asList(1, 2,3,4,5);
JavaRDD<String> distData = sc.parallelize(data);

并行集合的一个重要参数是将数据集切割为的分区数。 Spark将为群集的每个分区运行一个任务。 通常,您希望群集中的每个CPU有2-4个分区。 通常,Spark会尝试根据您的群集自动设置分区数。 但是,您也可以通过将其作为第二个参数传递给并行化来手动设置它(例如sc.parallelize(data,10))。 注意:代码中的某些位置使用术语切片(分区的同义词)来保持向后兼容性。

External Datasets(外部数据集)

Spark可以从Hadoop支持的任何存储源创建分布式数据集,包括本地文件系统,HDFS,Cassandra,HBase,Amazon S3等.Spark支持文本文件,SequenceFiles和任何其他Hadoop InputFormat。

可以使用SparkContext的textFile方法创建文本文件RDD。 此方法获取文件的URI(计算机上的本地路径,或hdfs://,s3a://等URI)并将其作为行集合读取。 这是一个示例调用:

JavaRDD<String> distFile= sc.textFile("c:/a.txt");

创建后,distFile可以由数据集操作执行。例如,我们可以使用map和reduce操作来添加所有行的大小,如下所示:distFile.map(s => s.length).reduce((a,b)=> a + b)。
其中map和reduce的概念和yarn的概念项目:

  1. 每一行作为字符串传入map函数s,返回的数据将传入reduce。
  2. reduce进行聚合,会将map返回的所有行处理过的数据依次进行聚合,比如第一个和第二个调用reduce,第三个和第四个调用reduce,将他们结果作为参数在调用reduce。

textFile方法还采用可选的第二个参数来控制文件的分区数。默认情况下,Spark为文件的每个块创建一个分区(HDFS中默认为128MB),但您也可以通过传递更大的值来请求更多的分区。请注意,您不能拥有比块少的分区。

除文本文件外,Spark的Scala API还支持其他几种数据格式:

  1. SparkContext.wholeTextFiles允许您读取包含多个小文本文件的目录,并将它们作为(文件名,内容)对返回。这与textFile形成对比,textFile将在每个文件中每行返回一条记录。分区由数据局部性决定,在某些情况下,可能导致分区太少。对于这些情况,wholeTextFiles提供了一个可选的第二个参数来控制最小数量的分区。

  2. 对于SequenceFiles,使用SparkContext的sequenceFile [K,V]方法,其中K和V是文件中键和值的类型。这些应该是Hadoop的Writable接口的子类,如IntWritable和Text。此外,Spark允许您为一些常见的Writable指定本机类型;例如,sequenceFile [Int,String]将自动读取IntWritables和Texts。

  3. 对于其他Hadoop InputFormats,您可以使用SparkContext.hadoopRDD方法,该方法采用任意JobConf和输入格式类,键类和值类。设置这些与使用输入源的Hadoop作业的方式相同。您还可以基于“新”MapReduce API(org.apache.hadoop.mapreduce)将SparkContext.newAPIHadoopRDD用于InputFormats。

  4. RDD.saveAsObjectFile和SparkContext.objectFile支持以包含序列化Java对象的简单格式保存RDD。虽然这不像Avro这样的专用格式有效,但它提供了一种保存任何RDD的简便方法。

RDD操作

RDD支持两种类型的操作:转换Transformations(从现有数据集创建新数据集)和操作Actions(在数据集上运行计算后将值返回到驱动程序)。例如,map是一个转换,它通过一个函数传递每个数据集元素,并返回一个表示结果的新RDD。另一方面,reduce是一个使用某个函数聚合RDD的所有元素的操作,并将最终结果返回给驱动程序(尽管还有一个返回分布式数据集的并行reduceByKey)。

Spark中的所有转换都是惰性的,因为它们不会立即计算结果。相反,他们只记得应用于某些基础数据集(例如文件)的转换。仅当操作需要将结果返回到驱动程序时才会计算转换。这种设计使Spark能够更有效地运行。例如,我们可以意识到通过map创建的数据集将用于reduce,并且仅将reduce的结果返回给驱动程序,而不是更大的映射数据集。

默认情况下,每次对其执行操作时,都可以重新计算每个转换后的RDD。但是,您也可以使用持久化(或缓存)方法在内存中保留RDD,在这种情况下,Spark会在群集上保留元素,以便在下次查询时更快地访问。还支持在磁盘上保留RDD或在多个节点上复制。

常用Transformations转换

下面的表格列了 Spark 支持的一些常用 transformations。详细内容请参阅 RDD API 文档(Scala, Java, Python) 和 PairRDDFunctions 文档(Scala, Java)。

TransformationMeaning
map(func )返回一个新的分布式数据集,将数据源的每一个元素传递给函数 func 映射组成。
filter(func )返回一个新的数据集,从数据源中选中一些元素通过函数 func 返回 true。
flatMap(func )类似于 map,但是每个输入项能被映射成多个输出项(所以 func 必须返回一个 Seq,而不是单个 item)。
mapPartitions(func )类似于 map,但是分别运行在 RDD 的每个分区上,所以 func 的类型必须是 Iterator => Iterator 当运行在类型为 T 的 RDD 上。
mapPartitionsWithIndex(func )类似于 mapPartitions,但是 func 需要提供一个 integer 值描述索引(index),所以 func 的类型必须是 (Int, Iterator) => Iterator 当运行在类型为 T 的 RDD 上。
sample(withReplacement, fraction, seed)对数据进行采样。
union(otherDataset)Return a new dataset that contains the union of the elements in the source dataset and the argument.
intersection(otherDataset)Return a new RDD that contains the intersection of elements in the source dataset and the argument.
distinct([numTasks]))Return a new dataset that contains the distinct elements of the source dataset.
groupByKey([numTasks])When called on a dataset of (K, V) pairs, returns a dataset of (K, Iterable) pairs. Note: If you are grouping in order to perform an aggregation (such as a sum or average) over each key, using reduceByKey or combineByKey will yield much better performance. Note: By default, the level of parallelism in the output depends on the number of partitions of the parent RDD. You can pass an optional numTasks argument to set a different number of tasks.
reduceByKey(func, [numTasks])When called on a dataset of (K, V) pairs, returns a dataset of (K, V) pairs where the values for each key are aggregated using the given reduce function func, which must be of type (V,V) => V. Like in groupByKey, the number of reduce tasks is configurable through an optional second argument.
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])When called on a dataset of (K, V) pairs, returns a dataset of (K, U) pairs where the values for each key are aggregated using the given combine functions and a neutral “zero” value. Allows an aggregated value type that is different than the input value type, while avoiding unnecessary allocations. Like in groupByKey, the number of reduce tasks is configurable through an optional second argument.
sortByKey([ascending], [numTasks])When called on a dataset of (K, V) pairs where K implements Ordered, returns a dataset of (K, V) pairs sorted by keys in ascending or descending order, as specified in the boolean ascending argument.
join(otherDataset, [numTasks])When called on datasets of type (K, V) and (K, W), returns a dataset of (K, (V, W)) pairs with all pairs of elements for each key. Outer joins are also supported through leftOuterJoin and rightOuterJoin.
cogroup(otherDataset, [numTasks])When called on datasets of type (K, V) and (K, W), returns a dataset of (K, Iterable, Iterable) tuples. This operation is also called groupWith.
cartesian(otherDataset)When called on datasets of types T and U, returns a dataset of (T, U) pairs (all pairs of elements).
pipe(command, [envVars])Pipe each partition of the RDD through a shell command, e.g. a Perl or bash script. RDD elements are written to the process’s stdin and lines output to its stdout are returned as an RDD of strings.
coalesce(numPartitions)Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset.
repartition(numPartitions)Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network.
常用Actions动作

下面的表格列了 Spark 支持的一些常用 actions。详细内容请参阅 RDD API 文档(Scala, Java, Python) 和 PairRDDFunctions 文档(Scala, Java)。

ActionMeaning
reduce(func)Aggregate the elements of the dataset using a function func (which takes two arguments and returns one). The function should be commutative and associative so that it can be computed correctly in parallel.
collect()Return all the elements of the dataset as an array at the driver program. This is usually useful after a filter or other operation that returns a sufficiently small subset of the data.
count()Return the number of elements in the dataset.
first()Return the first element of the dataset (similar to take(1)).
take(n)Return an array with the first n elements of the dataset. Note that this is currently not executed in parallel. Instead, the driver program computes all the elements.
takeSample(withReplacement, num, [seed])Return an array with a random sample of num elements of the dataset, with or without replacement, optionally pre-specifying a random number generator seed.
takeOrdered(n, [ordering])Return the first n elements of the RDD using either their natural order or a custom comparator.
saveAsTextFile(path)Write the elements of the dataset as a text file (or set of text files) in a given directory in the local filesystem, HDFS or any other Hadoop-supported file system. Spark will call toString on each element to convert it to a line of text in the file.
saveAsSequenceFile(path) (Java and Scala)Write the elements of the dataset as a Hadoop SequenceFile in a given path in the local filesystem, HDFS or any other Hadoop-supported file system. This is available on RDDs of key-value pairs that either implement Hadoop’s Writable interface. In Scala, it is also available on types that are implicitly convertible to Writable (Spark includes conversions for basic types like Int, Double, String, etc).
saveAsObjectFile(path) (Java and Scala)Write the elements of the dataset in a simple format using Java serialization, which can then be loaded using SparkContext.objectFile().
countByKey()Only available on RDDs of type (K, V). Returns a hashmap of (K, Int) pairs with the count of each key.
foreach(func)Run a function func on each element of the dataset. This is usually done for side effects such as updating an accumulator variable (see below) or interacting with external storage systems.

helloworld

package cn.test;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TestSpark {
    public static void main(String[] args) throws IOException {
        //初始化spark
        SparkConf conf = new SparkConf().setAppName("HelloWorld").setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);
        rddTestFile(sc);
        System.in.read();
    }
    /**
     * 统计文件词频数
     * @param sc
     */
    public static void rddTestFile(JavaSparkContext sc){
        JavaRDD<String> distData = sc.textFile("c:/a.txt");
        JavaPairRDD<String,Integer> map = distData.flatMapToPair(line->{
            String[] split=line.split(" ");
            List list=new ArrayList();
            for (String s : split) {
                list.add(new Tuple2<String,Integer>(s,1));
            }
            return list.iterator();
        });
        JavaPairRDD<String, Integer> stringIntegerJavaPairRDD = map.reduceByKey((x, y) -> x + y);
        stringIntegerJavaPairRDD.foreach(tuple->System.out.println(tuple._1+"-"+tuple._2));
    }
    /**
     * 统计字符串的个数
     * @param sc
     */
    public static void rddArray(JavaSparkContext sc){
        List<String> data = Arrays.asList("hello", "world","gggg");
        JavaRDD<String> distData = sc.parallelize(data);
        JavaRDD<Integer> map = distData.map(s -> s.length());
        Integer reduce = map.reduce((s1, s2) -> s1 + s2);
        System.out.println(reduce);
    }
}

集群模式:该模式被称为 Local[N] 模式,是用单机的多个线程来模拟 Spark 分布式计算,通常用来验证开发出来的应用程序逻辑上有没有问题其中N代表可以使用 N 个线程,每个线程拥有一个 core 。如果不指定 N,则默认是1个线程(该线程有1个 core )。如果是 local[*],则代表 Run Spark locally with as many worker threads as logical cores on your machine: 在本地运行Spark,与您的机器上的逻辑内核一样多的工作线程。

以上应用默认可直接本地运行,可能会抛出一个hadoop异常,但是不影响调试,如果不希望抛出该错误,可部署个window的hadoop,参考章节四。window调试mapreduce
设置HADOOP_HOME到对应hadoop根目录即可。
可查看地址:http://localhost:4040/jobs/ 查看任务执行相关信息

Spark Streaming流计算

Spark streaming是Spark核心API的一个扩展,它对实时流式数据的处理具有可扩展性、高吞吐量、可容错性等特点。我们可以从kafka、flume、Twitter、 ZeroMQ、Kinesis等源获取数据,也可以通过由
高阶函数map、reduce、join、window等组成的复杂算法计算出数据。最后,处理后的数据可以推送到文件系统、数据库、实时仪表盘中。事实上,你可以将处理后的数据应用到Spark的机器学习算法、
图处理算法中去。
在这里插入图片描述
在内部,它的工作原理如下图所示。Spark Streaming接收实时的输入数据流,然后将这些数据切分为批数据供Spark引擎处理,Spark引擎将数据生成最终的结果数据。
在这里插入图片描述
Spark Streaming支持一个高层的抽象,叫做离散流(discretized stream)或者DStream,它代表连续的数据流。DStream既可以利用从Kafka, Flume和Kinesis等源获取的输入数据流创建,也可以
在其他DStream的基础上通过高阶函数获得。在内部,DStream是由一系列RDDs组成。

本指南指导用户开始利用DStream编写Spark Streaming程序。用户能够利用scala、java或者Python来编写Spark Streaming程序。

注意:Spark 1.2已经为Spark Streaming引入了Python API。它的所有DStream transformations和几乎所有的输出操作可以在scala和java接口中使用。然而,它不紧支持基本的源如文本文件或者套接字上
的文本数据。同时支持flume、kafka等外部的源的API。

离散流(DStreams)

离散流或者DStreams是Spark Streaming提供的基本的抽象,它代表一个连续的数据流。它要么是从源中获取的输入流,要么是输入流通过转换算子生成的处理后的数据流。在内部,DStreams由一系列连续的
RDD组成。DStreams中的每个RDD都包含确定时间间隔内的数据,如下图所示:
在这里插入图片描述
任何对DStreams的操作都转换成了对DStreams隐含的RDD的操作。在前面的例子中,flatMap操作应用于lines这个DStreams的每个RDD,生成words这个DStreams的
RDD。过程如下图所示:
在这里插入图片描述
通过Spark引擎计算这些隐含RDD的转换算子。DStreams操作隐藏了大部分的细节,并且为了更便捷,为开发者提供了更高层的API。下面几节将具体讨论这些操作的细节。

输入DStreams和receivers

输入DStreams表示从数据源获取输入数据流的DStreams。在快速例子中,lines表示输入DStream,它代表从netcat服务器获取的数据流。每一个输入流DStream
和一个Receiver对象相关联,这个Receiver从源中获取数据,并将数据存入内存中用于处理。

输入DStreams表示从数据源获取的原始数据流。Spark Streaming拥有两类数据源

  • 基本源(Basic sources):这些源在StreamingContext API中直接可用。例如文件系统、套接字连接、Akka的actor等。
  • 高级源(Advanced sources):这些源包括Kafka,Flume,Kinesis,Twitter等等。它们需要通过额外的类来使用。我们在关联那一节讨论了类依赖。

具体使用参考官网

helloworld

使用nc启动一个端口,可持续不停写入文本数据,通过spark streaming每个5s获取数据并梳理。
安装nc,下载地址:https://eternallybored.org/misc/netcat/,下载完成后将文件路径加入path

nc -l -p 9999

添加依赖

        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-streaming_2.12</artifactId>
            <version>3.1.1</version>
        </dependency>

编写sparkstream项目

package cn.test;

import org.apache.spark.SparkConf;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import scala.Tuple2;

import java.util.Arrays;

public class TestSparkStream {
    public static void main(String[] args) throws InterruptedException {
        SparkConf conf = new SparkConf().setAppName("HelloWorld").setMaster("local[*]");
        JavaStreamingContext sc = new JavaStreamingContext(conf, Durations.seconds(5));
        JavaReceiverInputDStream<String> dstream = sc.socketTextStream("localhost", 9999);
        JavaDStream<String> wordsDstream=dstream.flatMap((line)->Arrays.asList(line.split(" ")).iterator());
        JavaPairDStream<String,Integer> javaPairDStream = wordsDstream.mapToPair(word -> new Tuple2(word, 1));
        JavaPairDStream<String, Integer> countsDstream = javaPairDStream.reduceByKey((x, y) -> x + y);
        countsDstream.print();
        sc.start();
        sc.awaitTermination();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值