维基百科在 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. 使用WakeLock使Android应用程序保持后台唤醒

     在使用一些产品列如微信.QQ之类的,如果有新消息来时,手机屏幕即使在锁屏状态下也会亮起并提示声音,这时用户就知道有新消息来临了.但是,一般情况下手机锁屏后,Android系统为了省电以及减少CP ...

  2. Coco2dx制作一个3D旋转的效果

    建了工程之后修改HelloWorldScene.cpp文件,修改部分为 // on "init" you need to initialize your instance bool ...

  3. Dynamics CRM2013 Server2012R2下IFD部署遇到There is already a listener on IP endpoint的解决方法

    接上一篇继续Server2012R2的问题,因为自己先在R2上部署的IFD报错后上网查了很多资料,但毕竟R2是新出的CRM2013也是新出的,网上基本还没有相关的问题反馈,基本都是2012以前的系统版 ...

  4. GCC内联函数:__builtin_types_compatible_p

    #if 0 - Built-in Function: int __builtin_types_compatible_p (type1, type2) You can use the built-in ...

  5. 使用Android Support Design 控件TabLayout 方便快捷实现选项卡功能

    1.概述 TabLayout是在2015年的google大会上,google发布了新的Android Support Design库的新组件之一,以此来全面支持Material Design 设计风格 ...

  6. C++ 传参时传内置类型时用传值(pass by value)方式效率较高

    来源:唐磊的个人博客<C++ 传参时传内置类型时用传值(pass by value)方式效率较高> 在<Effective C++>里提到对内置(C-like)类型在函数传参时 ...

  7. DB Query Analyzer 6.03, the most excellent Universal DB Access tools on any Microsoft Windows OS

      DB Query Analyzer 6.03, the most excellent Universal database Access tools on any Microsoft Wind ...

  8. 使用Material Design 应用主题

    自从去年Google推出Material Design这款设计语言后,新的设计规范越来越受开发者们的喜爱,这篇文章包括后续文章就是慢慢的介绍在App中使用Material Design界面主题.动画. ...

  9. myBatis源码之Configuration

    Configuration类主要是用来存储对mybatis的配置文件及mapper文件解析后的数据,Configuration对象会贯穿整个myabtis的执行流程,为mybatis的执行过程提供必要 ...

  10. HashMap是无序的

    一. 说明 HashMap是基于哈希表Map的实现.设计初衷主要是为了解决键值(key-value)对应关联的,HashMap的优势是可以很快的根据键(key)找到该键对应的值(value),但是我们 ...