3. Tutorials 之API Tutorials(DataStream API)

flink 1.8

DataStream API TutorialDataStream API教程

 

在本指南中,我们将从零开始,从设置Flink项目到在Flink集群上运行流应用程序。

Wikipedia提供了一个IRC通道,所有对wiki的编辑都记录在这个通道中。我们将在Flink中读取这个通道,并计算每个用户在给定时间内编辑的字节数。这很容易在几分钟内使用Flink实现,但它将为您提供一个良好的基础,您可以在此基础上开始自己构建更复杂的分析程序。

 

Setting up a Maven Project设置Maven项目

我们将使用Flink Maven Archetype来创建我们的项目结构。有关这方面的更多信息,请参见Java API Quickstart。出于我们的目的,运行命令是这样的:

$ mvn archetype:generate \
    -DarchetypeGroupId=org.apache.flink \
    -DarchetypeArtifactId=flink-quickstart-java \
    -DarchetypeVersion=1.8.0 \
    -DgroupId=wiki-edits \
    -DartifactId=wiki-edits \
    -Dversion=0.1 \
    -Dpackage=wikiedits \
    -DinteractiveMode=false

如果您愿意,可以编辑groupId、artifactId和package。使用上述参数,Maven将创建一个项目结构,如下图所示:

$ tree wiki-edits
wiki-edits/
├── pom.xml
└── src
    └── main
        ├── java
        │   └── wikiedits
        │       ├── BatchJob.java
        │       └── StreamingJob.java
        └── resources
            └── log4j.properties

 

这是我们的pom.xml文件,它已经在根目录中添加了Flink依赖项,以及src/main/java中的几个Flink程序示例。我们可以删除示例程序,因为我们要从头开始:

