llvm编译器实战教程第二版_Spark实战第二版(涵盖Spark3.0)第五章 部署构一个简单的应用程序...

关注公众号:登峰大数据,阅读Spark实战第二版(完整中文版),系统学习Spark3.0大数据框架!

如果您觉得作者翻译的内容有帮助,请分享给更多人。您的分享,是作者翻译的动力!

f32e16a9b32f41f9bc4f7a1e3c3cc08c.png

本章涵盖了

  • 构建一个不需要数据接入的简单应用程序

  • 使用Java lambdas语法和Spark

  • 构建使用或不使用lambdas语法的应用程序

  • 在本地模式、集群模式和交互方式下与Spark交互

  • 用Spark计算圆周率π的近似值

在前几章中,了解了Apache Spark是什么,以及如何构建简单的应用程序,理解了包括dataframe和惰性在内的关键概念。第5章和第6章是相关联的:您将在本章构建一个应用程序,并在第6章中部署它。

在本章中,将从零开始构建一个应用程序。在本书之前构建了应用程序,但它们总是需要在过程的最开始就接入数据。本章实验本身会通过Spark产生数据,避免接入数据的需要。在集群中接入数据比创建自生成的数据集要复杂一些。这个应用程序的目标是近似地求出圆周率π的值。

然后您将了解与Spark互动的三种方式:

  • 本地模式,通过前几章中的示例,您已经熟悉该模式

  • 集群模式

  • 交互模式

实验

本章中的例子可以在GitHub中获得:https://github .com/jgperrin/net.jgp.books.spark.ch05。

5.1 一个无数据接入的例子

在本节中,您将处理一个不需要接入数据的示例。正如你在这本书中看到的许多例子一样,接入是整个大数据处理的关键部分。但是,本章将介绍部署,包括集群上的部署,所以我不希望您因为过多地了解而分心。

在集群上工作意味着数据对所有节点都可用。要理解集群上的部署,您需要花大量时间关注数据分布。你将在第6章中学习更多关于数据分布的知识。

因此,为了简化对部署的理解,我跳过了数据接入,而将重点放在理解所有组件、数据流和实际部署上。Spark将生成一个大的随机数据集(自生成),可以用来计算圆周率π。

5.1.1 计算π

在这个小小的理论部分中,我解释了如何通过使用darts(投掷飞镖)来计算π,以及如何在Spark中实现这个过程。如果你讨厌数学,你会发现这部分并不是那么糟糕(如果你对数学过敏,你可以跳过这部分)。不过,我还是想把这一部分献给我的大儿子皮埃尔-尼古拉,他肯定比我更欣赏这种算法的美。

你还在阅读吗?好啊!在这个简短的部分中,您将了解如何通过投掷飞镖来获得π近似的表达式,然后在Spark中用代码实现。结果和实际代码在下一节中。

