【发布时间】:2019-11-20 17:09:55
【问题描述】:
问题如下: 我们有多种文件类型要使用自定义逻辑进行解析。假设我们有以下类型:CSV、JSON、BIN。为此,我使用 enumeratum 库创建了一个枚举
import enumeratum._
import scala.collection.immutable
/**
* Implementation of enumeratum for various input file types.
* See more at: https://github.com/lloydmeta/enumeratum
*/
sealed trait InputFileType extends EnumEntry
object InputFileType extends Enum[InputFileType] {
val values: immutable.IndexedSeq[InputFileType] = findValues
case object CSV extends InputFileType
case object JSON extends InputFileType
case object BIN extends InputFileType
}
对于每个InputFileType,都有一个对应的读者:
import org.apache.spark.input.PortableDataStream
class CSVReader(filename: String, file: String)
class JSONReader(filename: String, file: String)
class BINReader(filename: String, file: PortableDataStream)
由于它们应该共享一些共同的步骤并具有相似的结构,因此我想为它们创建一个超类,其中我们有一些每个读者都应该实现的步骤。但是file 输入可能会有所不同,所以我想为它制作一个 ADT。因此:
import org.apache.spark.input.PortableDataStream
sealed trait File
object File {
case class Text(file: String) extends File
case class BinaryStream(file: PortableDataStream) extends File
}
abstract class AbstractReader(filename: String, file: File) {
def doStep1: Seq[String]
def process: Result
}
class CSVReader(filename: String, file: String) extends AbstractReader(filename, Text(file)) {
override def doStep1: Seq[String] = ???
override def process: Result = ???
}
class JSONReader(filename: String, file: String) extends AbstractReader(filename, Text(file)) {
override def doStep1: Seq[String] = ???
override def process: Result = ???
}
class BINReader(filename: String, file: PortableDataStream) extends AbstractReader(filename, BinaryStream(file)) {
override def doStep1: Seq[String] = ???
override def process: Result = ???
}
//where
case class Data(col1: String, col2: Int)
type Result = List[Data]
现在我的想法是创建一个SparkReader 将它们读入RDD[Result]:
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession
class SparkReader(spark: SparkSession, fileType: String) extends Serializable {
def readTextFile(path: String): RDD[(String, String)] = spark.sparkContext.wholeTextFiles(path)
def readBinaryFile(path: String): RDD[(String, PortableDataStream)] = spark.sparkContext.binaryFiles(path)
}
object SparkReader {
def apply(spark: SparkSession, fileType: String): RDD[Result] = InputFileType.withNameInsensitive(fileType) match {
case InputFileType.CSV => new SparkReader(spark, fileType).readTextFile(path)
.map{case (filename: String, file: String) => new CSVReader(filename, file).process}
case InputFileType.JSON => new SparkReader(spark, fileType).readTextFile(path)
.map{case (filename: String, file: String) => new JSONReader(filename, file).process}
case InputFileType.BIN => new SparkReader(spark, fileType).readBinaryFile(path)
.map{case (filename: String, file: PortableDataStream) => new BINReader(filename, file).process}
}
}
但是我对这个解决方案并不满意,因为最终有很多代码重复,我想要一个能够隐式决定使用哪个阅读器的设计。
最后我想实例化一个类GenericReader:
class GenericReader(spark: SparkSession, fileType: String) extends Serializable {
def read(path: String): RDD[Result] = ???
}
然后当我调用new GenericReader(spark, "csv").read("myPath") 时,它能够得出它必须使用spark.sparkContext.wholeTextFiles 读取数据并应用CSVReader。或者当它是new GenericReader(spark, "bin").read("myPath") 时,使用spark.sparkContext.binaryFiles 创建RDD 并应用BINReader。
我也尝试应用隐式设计模式,但没有成功。 我怎样才能更优雅地解决这个问题?
【问题讨论】:
标签: scala apache-spark implicit