$ rm wiki-edits/src/main/java/wikiedits/*.java

作为最后一步,我们需要添加Flink Wikipedia连接器connector作为依赖项,以便在程序中使用它。编辑pom.xml的dependencies部分,使它看起来像这样:

<dependencies>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-java</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-java_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-wikiedits_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
</dependencies>

 

注意添加的flink-connector-wikiedits_2.11依赖项。(这个示例和Wikipedia连接器connector 的灵感来自Apache Samza的Hello Samza示例。)

Writing a Flink Program编写Flink程序

现在时候是编码时间。启动您喜欢的IDE并导入Maven项目或打开文本编辑器并创建文件src/main/java/wikiedits/WikipediaAnalysis.java:

package wikiedits;

public class WikipediaAnalysis {

    public static void main(String[] args) throws Exception {

    }
}

上面这个程序非常基础,但我们会边做边讲解。注意,这里我不会给出import语句,因为ide可以自动添加它们。在本节的最后,如果您只是想跳过前面的步骤并在编辑器中输入导入语句,那么我将使用import语句展示完整的代码。

Flink程序的第一步是创建一个StreamExecutionEnvironment(如果正在编写批处理作业,则为ExecutionEnvironment)。这可以用来设置执行参数并创建从外部系统读取数据源sources。我们把这个添加到main方法中:

StreamExecutionEnvironment see = StreamExecutionEnvironment.getExecutionEnvironment();

 接下来,我们将创建一个从Wikipedia IRC日志中读取的源代码:

DataStream<WikipediaEditEvent> edits = see.addSource(new WikipediaEditsSource());

这创建了一个WikipediaEditEvent元素的数据流,我们可以进一步处理它。对于本例的目的,我们感兴趣的是确定每个用户在特定时间窗口(比如5秒)中添加或删除的字节数。为此,我们首先必须指定要在用户名上对流进行key操作,也就是说此流上的算子操作应考虑用户名。在我们的例子中,窗口中每个用户编辑的字节总数都可以进行统计分析。对于key流,我们必须提供一个KeySelector,如下所示:

KeyedStream<WikipediaEditEvent, String> keyedEdits = edits
    .keyBy(new KeySelector<WikipediaEditEvent, String>() {
        @Override
        public String getKey(WikipediaEditEvent event) {
            return event.getUser();
        }
    });

 

这给了我们一个WikipediaEditEvent流,它有一个String key的用户名。现在,我们可以指定希望将窗口应用于此流,并基于这些窗口中的元素计算结果。窗口指定要在其上执行计算的流的一个片段。当计算无限元素流上的聚合时,需要使用Windows。在我们的例子中,我们想要每5秒聚合一次编辑的字节数:

这为我们提供了一个WikipediaEditEvent具有StringKeys的用户名。我们现在可以指定我们希望在此流上加上窗口,并根据这些窗口中的元素计算结果。当计算无限元素流上的聚合时,需要使用Windows。在我们的例子中,我们想要每5秒聚合一次编辑的字节数:

DataStream<Tuple2<String, Long>> result = keyedEdits
    .timeWindow(Time.seconds(5))
    .fold(new Tuple2<>("", 0L), new FoldFunction<WikipediaEditEvent, Tuple2<String, Long>>() {
        @Override
        public Tuple2<String, Long> fold(Tuple2<String, Long> acc, WikipediaEditEvent event) {
            acc.f0 = event.getUser();
            acc.f1 += event.getByteDiff();
            return acc;
        }
    });

第一步调用. timewindow()指定我们希望有5秒的滚动(非重叠)窗口。第二步调用是为唯一key值在每个窗口指定一个Fold transformation转换(窗口函数)。在我们的例子中,我们从一个初始值("",0L)开始,并将该时间窗口中每个编辑的字节差添加到其中。对于每个用户,生成的Stream流中包含一个Tuple2<String,Long>,并每5秒发出一次。

剩下要做的就是将流打印到控制台并开始执行:

result.print();

see.execute();

 

后一个调用是启动实际Flink作业所必需的。所有操作,例如创建 sources, transformations 和sinks都只构建内部DAG操作图。只有在调用execute()后,这个DAG操作图才会被抛到集群或在本地机器上执行。

完整的代码是这样的:

package wikiedits;

import org.apache.flink.api.common.functions.FoldFunction;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.wikiedits.WikipediaEditEvent;
import org.apache.flink.streaming.connectors.wikiedits.WikipediaEditsSource;

public class WikipediaAnalysis {

  public static void main(String[] args) throws Exception {

    StreamExecutionEnvironment see = StreamExecutionEnvironment.getExecutionEnvironment();

    DataStream<WikipediaEditEvent> edits = see.addSource(new WikipediaEditsSource());

    KeyedStream<WikipediaEditEvent, String> keyedEdits = edits
      .keyBy(new KeySelector<WikipediaEditEvent, String>() {
        @Override
        public String getKey(WikipediaEditEvent event) {
          return event.getUser();
        }
      });

    DataStream<Tuple2<String, Long>> result = keyedEdits
      .timeWindow(Time.seconds(5))
      .fold(new Tuple2<>("", 0L), new FoldFunction<WikipediaEditEvent, Tuple2<String, Long>>() {
        @Override
        public Tuple2<String, Long> fold(Tuple2<String, Long> acc, WikipediaEditEvent event) {
          acc.f0 = event.getUser();
          acc.f1 += event.getByteDiff();
          return acc;
        }
      });

    result.print();

    see.execute();
  }
}

你可以在IDE或命令行上运行这个例子,使用Maven:

$ mvn clean package
$ mvn exec:java -Dexec.mainClass=wikiedits.WikipediaAnalysis

 第一个命令构建我们的项目,第二个命令执行我们的主类。输出应该类似如下:

1> (Fenix down,114)
6> (AnomieBOT,155)
8> (BD2412bot,-3690)
7> (IgnorantArmies,49)
3> (Ckh3111,69)
5> (Slade360,0)
7> (Narutolovehinata5,2195)
6> (Vuyisa2001,79)
4> (Ms Sarah Welch,269)
4> (KasparBot,-245)

每行前面的数字告诉您这条输出是由哪个并行sinks实例打印生成的。

现在您开始编写自己的Flink程序。要了解更多信息,可以查看我们关于基本概念basic concepts和 DataStream API编程指南。如果您想学习如何在自己的机器上设置Flink集群并将结果写入Kafka请继续阅读额外的练习和资料。

 

Bonus Exercise: Running on a Cluster and Writing to Kafka奖金练习:在群集上运行并写入Kafka

请按照我们的本地安装教程 local setup tutorial在您的机器上设置Flink发行版,并参考Kafka quickstart来安装Kafka集群,然后再继续下面的学习。

第一步我们必须添加Flink Kafka connector 连接器作为依赖项,以便我们可以使用Kafka connector接收器。将其添加到依赖项部分的pom.xml文件中:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.11_2.11</artifactId>
    <version>${flink.version}</version>
</dependency>

接下来,我们需要修改我们的程序。我们将删除print() sink接收器,而使用Kafka connector 接收器。新代码如下:

result
    .map(new MapFunction<Tuple2<String,Long>, String>() {
        @Override
        public String map(Tuple2<String, Long> tuple) {
            return tuple.toString();
        }
    })
    .addSink(new FlinkKafkaProducer011<>("localhost:9092", "wiki-result", new SimpleStringSchema()));

相关类也需要导入:

import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer011;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.common.functions.MapFunction;

 注意,我们首先使用MapFunction将Tuple2<String, Long>的流转换为String流。我们这样做是因为普通字符串String将更容易写入到Kafka中。然后,我们创建一个Kafka sink。您可能需要根据您的设置调整主机名和端口。在运行程序之前需求创建一个kafka topic,它的名称为 “wiki-result”。使用Maven构建项目,因为我们需要打一个jar文件:

$ mvn clean package

 生成的jar文件将位于目标子文件夹中:target/wiki-edit -0.1.jar。我们稍后会用到它。

现在,我们准备启动一个Flink集群并运行在其上编写的Kafka程序。找到安装Flink的位置并启动本地集群:

 

$ cd my/flink/directory
$ bin/start-cluster.sh

我们还必须创建Kafka Topic,以便我们的程序可以写它:

$ cd my/kafka/directory
$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic wiki-results

 现在我们可以在本地Flink集群上运行jar文件了:

$ cd my/flink/directory
$ bin/flink run -c wikiedits.WikipediaAnalysis path/to/wikiedits-0.1.jar

如果一切按计划进行,该命令的输出应该类似如下:

 

03/08/2016 15:09:27 Job execution switched to status RUNNING.
03/08/2016 15:09:27 Source: Custom Source(1/1) switched to SCHEDULED
03/08/2016 15:09:27 Source: Custom Source(1/1) switched to DEPLOYING
03/08/2016 15:09:27 TriggerWindow(TumblingProcessingTimeWindows(5000), FoldingStateDescriptor{name=window-contents, defaultValue=(,0), serializer=null}, ProcessingTimeTrigger(), WindowedStream.fold(WindowedStream.java:207)) -> Map -> Sink: Unnamed(1/1) switched to SCHEDULED
03/08/2016 15:09:27 TriggerWindow(TumblingProcessingTimeWindows(5000), FoldingStateDescriptor{name=window-contents, defaultValue=(,0), serializer=null}, ProcessingTimeTrigger(), WindowedStream.fold(WindowedStream.java:207)) -> Map -> Sink: Unnamed(1/1) switched to DEPLOYING
03/08/2016 15:09:27 TriggerWindow(TumblingProcessingTimeWindows(5000), FoldingStateDescriptor{name=window-contents, defaultValue=(,0), serializer=null}, ProcessingTimeTrigger(), WindowedStream.fold(WindowedStream.java:207)) -> Map -> Sink: Unnamed(1/1) switched to RUNNING
03/08/2016 15:09:27 Source: Custom Source(1/1) switched to RUNNING

您可以看到各个操作算子是如何开始运行的。只有两个算子,因为出于性能原因,窗口后的操作被折叠成一个操作。在Flink中,我们称之为链接chaining。

您可以通过使用Kafka console consumer检查Kafka topic来观察程序的输出:

bin/kafka-console-consumer.sh  --zookeeper localhost:2181 --topic wiki-result groupid default

 您还可以查看Flink仪表板,它应该在http://localhost:8081处运行。您将看到集群资源和正在运行的job的概述:

如果你点击你正在运行的作业,你会看到一个视图,在那里你可以检查单独的操作,例如,看到处理过的元素的数量:

 

这就结束了我们的Flink之旅。如果您有任何问题,请随时在我们的邮件列表Mailing Lists中提出。

 

https://ci.apache.org/projects/flink/flink-docs-release-1.8/tutorials/datastream_api.html

https://flink.sojb.cn/tutorials/datastream_api.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值