有很多方法可以计算出π(参见Wikipedia:https://en.wikipedia.org/wiki/Approximations_of_%CF%80)。最适合我们场景的一种方法称为圆的面积求和,如图5.1所示。

cf9b0feb433d7cba7bb76b06819a1402.png

图5.1用圆圈的面积投掷飞镖来近似地模拟环境

这个代码通过向一个圆圈“投掷飞镖”来估计π:当点(飞镖的冲击)随机散落在单位正方形内时,一些点落在单位圆内。随着点的增加,圆内积分的分数接近于π /4。

你将模拟投掷数百万个飞镖。将随机生成每次投掷的横坐标(x)和纵坐标(y)。根据这些坐标,使用勾股定理,可以计算出飞镖是圆内还是圆外。图5.2说明了这种方法。

b83b65465ca302c08a1a78bd12bebd63.png

图5.2 使用勾股定理,可以很容易地确定掷出的点是否在圈内。

根据图5.2,您可以看到两次投掷,t1和t2,可以看到第一次投掷,t1,在圆外。它的坐标是x1 = 0.75 y1 = 0.9。距离d1为t1到原点的距离,其坐标为(0,0),表示为:

第二次也可以做同样的练习,其中d2表示原点到t2的距离:

这意味着第二次投掷是在圆圈内。

Spark将创建数据、应用转换(transformation),然后应用action操作——这是Spark的一种经典操作方式。过程(如图5.3所示)如下:

  1. 打开一个Spark会话。

  2. Spark创建一个数据集,其中包含每次飞镖投掷的一行数据。投掷的次数越多,你的近似值就会越精确。

  3. 创建包含每次抛出结果的数据集。

  4. 对有助于计算圆的面积的投掷次数求和。

  5. 计算两个区域投掷的比率,并将其乘以4,这近似于π。

在总结此过程时,图5.3介绍了所使用的方法以及所涉及的组件。您在第2章中了解了其中一些组件。第6章还将进一步详细说明每个组件。

e8520c93aaafad64dfa08c3cd808ba1f.png

图5.3 模拟仿真的过程,演示了Spark组件(驱动程序driver和执行程序executor)以及方法。executor由worker控制。

足够多的数学知识——让我们看看Java代码,好吗?

5.1.2 求圆周率π的近似值的代码

在本节中,您将浏览这些代码,这些代码将在本章的各个示例中使用。您将首先以本地模式运行代码。然后将修改该版本的代码以使用Java lambda函数。

Java 8引入了lambda函数,它可以不属于类而存在,可以作为参数传递,并按需执行。您将了解lambda函数如何帮助您(或不帮助)编写转换代码。

让我们首先看一下应用程序的输出,如下面的清单所示。

//清单5.1 投掷飞镖来求π的近似值的结果About to throw 1000000 darts, ready? Stay away from the target!Session initialized in 1685 msInitial dataframe built in 5083 msThrowing darts done in 21 ms//你在21毫秒内投了100万次飞镖。100000 darts thrown so far//只有当你调用action时,投掷才会开始。200000 darts thrown so far...900000 darts thrown so far1000000 darts thrown so farAnalyzing result in 6337 ms Pi is roughly 3.143304

该应用程序告诉您,在21毫秒内投掷了100万次飞镖。然而,Spark只有在您要求它时才会抛出飞镖,因为您调用一个action来分析结果。这是来自Spark的懒惰特点,你在第4章学到的;记住那些需要action来提醒的讨厌的孩子们!

在这个实验里,你将把加工过程分成不同的批次。我将其称为slices,在运行实验之后,可以在不同位置处理不同的slices值,以便更好地理解Spark如何处理这个过程。

实验

实验100的代码在net.jgp.books.spark.ch05.lab100_pi_compute .PiComputeApp类中,也在下面的清单中。

package net.jgp.books.spark.ch05.lab100_pi_compute; import java.io.Serializable;import java.util.ArrayList;import java.util.List;import org.apache.spark.api.java.function.MapFunction;import org.apache.spark.api.java.function.ReduceFunction;import org.apache.spark.sql.Dataset;import org.apache.spark.sql.Encoders;import org.apache.spark.sql.Row;import org.apache.spark.sql.SparkSession;...     private void start( int slices ) {       int numberOfThrows = 100000 * slices ;//可以用slices(切片)的数量作为乘数;稍后在集群上运行它时,它将非常有用。      System. out .println( "About to throw " + numberOfThrows            + " darts, ready? Stay away from the target!" );        long t0 = System.currentTimeMillis();      SparkSession spark = SparkSession            .builder()            .appName( "Spark Pi" )            .master( "local[*]" )//使用系统上所有可用的线程            .getOrCreate();       long t1 = System.currentTimeMillis();       System. out .println( "Session initialized in " + ( t1 - t0 ) + " ms" );

到目前为止,代码是相当标准的:它使用导入的Spark相关类,创建一个会话。你会注意到调用currentTimeMillis()来测量时间的花费:

List<Integer> listOfThrows = new ArrayList<>( numberOfThrows );       for ( int i = 0; i < numberOfThrows ; i ++) {         listOfThrows .add( i );      }      Dataset<Row> incrementalDf = spark            .createDataset( l , Encoders.INT())            .toDF();       long t2 = System.currentTimeMillis();       System. out .println( "Initial dataframe built in " + ( t2 - t1 ) + " ms" );

在这个代码片段中,从一个整数列表创建一个数据集,并将其转换为一个dataframe。当从列表创建数据集时,需要提供关于数据类型的提示——因此使用encodes.INT()参数。

这个dataframe的目的仅仅是在一个名为map的操作中将处理分派到尽可能多的节点上。在传统的编程中,如果你想抛出一百万个darts,你需要在单个线程,单个节点上使用一个循环。这不能扩展。在高度分布式的环境中,您可以在所有节点上映射投掷飞镖的过程。图5.4比较了这些过程。

84e4971fbeab2f82f608e5f6a0de55a4.png

图5.4 比较在迭代过程中投掷100万次飞镖的过程与将它们映射到四个节点上的过程(但可能更多)

换句话说,每一行incrementalDf都被传递给一个DartMapper的实例,它位于集群的所有物理节点上:

Dataset<Integer> dartsDs = incrementalDf            .map( new DartMapper(), Encoders.INT());       long t3 = System.currentTimeMillis();       System. out .println( "Throwing darts done in " + ( t3 - t2 ) + " ms" );

您将在清单5.3中看到DartMapper()。

reduce操作带来的结果:圆圈中飞镖的数量。以类似的方式,reduce操作在你的应用程序中是透明的,并且只包含一行代码:

int dartsInCircle = dartsDs .reduce( new DartReducer());       long t4 = System.currentTimeMillis();      System. out .println( "Analyzing result in " + ( t4 - t3 ) + " ms" );        System. out .println( "Pi is roughly " + 4.0 * dartsInCircle / numberOfThrows );

您将在清单5.3中看到DartReducer()。

我可以将处理过程总结如下:

  1. 创建一个列表,它将用于映射数据。

  2. map数据(投掷飞镖)。

  3. reduce结果。

让我们看一下清单5.3中的map和reduce操作的代码。

更进一步

您可以通过将numberOfThrows类型从int改为long来练习这个实验。如果使用Eclipse之类的IDE,您将直接看到它对代码其余部分的影响。这个示例的另一个变化是:在调用master时尝试合并slices变量(或它的一部分),如.master(“local[]”),以查看它如何影响性能(当使用更多核时,这一点会更明显)。*

//清单5.3计算π的代码:map和reduce类的代码private final class DartMapper         implements MapFunction<Row, Integer> {       private static final long serialVersionUID = 38446L;       @Override       public Integer call(Row r ) throws Exception {         double x = Math.random() * 2 - 1;//你随意扔飞镖;x和y是坐标。         double y = Math.random() * 2 - 1;         counter ++;//简单的计数器,看看发生了什么;不要重置它。         if ( counter % 100000 == 0) {            System. out .println( "" + counter + " darts thrown so far" );        }         return ( x * x + y * y <= 1) ? 1 : 0;//如果它在圆中,则返回1;如果它不在圆中,则返回0(参见后面的平方根说明)      }    } //reduce将计算相加的结果;注意,类型是匹配的。泛型异常来自方法的签名。   private final class DartReducer implements ReduceFunction<Integer> {       private static final long serialVersionUID = 12859L;       @Override       public Integer call(Integer x , Integer y ) throws Exception {         return x + y ;//返回每次投掷结果的和      }     }

为什么不返回平方根呢?

看一下mapper中的call()方法的返回值。如果遵循勾股定理,就需要返回x和y的平方和的平方根。但是,由于这些值小于1,这并不重要:我们关心的是飞镖是否在圆中,我们不关心从原点到投掷点的确切距离。因此,我们可以省去昂贵的开根号操作。

(未完待续......)  欢迎关注公众号,及时获得最新翻译内容:

9eaa8f92b7acc5cef547300e58113176.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值