我正在使用Cascading 2来创建Hadoop作业,并且我正在尝试创建一个以单一来源开头的流程。在对数据应用了几个函数之后,我需要拆分流程,以便使用此数据创建两个单独的报告(在两个单独的接收器中)。
//SOURCE
Scheme sourceScheme = new TextLine( new Fields( "line" ) );
Tap source = new Hfs( sourceScheme, input );
//REPORT1 SINK
Scheme report1SinkScheme = new TextDelimited( Fields.ALL, ",","\"" );
Tap report1Sink = new Hfs( report1SinkScheme, output1, SinkMode.REPLACE );
//REPORT2 SINK
Scheme report2SinkScheme = new TextDelimited( Fields.ALL, ",","\"" );
Tap report2Sink = new Hfs( report2SinkScheme, output2, SinkMode.REPLACE );
//INITIAL FUNCTIONS
Pipe firstPipe = new Pipe("firstPipe");
firstPipe = new Each(firstPipe, new Fields("line"), functionA);
firstPipe = new Each(firstPipe, functionB, Fields.ALL);
//REPORT1 FUNCTION
report1Pipe = new Each(firstPipe, Fields.ALL, function1, Fields.RESULTS);
//REPORT2 FUNCTION
report2Pipe = new Each(firstPipe, Fields.ALL, function2, Fields.RESULTS);
//CONNECT FLOW PARTS
FlowDef flowDef = new FlowDef()
.setName("report-flow")
.addSource(firstPipe, source)
.addSink(report1Pipe, report1Sink)
.addSink(report2Pipe, report2Sink);
new HadoopFlowConnector( properties ).connect( flowDef ).complete();
目前这给了我错误“java.lang.IllegalArgumentException:无法添加重复的sink:firstPipe”,但即使在搞乱它一段时间后,我还会遇到与流设置有关的各种其他问题。
有人可以解释如何构建这种形式的流(一个源,两个接收器)吗?我需要创建一个Cascade吗?或者在拆分之前是否需要一个中间接收器来保存数据?
请帮忙!
答案 0 :(得分:5)
您可以使用级联文档中提到的拆分模式。这是一个例子:
public static void main(String[] args) {
// source and sink
Scheme sourceScheme = new TextLine(new Fields("line"));
Tap source = new FileTap(sourceScheme, args[0]);
Fields sinkFields = new Fields("word", "count");
Scheme sinkScheme = new TextLine(sinkFields, sinkFields);
Tap sink_one = new FileTap(sinkScheme, "out-one.txt");
Tap sink_two = new FileTap(sinkScheme, "out-two.txt");
// the pipe assembly
Pipe assembly = new Pipe("wordcount");
String regex = "\\w+";
Function function = new RegexGenerator(new Fields("word"), regex);
assembly = new Each(assembly, new Fields("line"), function);
Aggregator count = new Count(new Fields("count"));
// ...split into two pipes
Pipe countOne = new Pipe("count-one", assembly);
countOne = new GroupBy(countOne, new Fields("word"));
countOne = new Every(countOne, count);
Pipe countTwo = new Pipe("count-two", assembly);
countTwo = new GroupBy(countTwo, new Fields("word"));
countTwo = new Every(countTwo, count);
// create the flow
final List<Pipe> pipes = new ArrayList<Pipe>(2);
pipes.add(countOne);
pipes.add(countTwo);
final Map<String, Tap> sinks = new HashMap<String, Tap>();
sinks.put("count-one", sink_one);
sinks.put("count-two", sink_two);
FlowConnector flowConnector = new LocalFlowConnector();
Flow flow = flowConnector.connect(source, sinks, pipes);
flow.complete();
}
答案 1 :(得分:4)
拆分模式位于级联用户指南中: http://docs.cascading.org/cascading/2.1/userguide/htmlsingle/#N21362
另一个(更简单)的例子包括在“不耐烦的级联”中,第5部分和第5部分。 6:
关于上面显示的代码的一点是它似乎缺少report1Pipe
和report2Pipe
的变量定义。要使用拆分模式,每个分支都需要一个名称,名称必须不同。
抛出异常是因为有两个分支都从管道程序集中的先前继承了相同的名称。因此,例如,那些flowDef.addSink(..)
调用对流程规划器来说是不明确的。
所以在“不耐烦”第5部分中,看看“D”,“DF”和“TF”分支如何在操作中命名。
Cascading要求这种命名似乎有点违反直觉,但是当您附加故障陷阱, debug 等等,特定分支机构。
或者,Clojure中的Cascalog DSL更具说明性,因此直接由语言处理 - 分支是子查询,陷阱等在子查询的闭包内处理。