簡體   English   中英

同一執行程序中的Spark任務如何共享變量(帶有SimpleDateFormat的NumberFormatException)?

[英]How Spark tasks in the same executor share variables (NumberFormatException with SimpleDateFormat)?

火花文檔說

默認情況下,當Spark作為一組任務在不同節點上並行運行一個函數時,它會將函數中使用的每個變量的副本傳送給每個任務。

如果創建Java SimpleDateFormat並將其用於RDD操作中,則會出現NumberFormatException: multiple points異常。

我知道SimpleDateFormat不是線程安全的。 但是正如spark docs所說,此SimpleDateFormat對象已復制到每個任務,因此不應有多個線程訪問此對象。

我推測一個執行程序中的所有任務共享同一個SimpleDateFormate對象,對嗎?


該程序打印相同的對象java.text.SimpleDateFormat@f82ede60

object NormalVariable {

  // create dateFormat here doesn't change
  // val dateFormat = new SimpleDateFormat("yyyy.MM.dd")

  def main(args: Array[String]) {

    val dateFormat = new SimpleDateFormat("yyyy.MM.dd")

    val conf = new SparkConf().setAppName("Spark Test").setMaster("local[*]")
    val spark = new SparkContext(conf)

    val dates = Array[String]("1999.09.09", "2000.09.09", "2001.09.09", "2002.09.09", "2003.09.09")

    println(dateFormat)

    val resultes = spark.parallelize(dates).map { i =>
      println(dateFormat)
      dateFormat.parse(i)
    }.collect()

    println(resultes.mkString(" "))
    spark.stop()
  }
}

如您所知, SimpleDateFormat不是線程安全的。

如果Spark在每個執行器上使用單個核心( --executor-cores 1 ),那么一切都應該正常工作。 但是,一旦您為每個執行程序配置了多個內核,您的代碼就會立即運行多線程, SimpleDateFormat會同時由多個Spark任務共享,並且很可能破壞數據並引發各種異常。

要解決此問題,可以使用與非Spark代碼相同的方法之一,即ThreadLocal ,以確保每個線程獲得一個SimpleDateFormat副本。

在Java中,這看起來像:

public class DateFormatTest {

  private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyyMMdd");
    }
  };

  public Date convert(String source) throws ParseException{
    Date d = df.get().parse(source);
    return d;
  }
}

並且Scala中的等效代碼工作原理相同-在此處顯示為spark-shell會話:

import java.text.SimpleDateFormat

object SafeFormat extends ThreadLocal[SimpleDateFormat] {
  override def initialValue = {
    new SimpleDateFormat("yyyyMMdd HHmmss")
  }
}

sc.parallelize(Seq("20180319 162058")).map(SafeFormat.get.parse(_)).collect

    res6: Array[java.util.Date] = Array(Mon Mar 19 16:20:58 GMT 2018)

因此,您將在作業classobject的頂層定義ThreadLocal ,然后調用df.get在RDD操作中獲取SimpleDateFormat

看到:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM