我使用带有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
方法。为什么呢?
答案 0 :(得分:2)
tl; dr 在Hadoop中启用日志聚合,并使用yarn logs -applicationId
查看日志(两个默认Spark执行程序的日志中包含println
)。不要忘记使用sbin/stop-yarn.sh
后跟sbin/start-yarn.sh
(或简称sbin/stop-all.sh
和sbin/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
的输出。
对我有用的是使用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
分支中调用该方法,因此它是否可访问无关紧要。
如果问题是该方法无法访问,您会看到异常被抛出,例如ClassNotFoundException
或AbstractMethodError
; Scala不会决定忽略方法调用。
但鉴于您的代码风格,我猜测transformation
是var
。那么设置它的代码很可能不会在驱动程序上执行(执行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_date
和date_format
内置函数应该可以解决问题
首先,将数据读取到dataframe
val df = sqlContext.read
.format("com.databricks.spark.csv")
.option("header", true)
.load("path to the data file")
然后只需将to_date
和date_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|
+---+-----+----------+--------+
简单不是吗?