维基百科在 IRC 频道上记录 Wiki 被修改的日志,我们可以通过监听这个 IRC 频道,来实时监控给定时间窗口内的修改事件。Apache Flink 作为流计算引擎,非常适合处理流数据,并且,类似于 Hadoop MapReduce 等框架,Flink 提供了非常良好的抽象,使得业务逻辑代码编写非常简单。我们通过这个简单的例子来感受一下 Flink 的程序的编写。

通过 Flink Quickstart 构建 Maven 工程

Flink 提供了 flink-quickstart-javaflink-quickstart-scala 插件,允许使用 Maven 的开发者创建统一的项目模版,应用项目模板可以规避掉很多部署上的坑。

构建这次工程的命令如下

$ mvn archetype:generate \
    -DarchetypeGroupId=org.apache.flink \
    -DarchetypeArtifactId=flink-quickstart-java \
    -DarchetypeCatalog=https://repository.apache.org/content/repositories/snapshots/ \
    -DarchetypeVersion=1.6-SNAPSHOT \
    -DgroupId=wiki-edits \
    -DartifactId=wiki-edits \
    -Dversion=0.1 \
    -Dpackage=wikiedits \
    -DinteractiveMode=false

注意高版本的 Maven 不支持 -DarchetypeCatalog 参数,可以将第一行改为  mvn org.apache.maven.plugins:maven-archetype-plugin:2.4::generate \ 或者去掉 -DarchetypeCatalog 行,并将 .m2/settings.xml 修改如下,其中主要是在 //profiles/profile/repositories 下设置好搜索 archetype 的仓库地址

<settings xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <profiles>
    <profile>
      <id>acme</id>
      <repositories>
        <repository>
            <id>archetype</id>
            <name>Apache Development Snapshot Repository</name>
            <url>https://repository.apache.org/content/repositories/snapshots/</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>

  <activeProfiles>
    <activeProfile>acme</activeProfile>
  </activeProfiles>

</settings>

