使用onComplete和andThen无法打印Scala Future的值

时间:2019-04-27 07:13:37

标签: scala apache-spark future

我正在尝试使用Scala-Spark从数据源读取增量数据。在访问源表之前,我试图计算在将来的代码中使用的分区列的最小值和最大值,该列存在于以下类中:GetSourceMeta

def getBounds(keyIdMap:scala.collection.mutable.Map[String, String]): Future[scala.collection.mutable.Map[String, String]] = Future {
    var boundsMap = scala.collection.mutable.Map[String, String]()
    keyIdMap.keys.foreach(table => if(!keyIdMap(table).contains("Invalid")) {
        val minMax    = s"select max(insert_tms) maxTms, min(insert_tms) minTms from schema.${table} where source='DB2' and key_id in (${keyIdMap(table)})"
        println("MinMax: " + minMax)
        val boundsDF  = spark.read.format("jdbc").option("url", con.getConUrl()).option("dbtable", s"(${minMax}) as ctids").option("user", con.getUserName()).option("password", con.getPwd()).load()
        try {
            val maxTms = boundsDF.select("minTms").head.getTimestamp(0).toString + "," + boundsDF.select("maxTms").head.getTimestamp(0).toString
            println("Bounds: " + maxTms)
            boundsMap += (table -> maxTms)
        } catch {
            case np: java.lang.NullPointerException =>  { println("No data found") }
            case e: Exception => { println(s"Unknown exception: $e") }
        }
    }
    )
    boundsMap.foreach(println)
    boundsMap
}

我在我的主要方法中将上述方法称为:

object LoadToCopyDB {
    val conf = new SparkConf().setAppName("TEST_YEAR").set("some parameters")
    def main(args: Array[String]): Unit = {
        val spark = SparkSession.builder().config(conf).master("yarn").enableHiveSupport().config("hive.exec.dynamic.partition", "true").config("hive.exec.dynamic.partition.mode", "nonstrict").getOrCreate()
        val gsm = new GetSourceMeta()
        val minMaxKeyMap = gsm.getBounds(keyIdMap).onComplete {
          case Success(values) => values.foreach(println)
          case Failure(f)      => f.printStackTrace
    }
.
.
.
}

好吧,onComplete没有打印任何值,所以我按如下方式使用andThen,这也没有帮助。

val bounds: Future[scala.collection.mutable.Map[String, String]] = gpMetaData.getBounds(incrementalIds) andThen {
  case Success(outval) => outval.foreach(println)
  case Failure(e)        => println(e)
}

主线程更早退出而没有让Future:getBounds执行。因此,我无法在终端上显示任何来自Future的println语句。我发现我需要保持主线程等待才能完成Future。但是当我在main和onComplete一起使用Await时:

Await.result(bounds, Duration.Inf)

编译器给出错误:

Type mismatch, expected: Awaitable[NotInferedT], actual:Unit

如果我将val minMaxKeyMap声明为Future[scala.collection.mutable.Map[String, String],则编译器会说:Expression of type Unit doesn't conform to expected type Future[mutable.map[String,String]]

我试图在Await语句后打印bounds的值,但这只是打印一个空的Map。

我不知道该如何解决。谁能让我知道我该怎么做才能使Future正常运行?

1 个答案:

答案 0 :(得分:0)

在这种情况下,始终最好遵循这些类型。方法 onComplete 仅返回Unit,它不会返回Future,因此无法使用Await传递。

例如,如果要返回任何类型的Future,则必须将值映射或平面映射并返回一个选项。在这种情况下,无论返回什么,都只希望Await方法等待此结果并打印跟踪。您可以在恢复中处理可能的异常。就像在您的代码中一样:

val minMaxKeyMap:Future[Option[Any] = gsm.getBounds(keyIdMap).map { values =>
   values.foreach(println)
   None
}.recover{
   case e: Throwable => 
          e. printStackTrace
          None
}

请注意,recover部分必须返回该类型的实例。 之后,您可以将“等待”应用于“将来”,然后将结果打印出来。这不是最漂亮的解决方案,但可以解决您的问题。