flink 1.8
Batch Examples
下面的示例程序展示了Flink的不同应用,从简单的单词计数到图形算法。代码示例演示了Flink’s DataSet API的使用。
下面和更多示例的完整源代码可以在Flink源码库的 flink-examples-batch模块中找到。
Running an example
为了运行一个Flink实例,我们假设您有一个可用的正在运行的Flink实例。导航中的“快速启动Quickstart”和“设置Setup”选项卡描述了启动Flink的各种方法。
最简单的方法是运行 ./bin/start-cluster.sh集群。默认情况下,它启动一个本地集群,其中包含一个JobManager和一个TaskManager。
Flink的每个二进制版本都包含一个示例目录,其中包含本页面上每个示例的jar文件。
要运行WordCount示例,执行以下命令:
./bin/flink run ./examples/batch/WordCount.jar
其他示例也可以以类似的方式启动。
注意,通过使用内置数据,许多示例运行时没有传递任何参数。要运行真实数据的WordCount,您必须传递到数据所在的路径:
./bin/flink run ./examples/batch/WordCount.jar --input /path/to/some/text/data --output /path/to/result
请注意,非本地文件系统需要模式前缀,例如hdfs://。
Word Count
WordCount是大数据处理系统的“Hello World”。它计算文本集合中单词出现的频次。该算法分为两个步骤:首先,将文本分割成单个单词。其次,对单词进行分组和计数。
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
DataSet<String> text = env.readTextFile("/path/to/file");
DataSet<Tuple2<String, Integer>> counts =
// split up the lines in pairs (2-tuples) containing: (word,1)
text.flatMap(new Tokenizer())
// group by the tuple field "0" and sum up tuple field "1"
.groupBy(0)
.sum(1);
counts.writeAsCsv(outputPath, "\n", " ");
// User-defined functions
public static class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
// normalize and split the line
String[] tokens = value.toLowerCase().split("\\W+");
// emit the pairs
for (String token : tokens) {
if (token.length() > 0) {
out.collect(new Tuple2<String, Integer>(token, 1));
}
}
}
}
WordCount example示例使用输入参数实现上述算法: --input <path> --output <path>。任何文本文件都可以作为测试数据。
Page Rank
PageRank算法在一个链接图结构中来计算页面链接的相关性和重要性,链接是从一个页面指向另一个页面。它是一个迭代图算法,这意味着它需要进行大量重复的计算过程。在每个迭代计算中,每个页面都将它自身等级rank权重赋值给所有相邻页面链接上,每个页面的新等级rank权重都是通过将相邻页面链接等级rank权重相加得到的。PageRank算法是由谷歌搜索引擎推广的,它利用网页的重要性对搜索查询结果进行排序。
在这个简单的例子中,PageRank是通过大量迭代bulk iteration和固定数量的迭代实现的。
ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
// read the pages and initial ranks by parsing a CSV file
DataSet<Tuple2<Long, Double>> pagesWithRanks = env.readCsvFile(pagesInputPath)
.types(Long.class, Double.class)
// the links are encoded as an adjacency list: (page-id, Array(neighbor-ids))
DataSet<Tuple2<Long, Long[]>> pageLinkLists = getLinksDataSet(env);
// set iterative data set
IterativeDataSet<Tuple2<Long, Double>> iteration = pagesWithRanks.iterate(maxIterations);
DataSet<Tuple2<Long, Double>> newRanks = iteration
// join pages with outgoing edges and distribute rank
.join(pageLinkLists).where(0).equalTo(0).flatMap(new JoinVertexWithEdgesMatch())
// collect and sum ranks
.groupBy(0).sum(1)
// apply dampening factor
.map(new Dampener(DAMPENING_FACTOR, numPages));
DataSet<Tuple2<Long, Double>> finalPageRanks = iteration.closeWith(
newRanks,
newRanks.join(iteration).where(0).equalTo(0)
// termination condition
.filter(new EpsilonFilter()));
finalPageRanks.writeAsCsv(outputPath, "\n", " ");
// User-defined functions
public static final class JoinVertexWithEdgesMatch
implements FlatJoinFunction<Tuple2<Long, Double>, Tuple2<Long, Long[]>,
Tuple2<Long, Double>> {
@Override
public void join(<Tuple2<Long, Double> page, Tuple2<Long, Long[]> adj,
Collector<Tuple2<Long, Double>> out) {
Long[] neighbors = adj.f1;
double rank = page.f1;
double rankToDistribute = rank / ((double) neigbors.length);
for (int i = 0; i < neighbors.length; i++) {
out.collect(new Tuple2<Long, Double>(neighbors[i], rankToDistribute));
}
}
}
public static final class Dampener implements MapFunction<Tuple2<Long,Double>, Tuple2<Long,Double>> {
private final double dampening, randomJump;
public Dampener(double dampening, double numVertices) {
this.dampening = dampening;
this.randomJump = (1 - dampening) / numVertices;
}
@Override
public Tuple2<Long, Double> map(Tuple2<Long, Double> value) {
value.f1 = (value.f1 * dampening) + randomJump;
return value;
}
}
public static final class EpsilonFilter
implements FilterFunction<Tuple2<Tuple2<Long, Double>, Tuple2<Long, Double>>> {
@Override
public boolean filter(Tuple2<Tuple2<Long, Double>, Tuple2<Long, Double>> value) {
return Math.abs(value.f0.f1 - value.f1.f1) > EPSILON;
}
}
PageRank program程序实现了上面的示例。它需要运行以下参数:--pages <path> --links <path> --output <path> --numPages <n> --iterations <n>。
输入文件为纯文本文件,格式必须如下:
- 页面表示为由换行字符分隔的(long)ID。
- 例如,“1\n2\n12\n42\n63\n”给出了包含IDs 1、2、12、42和63的5个页面。
- 链接表示为由空格字符分隔的页面id对。链接由换行符分隔:
- 例如“1 2\n2 12\n1 12\n42 63\n”给出四个(定向)链接(1)- >(2),(2)- >(12),(1)- >(12) 和(42)- > (63)。
对于这个简单的实现,要求每个页面至少有一个传入链接和一个传出链接(页面可以指向自身)。
Connected Components连通分量
连通分量算法通过在一个较大的图中为所有顶点分配相同的连通分量ID来识别连通的部分。与PageRank相似,连通分量是一个迭代算法。在每个步骤中,每个顶点都将其当前区域ID传播到其所有相邻顶点。如果一个顶点小于它自己的区域ID,则它接受来自相邻顶点的区域ID。
此实现使用增量迭代delta iteration:没有更改区域ID的顶点不参与下一步。这将具有更好的性能,因为后面的迭代通常只处理一些离群点。
// read vertex and edge data
DataSet<Long> vertices = getVertexDataSet(env);
DataSet<Tuple2<Long, Long>> edges = getEdgeDataSet(env).flatMap(new UndirectEdge());
// assign the initial component IDs (equal to the vertex ID)
DataSet<Tuple2<Long, Long>> verticesWithInitialId = vertices.map(new DuplicateValue<Long>());
// open a delta iteration
DeltaIteration<Tuple2<Long, Long>, Tuple2<Long, Long>> iteration =
verticesWithInitialId.iterateDelta(verticesWithInitialId, maxIterations, 0);
// apply the step logic:
DataSet<Tuple2<Long, Long>> changes = iteration.getWorkset()
// join with the edges
.join(edges).where(0).equalTo(0).with(new NeighborWithComponentIDJoin())
// select the minimum neighbor component ID
.groupBy(0).aggregate(Aggregations.MIN, 1)
// update if the component ID of the candidate is smaller
.join(iteration.getSolutionSet()).where(0).equalTo(0)
.flatMap(new ComponentIdFilter());
// close the delta iteration (delta and new workset are identical)
DataSet<Tuple2<Long, Long>> result = iteration.closeWith(changes, changes);
// emit result
result.writeAsCsv(outputPath, "\n", " ");
// User-defined functions
public static final class DuplicateValue<T> implements MapFunction<T, Tuple2<T, T>> {
@Override
public Tuple2<T, T> map(T vertex) {
return new Tuple2<T, T>(vertex, vertex);
}
}
public static final class UndirectEdge
implements FlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> {
Tuple2<Long, Long> invertedEdge = new Tuple2<Long, Long>();
@Override
public void flatMap(Tuple2<Long, Long> edge, Collector<Tuple2<Long, Long>> out) {
invertedEdge.f0 = edge.f1;
invertedEdge.f1 = edge.f0;
out.collect(edge);
out.collect(invertedEdge);
}
}
public static final class NeighborWithComponentIDJoin
implements JoinFunction<Tuple2<Long, Long>, Tuple2<Long, Long>, Tuple2<Long, Long>> {
@Override
public Tuple2<Long, Long> join(Tuple2<Long, Long> vertexWithComponent, Tuple2<Long, Long> edge) {
return new Tuple2<Long, Long>(edge.f1, vertexWithComponent.f1);
}
}
public static final class ComponentIdFilter
implements FlatMapFunction<Tuple2<Tuple2<Long, Long>, Tuple2<Long, Long>>,
Tuple2<Long, Long>> {
@Override
public void flatMap(Tuple2<Tuple2<Long, Long>, Tuple2<Long, Long>> value,
Collector<Tuple2<Long, Long>> out) {
if (value.f0.f1 < value.f1.f1) {
out.collect(value.f0);
}
}
}
ConnectedComponents程序实现了上面的示例。它需要运行以下参数:--vertices <path> --edges <path> --output <path> --iterations <n>。
输入文件为纯文本文件,格式必须如下:
- 顶点表示为id,并由换行符分隔。
- 例如“1\n2\n12\n42\n63\n”给出了五个顶点(1)、(2)、(12)、(42)和(63)。
- 边表示为顶点id的对,顶点id由空格字符分隔。边线由换行符分隔:
- 例如,"1 2\n2 12\n1 12\n42 63\n"给出四个(无向)链路(1) - (2),(2) - (12),(1) - (12)和(42) - (63)。
https://ci.apache.org/projects/flink/flink-docs-release-1.8/dev/batch/examples.html