「Flink」使用Java lambda表达式实现Flink WordCount
本篇我们将使用Java语言来实现Flink的单词统计。
代码开发
环境准备
导入Flink 1.9 pom依赖
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
</dependencies>
构建Flink流处理环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
自定义source
每秒生成一行文本
DataStreamSource<String> wordLineDS = env.addSource(new RichSourceFunction<String>() {
private boolean isCanal = false;
private String[] words = {
"important oracle jdk license update",
"the oracle jdk license has changed for releases starting april 16 2019",
"the new oracle technology network license agreement for oracle java se is substantially different from prior oracle jdk licenses the new license permits certain uses such as ",
"personal use and development use at no cost but other uses authorized under prior oracle jdk licenses may no longer be available please review the terms carefully before ",
"downloading and using this product an faq is available here ",
"commercial license and support is available with a low cost java se subscription",
"oracle also provides the latest openjdk release under the open source gpl license at jdk java net"
};
@Override
public void run(SourceContext<String> ctx) throws Exception {
// 每秒发送一行文本
while (!isCanal) {
int randomIndex = RandomUtils.nextInt(0, words.length);
ctx.collect(words[randomIndex]);
Thread.sleep(1000);
}
}
@Override
public void cancel() {
isCanal = true;
}
});
单词计算
// 3. 单词统计
// 3.1 将文本行切分成一个个的单词
SingleOutputStreamOperator<String> wordsDS = wordLineDS.flatMap((String line, Collector<String> ctx) -> {
// 切分单词
Arrays.stream(line.split(" ")).forEach(word -> {
ctx.collect(word);
});
}).returns(Types.STRING); //3.2 将单词转换为一个个的元组
SingleOutputStreamOperator<Tuple2<String, Integer>> tupleDS = wordsDS
.map(word -> Tuple2.of(word, 1))
.returns(Types.TUPLE(Types.STRING, Types.INT)); // 3.3 按照单词进行分组
KeyedStream<Tuple2<String, Integer>, String> keyedDS = tupleDS.keyBy(tuple -> tuple.f0); // 3.4 对每组单词数量进行累加
SingleOutputStreamOperator<Tuple2<String, Integer>> resultDS = keyedDS
.timeWindow(Time.seconds(3))
.reduce((t1, t2) -> Tuple2.of(t1.f0, t1.f1 + t2.f1)); resultDS.print();
参考代码
public class WordCount {
public static void main(String[] args) throws Exception {
// 1. 构建Flink流式初始化环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 2. 自定义source - 每秒发送一行文本
DataStreamSource<String> wordLineDS = env.addSource(new RichSourceFunction<String>() {
private boolean isCanal = false;
private String[] words = {
"important oracle jdk license update",
"the oracle jdk license has changed for releases starting april 16 2019",
"the new oracle technology network license agreement for oracle java se is substantially different from prior oracle jdk licenses the new license permits certain uses such as ",
"personal use and development use at no cost but other uses authorized under prior oracle jdk licenses may no longer be available please review the terms carefully before ",
"downloading and using this product an faq is available here ",
"commercial license and support is available with a low cost java se subscription",
"oracle also provides the latest openjdk release under the open source gpl license at jdk java net"
};
@Override
public void run(SourceContext<String> ctx) throws Exception {
// 每秒发送一行文本
while (!isCanal) {
int randomIndex = RandomUtils.nextInt(0, words.length);
ctx.collect(words[randomIndex]);
Thread.sleep(1000);
}
}
@Override
public void cancel() {
isCanal = true;
}
});
// 3. 单词统计
// 3.1 将文本行切分成一个个的单词
SingleOutputStreamOperator<String> wordsDS = wordLineDS.flatMap((String line, Collector<String> ctx) -> {
// 切分单词
Arrays.stream(line.split(" ")).forEach(word -> {
ctx.collect(word);
});
}).returns(Types.STRING);
//3.2 将单词转换为一个个的元组
SingleOutputStreamOperator<Tuple2<String, Integer>> tupleDS = wordsDS
.map(word -> Tuple2.of(word, 1))
.returns(Types.TUPLE(Types.STRING, Types.INT));
// 3.3 按照单词进行分组
KeyedStream<Tuple2<String, Integer>, String> keyedDS = tupleDS.keyBy(tuple -> tuple.f0);
// 3.4 对每组单词数量进行累加
SingleOutputStreamOperator<Tuple2<String, Integer>> resultDS = keyedDS
.timeWindow(Time.seconds(3))
.reduce((t1, t2) -> Tuple2.of(t1.f0, t1.f1 + t2.f1));
resultDS.print();
env.execute("app");
}
}
Flink对Java Lambda表达式支持情况
Flink支持Java API所有操作符使用Lambda表达式。但是,但Lambda表达式使用Java泛型时,就需要声明类型信息。
我们来看下上述的这段代码:
SingleOutputStreamOperator<String> wordsDS = wordLineDS.flatMap((String line, Collector<String> ctx) -> {
// 切分单词
Arrays.stream(line.split(" ")).forEach(word -> {
ctx.collect(word);
});
}).returns(Types.STRING);
之所以这里将所有的类型信息,因为Flink无法正确自动推断出来Collector中带的泛型。我们来看一下FlatMapFuntion的源代码
@Public
@FunctionalInterface
public interface FlatMapFunction<T, O> extends Function, Serializable {
/**
* The core method of the FlatMapFunction. Takes an element from the input data set and transforms
* it into zero, one, or more elements.
*
* @param value The input value.
* @param out The collector for returning result values.
*
* @throws Exception This method may throw exceptions. Throwing an exception will cause the operation
* to fail and may trigger recovery.
*/
void flatMap(T value, Collector<O> out) throws Exception;
}
我们发现 flatMap的第二个参数是Collector<O>,是一个带参数的泛型。Java编译器编译该代码时会进行参数类型擦除,所以Java编译器会变成成:
void flatMap(T value, Collector out)
这种情况,Flink将无法自动推断类型信息。如果我们没有显示地提供类型信息,将会出现以下错误:
org.apache.flink.api.common.functions.InvalidTypesException: The generic type parameters of 'Collector' are missing.
In many cases lambda methods don't provide enough information for automatic type extraction when Java generics are involved.
An easy workaround is to use an (anonymous) class instead that implements the 'org.apache.flink.api.common.functions.FlatMapFunction' interface.
Otherwise the type has to be specified explicitly using type information.
这种情况下,必须要显示指定类型信息,否则输出将返回值视为Object类型,这将导致Flink无法正确序列化。
所以,我们需要显示地指定Lambda表达式的参数类型信息,并通过returns方法显示指定输出的类型信息
我们再看一段代码:
SingleOutputStreamOperator<Tuple2<String, Integer>> tupleDS = wordsDS
.map(word -> Tuple2.of(word, 1))
.returns(Types.TUPLE(Types.STRING, Types.INT));
为什么map后面也需要指定类型呢?
因为此处map返回的是Tuple2类型,Tuple2是带有泛型参数,在编译的时候同样会被查出泛型参数信息,导致Flink无法正确推断。
更多关于对Java Lambda表达式的支持请参考官网:https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/java_lambdas.html
「Flink」使用Java lambda表达式实现Flink WordCount的更多相关文章
- Java Lambda表达式初探
Java Lambda表达式初探 前言 本文受启发于Trisha Gee在JavaOne 2016的主题演讲Refactoring to Java 8. Java 8已经发行两年多,但很多人仍然在使用 ...
- Java Lambda表达式入门
Java Lambda表达式入门 http://blog.csdn.net/renfufei/article/details/24600507 Java 8十个lambda表达式案例 http://w ...
- Java Lambda表达式入门[转]
原文链接: Start Using Java Lambda Expressions http://blog.csdn.net/renfufei/article/details/24600507 下载示 ...
- Java Lambda表达式教程与示例
Lambda表达式是Java 8中引入的一个新特性.一个lambda表达式是一个匿名函数,而且这个函数没有名称且不属于任何类.lambda表达式的概念最初是在LISP编程语言中引入的. Java La ...
- Java Lambda表达式forEach无法跳出循环的解决思路
Java Lambda表达式forEach无法跳出循环的解决思路 如果你使用过forEach方法来遍历集合,你会发现在lambda表达式中的return并不会终止循环,这是由于lambda的底层实现导 ...
- java lambda表达式学习笔记
lambda是函数式编程(FP,functional program),在java8中引入,而C#很早之前就有了.在java中lambda表达式是'->',在C#中是‘=>’. 杜甫说:射 ...
- 《Java基础知识》Java Lambda表达式
接触Lambda表达式的时候,第一感觉就是,这个是啥?我居然看不懂,于是开始寻找资料,必须弄懂它. 先来看一个案例: @FunctionalInterface public interface MyL ...
- Java lambda 表达式常用示例
实体类 package com.lkb.java_lambda.dto; import lombok.Data; /** * @program: java_lambda * @description: ...
- Java lambda 表达式详解(JDK 8 新特性)
什么是 lambda 表达式 lambda 表达式(拉姆达表达式)是 JAVA 8 中提供的一种新的特性,它使 Java 也能进行简单的"函数式编程". lambda 表达式的本质 ...
随机推荐
- Collections.synchronizedList 、CopyOnWriteArrayList、Vector介绍、源码浅析与性能对比
## ArrayList线程安全问题 众所周知,`ArrayList`不是线程安全的,在并发场景使用`ArrayList`可能会导致add内容为null,迭代时并发修改list内容抛`Concurre ...
- THU的告诫
究其原因,还是自己只会OI了.要成为一个合格的选手,是方方面面各个地方都要看的,同时也要自己去实现.快速实现.清晰实现.运行成功. 尤其是各种平时用的东西,不要只会技,还要研其道. 再次警醒,各位同我 ...
- JavaWeb高级编程(上)
好久没更新了,发一篇以前记录学习的笔记. 面向读者:已经具有丰富的Java语言和Java SE平台知识的软件开发者和软件工程师. 预掌握知识: Internet.TCP.HTTP协议 HTML(5) ...
- C语言博客作业8
本周作业头 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 作业地址 我在这个课程的目标是 熟练运用C语言来写代码 这个作业在那个具体方面帮助我实现目标 while语句的运用 参考文献 ...
- CTF--HTTP服务--SSI注入
开门见山 1. 扫描靶场ip,发现VM 192.168.31.160 2. 扫描主机服务信息和服务版本 3. 快速扫描靶场全部信息 4. 探测开放的http的敏感信息 5. 再用dirb扫描敏感页面 ...
- Shell常用脚本之用户操作
批量创建10个系统帐号oldboy01-oldboy10并设置密码,密码为随机8位字符串 #!/bin/bash ..} do useradd $user -] | ` | passwd --stdi ...
- File流与IO流 看这一篇就够了
主要内容 File类 递归 IO流 字节流 字符流 异常处理 Properties 缓冲流 转换流 序列化流 打印流 学习目标 [ ] 能够说出File对象的创建方式 [ ] 能够说出File类获取名 ...
- python学习Day05--字典
[主要内容] 1. dict 用大括号{} 括起来. 内部使用key:value的形式来保存数据 {'jay':'周杰伦', "jj":'林俊杰'} 注意:字典的key必须是可哈希 ...
- 安卓开发实战-记账本APP(五)
今天将昨天剩余的bug修复,并且完成图标的美化,设置长按删除,模仿支付宝实现金额的动态增加. ①将昨天的布局进行了修改:之前是fragment,改成FrameLayout布局,不再设置name,进而在 ...
- 安卓开发实战-记账本APP(六)
记账本APP开发---终结篇 昨天的动态数字录屏奉上:在抖音上拍了一个(ps:欢迎点赞) https://v.douyin.com/poEjmG/ 今天将图表的内容进行了制作,我用的是MPChart的 ...