介绍
Apache Spark有许多内建方法进行数据处理,但是在实际应用中,可能需要domain specific operators来解决实际问题,我们需要扩展Spark API来添加自定义方法。
扩展Spark API有两种方法:
- 在存在的RDD上添加自定义方法
- 创建新的RDD
动力
我们有一份销售数据,以CSV格式进行存储,包含transactionid, customerid, itemid, itemvalue列。
class SalesRecord(val transactionId: String,
val customerId: String,
val itemId: String,
val itemValue: Double) extends Comparable[SalesRecord]
with Serializable {
override def compareTo(o: SalesRecord): Int = {
return this.transactionId.compareTo(o.transactionId)
}
override def toString: String = {
transactionId + "," + customerId + "," + itemId + "," + itemValue
}
}
转换数据为RDD[SalesRecord]
object Main {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("fengfengzai").setMaster("local")
val sc = new SparkContext(conf)
val dataRDD = sc.textFile("/to/path/sample.csv")
val salesRecordRDD = dataRDD.map(row => {
val colValues = row.split(",")
new SalesRecord(colValues(0), colValues(1), colValues(2), colValues(3).toDouble)
})
print(salesRecordRDD.map(_.itemValue).sum())
}
}
输出为:
100
尽管 salesRecordRDD.map(_.itemValue).sum() 是简洁的,但是不具有可读性,使用下面的方法进行求和会更好,这个方法需要我们自己实现。
salesRecordRDD.totalSales
添加自定义方法到RDD
- 定义帮助类(拥有自定方法)
class CustomFunctions(rdd: RDD[SalesRecord]) { def totalSales = rdd.map(_.itemValue).sum() }
- 隐式转换(添加方法到RDD上)
object CustomFunctions { implicit def addCustomFunctions(rdd: RDD[SalesRecord]) = new CustomFunctions(rdd) }
- 使用自定义方法
import CustomFunctions._ println(salesRecordRDD.totalSales)
创建自定义RDD
- 创建DiscountRDD
例如,想要懒计算每个销售的折扣,可以建立一个DiscountRDD。
当创建RDD时,必须覆盖compute, getPartitions方法。class DiscountRDD(prev: RDD[SalesRecord], discountPercentage: Double) extends RDD[SalesRecord](prev){ override def compute(split: Partition, context: TaskContext): Iterator[SalesRecord] = { firstParent[SalesRecord].iterator(split, context).map(salesRecord => { val discount = salesRecord.itemValue * discountPercentage new SalesRecord(salesRecord.transactionId, salesRecord.customerId, salesRecord.itemId, discount) }) } override protected def getPartitions: Array[Partition] = { firstParent[SalesRecord].partitions } }
- 添加自定义操作
使用上面类似方法,添加自定义方法来创建DiscountRDD。def discount(discountPercentage: Double) = new DiscountRDD(rdd, discountPercentage)
- 使用隐式转换进行操作
val discountRDD = salesRecordRDD.discount(0.1) println(discountRDD.collect().toList)