Scala 烫伤型管道API外部操作模式
我有一份由Antonios Chalkiopoulos编写的MapReduce与滚烫编程的副本。在这本书中,他讨论了滚烫代码的外部操作设计模式。你可以在他的网站上看到一个例子。我已经选择了使用。当然,这带来了新的挑战,但我更喜欢它而不是Fields API,这是我之前提到的书和网站中大量讨论的内容 我想知道人们是如何用类型安全API实现外部操作模式的。我的初步实施如下: 我创建了一个扩展com.twitter.spothing.Job的类,它将 作为我的工作班,我将“管理争论,定义 点击,并使用外部操作构建数据处理 管道' 我创建一个对象,在其中定义要在类型中使用的函数 安全管道。因为类型安全管道将参数作为函数, 然后我可以将对象中的函数作为参数传递给 管道 这将创建如下所示的代码:Scala 烫伤型管道API外部操作模式,scala,hadoop,design-patterns,cascading,scalding,Scala,Hadoop,Design Patterns,Cascading,Scalding,我有一份由Antonios Chalkiopoulos编写的MapReduce与滚烫编程的副本。在这本书中,他讨论了滚烫代码的外部操作设计模式。你可以在他的网站上看到一个例子。我已经选择了使用。当然,这带来了新的挑战,但我更喜欢它而不是Fields API,这是我之前提到的书和网站中大量讨论的内容 我想知道人们是如何用类型安全API实现外部操作模式的。我的初步实施如下: 我创建了一个扩展com.twitter.spothing.Job的类,它将 作为我的工作班,我将“管理争论,定义 点击,并使用
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) = {
...
}
}
如您所见,传递给烫伤类型安全操作的函数与作业本身是分开的。虽然这并不像外部操作模式那样“干净”,但这是编写此类代码的一种快速方法。此外,我可以使用JUnitRunner进行作业级集成测试,使用ScalateTest进行功能级单元测试
这篇文章的重点是问人们是如何做这类事情的?互联网上关于滚烫类型安全API的文档很少。有没有更适合Scala功能的方法?我是否缺少设计模式的一个关键组件?我对此有点紧张,因为通过Fields API,您可以使用BushingTest在管道上编写单元测试。据我所知,你不能用TypedPipes做那件事。请让我知道,是否有一个普遍同意的模式,为滚烫的类型安全API,或者您如何创建可重用的,模块化的,可测试的类型安全API代码。谢谢你的帮助
安东尼奥斯回复后更新2
谢谢你的回复。这基本上就是我想要的答案。我想继续谈话。正如我所评论的,我在您的回答中看到的主要问题是,此实现需要一个特定类型的实现,但是如果在整个工作过程中类型发生了变化,该怎么办?我已经探索了这段代码,它似乎可以工作,但它似乎被黑客入侵了
def self: TypedPipe[Any]
def testingPipe: TypedPipe[(LongWritable, Text)] = self.map(
(firstVar: Any) => {
val tester = firstVar.asInstanceOf[(LongWritable, Text)]
(tester._1, tester._2)
}
)
这样做的好处是我声明了self的一个实现,但缺点是这种丑陋的类型转换。此外,我还没有用更复杂的管道对此进行深入测试。因此,基本上,您对如何在类型更改时仅使用一个自我实现来处理类型以保持整洁/简洁有何想法?我不确定您看到的代码片段存在什么问题,以及为什么您认为它“不够整洁”。我觉得很好 至于使用类型化API的单元测试作业,请看一看,它似乎正是您所寻找的 Scala是使用隐式类实现的。 您向编译器添加了将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)
}
关于如何跨管道管理类型,我的建议是尝试找出一些有意义的基本类型和用例类。为了使用您的示例,我将方法convertexttoeither
重命名为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) )
那你会的
用于loginputo操作
类型LogInput
用于事件操作
类型事件