为什么RDD.map中的代码没有在YARN中执行?

时间:2018-03-27 04:24:11

标签: scala apache-spark

我使用带有5个节点的Hortonworks 2.6。我spark-submit到YARN(16GB RAM和4个核心)。

我有一个RDD转换在local但在yarn主网址中运行正常。

rdd1的值类似于:

id  name    date
1   john    10/05/2001 (dd/mm/yyyy)
2   steve   11/06/2015

我想将日期格式从dd/mm/yyyy更改为mm/dd/yy,因此我编写了一个方法transformations.transform,我在RDD.map函数中使用如下:

rdd2 = rdd1.map { rec => (rec.split(",")(0), transformations.transform(rec)) }

transformations.transform方法如下:

object transformations {
  def transform(t: String): String = {
    val msg = s">>> transformations.transform($t)"
    println(msg)     
    msg
  }
}

实际上上面的代码在本地工作正常,但在集群中没有。该方法只返回一个输出,好像map看起来如下:

rdd2 = rdd1.map { rec => (rec.split(",")(0), rec) } 

rec似乎没有传递给transformations.transform方法。

我确实使用动作来触发transformations.transform()方法,但没有运气。

val rdd3 = rdd2.count()
println(rdd3)

println打印计数但不调用transformations.transform方法。为什么呢?

3 个答案:

答案 0 :(得分:2)

tl; dr 在Hadoop中启用日志聚合,并使用yarn logs -applicationId查看日志(两个默认Spark执行程序的日志中包含println)。不要忘记使用sbin/stop-yarn.sh后跟sbin/start-yarn.sh(或简称sbin/stop-all.shsbin/start-all.sh)来退回YARN群集。

您在YARN的日志中看不到println输出的原因是,当一个Spark应用程序被spark-submit添加到YARN群集时,会启动三个YARN容器,即ApplicationMaster的一个容器和Spark执行器的两个容器。

RDD.map是一个始终在Spark执行器上运行的转换(作为每个RDD分区一个任务集)。这意味着println转到执行者的日志。

注意:在local模式下,单个JVM同时运行驱动程序和单个执行程序(作为线程)。

令我惊讶的是,对于Spark应用程序,您将无法在http://localhost:8088/cluster的ResourceManager Web UI中找到println的输出。

RM's web UI

对我有用的是使用yarn.log-aggregation-enable YARN属性启用日志聚合(您可以在文章Enable Log Aggregation中阅读):

// etc/hadoop/yarn-site.xml
<property>
  <name>yarn.log-aggregation-enable</name>
  <value>true</value>
</property>
<property>
  <name>yarn.nodemanager.log-aggregation.roll-monitoring-interval-seconds</name>
  <value>3600</value>
</property>

通过配置更改,您只需spark-submit --master yarn提交Spark应用程序,然后yarn logs -applicationId(我使用yarn logs -applicationId application_ID > output.txt并审核output.txt)。

你应该在那里找到>>> transformations.transform(1,john,10/05/2001)

守则

我使用的代码如下:

import org.apache.spark.SparkContext

object HelloRdd extends App {

  object transformations {
    def transform(t: String): String = {
      val msg = s">>> transformations.transform($t)"
      println(msg)
      msg
    }
  }

  val sc = SparkContext.getOrCreate()
  val rdd1 = sc.textFile(args(0))
  val rdd2 = rdd1.map { rec => (rec.split(",")(0), transformations.transform(rec)) }
  rdd2.count()
}

以下是我用于测试的spark-submit

$ HADOOP_CONF_DIR=/tmp ~/dev/apps/spark/bin/spark-submit \
  --master yarn \
  target/scala-2.11/spark-project_2.11-0.1.jar `pwd`/hello.txt

答案 1 :(得分:1)

您确实没有提供足够的信息,

  

是的,我在本地工作正常,它执行if循环但在群集中执行

相矛盾
  

在群集

中运行时,无法访问地图内的方法

如果它正在执行else分支,它没有任何理由在if分支中调用该方法,因此它是否可访问无关紧要。

如果问题是该方法无法访问,您会看到异常被抛出,例如ClassNotFoundExceptionAbstractMethodError; Scala不会决定忽略方法调用。

但鉴于您的代码风格,我猜测transformationvar。那么设置它的代码很可能不会在驱动程序上执行(执行if的地方)。在本地模式下无关紧要,但在集群模式下,它只在其执行的节点上设置transformation的副本。

这与https://spark.apache.org/docs/latest/rdd-programming-guide.html#local-vs-cluster-modes

中描述的问题相同
  

通常,闭包 - 类似循环或本地定义的方法的构造不应该用于改变某些全局状态。 Spark没有定义或保证从闭包外部引用的对象的突变行为。执行此操作的某些代码可能在本地模式下工作,但这只是偶然的,并且此类代码在分布式模式下不会按预期运行。

答案 2 :(得分:0)

  
    

为什么RDD.map中的代码没有用count执行?
    我想将日期格式从(dd / mm / yyyy)更改为(mm / dd / yy),因此在map()函数中使用名为transform inside transformations(object)的方法

  

如果您只想更改日期格式,那么我建议您不要经历这样的复杂性,因为很难分析问题的原因。我会建议您应用数据帧而不是rdds ,因为有许多内置函数可以满足您的需求。对于您的特定要求to_datedate_format 内置函数应该可以解决问题

首先,将数据读取到dataframe

val df = sqlContext.read
  .format("com.databricks.spark.csv")
  .option("header", true)
  .load("path to the data file")

然后只需将to_datedate_format函数应用为

import org.apache.spark.sql.functions._
df.withColumn("date2", date_format(to_date(col("date"), "dd/MM/yyyy"), "MM/dd/yy")).show(false)

你应该

+---+-----+----------+--------+
|id |name |date      |date2   |
+---+-----+----------+--------+
|1  |john |10/05/2001|05/10/01|
|2  |steve|11/06/2015|06/11/15|
+---+-----+----------+--------+

简单不是吗?