成功下载项目模板后,在当前目录下应当能看到 wiki-edit 目录。执行命令 rm wiki-edits/src/main/java/wikiedits/*.java 清除模板自带的 Java 文件。

为了监听维基百科的 IRC 频道,在 pom.xml 文件下添加如下依赖,分别是 Flink 的客户端和 WikiEdit 的连接器

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-wikiedits_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

编写 Flink 程序

接下来的代码编写工作假定你是在 IDE 下编写的,主要是为了避免啰嗦的 import 语句。包含 import 等模板代码的全部代码在末尾给出。

首先我们创建用于运行的主程序代码 src/main/java/wikiedits/WikipediaAnalysis.java

package wikiedits;

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

    }
}

流处理的 Flink 程序的第一步是创建流处理执行上下文 StreamExecutionEnvironment,它类似于其他框架内的 Configuration 类,用于配制 Flink 程序和运行时的各个参数,对应的语句如下

StreamExecutionEnvironment see = StreamExecutionEnvironment.getExecutionEnvironment();

下一步我们以维基百科 IRC 频道的日志作为数据源创建连接

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

这个语句创建了填充 WikipediaEditEventDataStream,拿到数据流之后我们就可以对它做进一步的操作了。

我们的目标是统计给定时间窗口内,比如说五秒内,用户对维基百科的修改字节数。因此我们对每个 WikipediaEditEvent 以用户名作为键来标记(keyed)。Flink 兼容 Java 1.6 版本,因此古老的版本中 Flink 提供 KeySelector 函数式接口来标记

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

当前版本的 Flink 主要支持的是 Java 8 版本,因此我们也可以用 Lambda 表达式来改写这段较为繁琐的代码

KeyedStream<WikipediaEditEvent, String> keyedEdits = edits
        .keyBy(WikipediaEditEvent::getUser);

这个语句定义了 keyedEdits 变量,它是一个概念上形如(String, WikipediaEditEvent) 的数据流,即以字符串(用户名)为键,WikipediaEditEvent 为值的数据的流。这一步骤类似于 MapReduce 的 Shuffle 过程,针对 keyedEdits 的处理将自动按照键分组,因此我们可以直接对数据进行 fold 操作以折叠聚合同一用户名的修改字节数

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;
        }
    });

在新版的 Flink 中,FoldFunction 因为无法支持部分聚合被废弃了,如果对程序有强迫症,我们可以采用类似于 MapReduce 的办法来改写上边的代码,各个方法调用的作用与它们的名字一致,其中,为了绕过类型擦除导致的问题使用了 returns 函数

DataStream<Tuple2<String, Long>> result = keyedEdits
        .map((event) -> new Tuple2<>(event.getUser(), Long.valueOf(event.getByteDiff())))
        .returns(new TypeHint<Tuple2<String, Long>>(){})
        .timeWindowAll(Time.seconds(5))
        .reduce((acc, a) -> new Tuple2<>(a.f0, acc.f1+a.f1));

经过处理后的数据流 result 中就包含了我们所需要的信息,具体地说是填充了 Tuple2<String, Long>,即(用户名,修改字节数)元组的流,我们可以使用 result.print() 来打印它。

程序至此主要处理逻辑就写完了,但是 Flink 还需要在 StreamExecutionEnvironment 类型的变量上调用 execute 方法以实际执行整个 Flink 程序,该方法执行时将整个 Flink 程序转化为任务图并提交到 Flink 集群中。

整个程序的代码,包括模板代码,如下所示

package wikiedits;

import org.apache.flink.api.common.typeinfo.TypeHint;
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;
import org.apache.flink.api.java.tuple.Tuple2;

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(WikipediaEditEvent::getUser);
        DataStream<Tuple2<String, Long>> result = keyedEdits
                .map((event) -> new Tuple2<>(event.getUser(), Long.valueOf(event.getByteDiff())))
                .returns(new TypeHint<Tuple2<String, Long>>(){})
                .timeWindowAll(Time.seconds(5))
                .reduce((acc, a) -> new Tuple2<>(a.f0, acc.f1+a.f1));
        result.print();
        see.execute();
    }
}

可以通过 IDE 运行程序,在控制台看到类似下面格式的输出,每一行前面的数字代表了这是由 print 的并行实例中的编号为几的实例运行的结果

1> (LilHelpa,1966)
2> (1.70.80.5,2066)
3> (Beyond My Ken,-6550)
4> (Aleksandr Grigoryev,725)
1> (6.77.155.31,1943)
2> (Serols,1639)
3> (ClueBot NG,1907)
4> (GSS,3155)

Apache Flink 流处理实例的更多相关文章

  1. Apache Flink流式处理

    花了四小时,看完Flink的内容,基本了解了原理. 挖个坑,待总结后填一下. 2019-06-02 01:22:57等欧冠决赛中,填坑. 一.概述 storm最大的特点是快,它的实时性非常好(毫秒级延 ...

  2. 官宣 | Apache Flink 1.12.0 正式发布,流批一体真正统一运行!

    官宣 | Apache Flink 1.12.0 正式发布,流批一体真正统一运行! 原创 Apache 博客 [Flink 中文社区](javascript:void(0) 翻译 | 付典 Revie ...

  3. Apache Flink 1.12.0 正式发布,DataSet API 将被弃用,真正的流批一体

    Apache Flink 1.12.0 正式发布 Apache Flink 社区很荣幸地宣布 Flink 1.12.0 版本正式发布!近 300 位贡献者参与了 Flink 1.12.0 的开发,提交 ...

  4. 《基于Apache Flink的流处理》读书笔记

    前段时间详细地阅读了 <Apache Flink的流处理> 这本书,作者是 Fabian Hueske&Vasiliki Kalavri,国内崔星灿翻译的,这本书非常详细.全面得介 ...

  5. Apache Flink中的广播状态实用指南

    感谢英文原文作者:https://data-artisans.com/blog/a-practical-guide-to-broadcast-state-in-apache-flink 不过,原文最近 ...

  6. Apache Flink:特性、概念、组件栈、架构及原理分析

     2016-04-30 22:24:39    Yanjun Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时(Flink Runtim ...

  7. Apache Flink 漫谈系列 - JOIN 算子

    聊什么 在<Apache Flink 漫谈系列 - SQL概览>中我们介绍了JOIN算子的语义和基本的使用方式,介绍过程中大家发现Apache Flink在语法语义上是遵循ANSI-SQL ...

  8. 深入理解Apache Flink

    Apache Flink(下简称Flink)项目是大数据处理领域最近冉冉升起的一颗新星,其不同于其他大数据项目的诸多特性吸引了越来越多人的关注.本文将深入分析Flink的一些关键技术与特性,希望能够帮 ...

  9. 深入理解Apache Flink核心技术

    深入理解Apache Flink核心技术 2016年02月18日 17:04:03 阅读数:1936 标签: Apache-Flink数据流程序员JVM   版权声明:本文为博主原创文章,未经博主允许 ...

随机推荐

  1. pig加载两个不同字段个数的文件?load file with different items(f1有42列,f2有43列读到一个对象中)

    我文章提到,加载一个文件的部分列是可行.两列,你只读一列,没问题. 但是,两个文件,f1和f2,f1有42列,f2有43列,同时加载到一个流对象,如何? 答:成功加载.但是无结构(schema unk ...

  2. MOAC中“MO:安全性配置文件“对于开发者

    1. 获取配置文件的值:应用开发员->配置文件->输入用户配置文件名,找到上面的名称,即可填入fnd_profile.VALUE()中. 2. MO:安全性配置文件有值的话,就代表启用了M ...

  3. python类:类方法和静态方法

    http://blog.csdn.net/pipisorry/article/details/49516185 面相对象程序设计中,类方法和静态方法是经常用到的两个术语.逻辑上讲:类方法是只能由类名调 ...

  4. SQL备份所有数据库脚本

    技巧要点:使用游标循环读取所有数据库名,然后定义存放路径,最后备份所有数据库到指定存在的本地文件夹中 脚本如下: declare @fileName varchar(255) --定义备份文件名变量d ...

  5. Touch Handling in Cocos2D 3.x(七)

    在touchMoved方法中寻找触摸在父节点(CCScene)中的位置并且移动CCDragSprite到其父节点的相应位置中去. 在我们在动作中观赏拖放机制之前,我们需要使用这个新实现的类.打开Mai ...

  6. jsp自动编译机制

    总的来说,Jasper的自动检测实现的机制比较简单,依靠某后台线程不断检测JSP文件与编译后的class文件的最后修改时间是否相同,若相同则认为没有改动,但倘若不同则需要重新编译.实际上由于在Tomc ...

  7. 敏捷测试(2)--ATDD概念

    什么是验收测试驱动开发 在准备实施一个功能或特性之前,首先团队需要定义出期望的质量标准和验收细则,以明确而且达成共识的验收测试计划(包含一系列测试场景)来驱动开发人员的TDD实践和测试人员的测试脚本开 ...

  8. vs2010 单文档MFC 通过加载位图文件作为客户区背景

    实现效果: 这个其实是一个非常常见的功能,大家都会考虑给自己简单的工程做一个背景界面.其实只要在view类中重载OnEraseBkgnd()这个函数就好了. 代码如下: BOOL CdddView:: ...

  9. linux下ruby使用tcl/tk编程环境设置

    正常情况下最新的ruby都是不带tcl/tk选项编译的,所以我们在运行tcl/tk代码时都会发生找不到tk库的错误.解决办法很简单只要以tcl/tk选项编译ruby即可. 这里以ubuntu 15.0 ...

  10. 新版MATERIAL DESIGN 官方动效指南(一)

    Google 刚发布了新版Material Design 官方动效指南,全文包括三个部分:为什么说动效很重要?如何制作优秀的Material Design动效及转场动画,动效的意义.新鲜热辣收好不谢! ...