Scalding TypedPipe API外部操作模式

时间:2016-01-08 21:36:38

标签: scala hadoop design-patterns cascading scalding

我有一份由Antonios Chalkiopoulos编写的MapReduce with Scalding的副本。在书中,他讨论了Scalding代码的外部操作设计模式。你可以在他的网站here上看到一个例子。我已经选择使用Type Safe API。当然,这引入了新的挑战,但我更喜欢它而不是Fields API,这是我之前提到的书和网站中大量讨论的内容。

我想知道人们如何使用Type Safe API实现外部操作模式。我的初步实施如下:

  

我创建了一个扩展com.twitter.scalding.Job的类   作为我的Scalding工作班,我将管理论点,定义   点击,并使用外部操作来构建数据处理   管道'

     

我创建了一个对象,我在其中定义要在Type中使用的函数   安全管道。因为Type Safe管道将一个函数作为参数,   然后我可以将对象中的函数作为参数传递给   管道

这会创建如下所示的代码:

class MyJob(args: Args) extends Job(args) {

  import MyOperations._

  val input_path = args(MyJob.inputArgPath)
  val output_path = args(MyJob.outputArgPath)

  val eventInput: TypedPipe[(LongWritable, Text)] = this.mode match {
    case m: HadoopMode => TypedPipe.from(WritableSequenceFile[LongWritable, Text](input_path))
    case _ => TypedPipe.from(WritableSequenceFile[LongWritable, Text](input_path))
  }

  val eventOutput: FixedPathSource with TypedSink[(LongWritable, Text)] with TypedSource[(LongWritable, Text)] = this.mode match {
    case m: HadoopMode => WritableSequenceFile[LongWritable, Text](output_path)
    case _ => TypedTsv[(LongWritable, Text)](output_path)
  }

  val validatedEvents: TypedPipe[(LongWritable, Either[Text, Event])] = eventInput.map(convertTextToEither).fork
  validatedEvents.filter(isEvent).map(removeEitherWrapper).write(eventOutput)
}

object MyOperations {

  def convertTextToEither(v: (LongWritable, Text)): (LongWritable, Either[Text, Event]) = {
    ...
  }

  def isEvent(v: (LongWritable, Either[Text, Event])): Boolean = {
    ...
  }

  def removeEitherWrapper(v: (LongWritable, Either[Text, Event])): (LongWritable, Text) = {
    ...
  }
}

如您所见,传递给Scalding Type Safe操作的函数与作业本身保持独立。虽然这不像“干净”。作为外部操作模式,这是编写此类代码的快速方法。另外,我可以使用JUnitRunner进行工作级集成测试,使用ScalaTest进行功能级单元测试。

这篇文章的要点是问人们如何做这种事情?互联网上用于Scalding Type Safe API的文档很少。是否有更多功能Scala友好的方式来做到这一点?我在这里错过了设计模式的关键组件吗?我对此感到紧张,因为使用Fields API,您可以使用ScaldingTest在管道上编写单元测试。据我所知,你不能用TypedPipes做到这一点。如果Scalding Type Safe API存在普遍认可的模式,或者您如何创建可重用,模块化和可测试的Type Safe API代码,请告知我们。谢谢你的帮助!

在Antonios'之后更新2回复

感谢您的回复。这基本上就是我要找的答案。我想继续谈话。我在回答的过程中看到的主要问题是,这个实现需要特定的类型实现,但如果类型在整个作业中发生变化会怎么样?我已经探索过这段代码,它似乎有效,但它似乎被黑了。

def self: TypedPipe[Any]

def testingPipe: TypedPipe[(LongWritable, Text)] = self.map(
    (firstVar: Any) => {
        val tester = firstVar.asInstanceOf[(LongWritable, Text)]
        (tester._1, tester._2)
    }
)

这方面的好处是我声明了一个自我的实现,但缺点是这种丑陋的类型转换。另外,我没有用更复杂的管道深入测试这个。所以基本上,你对如何处理类型的想法是什么,只有一个自我实现清洁/简洁?

2 个答案:

答案 0 :(得分:2)

Scala extension methods是使用隐式类实现的。 您向编译器添加了将TypedPipe转换为包含外部操作的(包装器)类的功能:

import com.twitter.scalding.TypedPipe
import com.twitter.scalding._
import cascading.flow.FlowDef

class MyJob(args: Args) extends Job(args) {

  implicit class MyOperationsWrapper(val self: TypedPipe[Double]) extends MyOperations with Serializable

  val pipe = TypedPipe.from(TypedTsv[Double](args("input")))

  val result = pipe
    .operation1
    .operation2(x => x*2)
    .write(TypedTsv[Double](args("output")))

}

trait MyOperations {

  def self: TypedPipe[Double]

  def operation1(implicit fd: FlowDef): TypedPipe[Double] =
    self.map { x =>
      println(s"Input: $x")
      x / 100
    }

  def operation2(datafn:Double => Double)(implicit fd: FlowDef): TypedPipe[Double] =
    self.map { x=>
      val result = datafn(x)
      println(s"Result: $result")
      result
    }

}

import org.apache.hadoop.util.ToolRunner
import org.apache.hadoop.conf.Configuration

object MyRunner extends App {

  ToolRunner.run(new Configuration(), new Tool, (classOf[MyJob].getName :: "--local" ::
    "--input" :: "doubles.tsv" ::
    "--output":: "result.tsv" :: args.toList).toArray)

}

关于如何跨管道管理类型,我的建议是尝试找出一些有意义的基本类型和用例类。要使用您的示例,我会将方法convertTextToEither重命名为extractEvents

case class LogInput(l : Long, text: Text)
case class Event(data: String)
def extractEvents( line : LogInput ): TypedPipe[Event] =
  self.filter( isEvent(line) )
      .map ( getEvent(line.text) ) 

然后你会

  • LogInputOperations代表LogInput个类型
  • EventOperations代表Event个类型

答案 1 :(得分:1)

我不确定您在显示的代码段中看到的问题是什么,以及为什么您认为它“不太干净”。它看起来很好。

对于使用类型化API问题的单元测试工作,请查看JobTest,它似乎正是您正在寻找的。