「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 表达式的本质 ...
随机推荐
- .netcore 3.1高性能微服务架构:webapi规范
1.1 定义 1.基础接口:单一职责原则,每个接口只负责各自的业务,下接db,通用性强. 2.聚合接口:根据调用方需求聚合基础接口数据,业务性强. 1.2 协议 1. 客户端在通过 API 与后端服务 ...
- 提供程序模式 提供 coding 一点点
放个图先,预则立码
- 个人作业四——Alpha测试
个人作业四--Alpha测试 这个作业属于哪个课程 软件工程 这个作业要求在哪里 作业要求 团队名称 GP工作室 这个作业的目标 对其他小组的项目进行测试 测试人员 许佳文 学号 2017310242 ...
- 使用postman测试接口
1.什么是接口测试 其实接口测试就和普通功能测试没什么区别,区别就是功能测试是在页面上点点点,在页面上输入值,提交数据看结果,而接口测试没有页面,通过接口规范文档上的调用地址.请求参数,拼接报文,然后 ...
- python下的selenium和chrome driver的安装
selenium是一款支持多种语言.多种浏览器.多个平台的开源web自动化测试软件,测试人员可用python.java等语言编写自动化脚本,使得浏览器可以完全按照你的指令运行,大大节省了测试人员用鼠标 ...
- 笔记常用Linux命令(二) 进程和端口
查看系统进程 ps:用于报告当前系统的进程状态 a:显示所有终端机下执行的程序 ps -ef/ps aux: 这两个命令都是查看当前系统正在运行进程,两者的区别是展示格式不同. 如果想要查看特定的进程 ...
- Windows 10搭建Apache2.4 + PHP7 + MySQL环境
一.准备 1.资源 Apache https://www.apachelounge.com/download/ PHP http://windows.php.net/download#php-7.0 ...
- 【编程的乐趣-用python解算法谜题系列】谜题一 保持一致
谜题一 保持一致 谜题 假设有一大群人排队等待观看棒球比赛.他们都是主场球迷,每个人都戴着队帽,但不是所有人都用同一种戴法,有些人正着戴,有些人反着戴. 假定你是保安,只有在全组球迷帽子戴法一致时才能 ...
- 使用abp框架与vue一步一步写我是月老的小工具(1)
一.前言 因为工作的原因,我真正写代码的时间很少,技术面广但深度一直不够,兴趣广泛但缺乏专业精神.以前一直想用asp.net写一款框架,用来在企业开发过程中做一些基础工作,不过后来我找到abp这个框架 ...
- EMC NW NMM to restore MS AG database
Following last article, how to restore MS AG database , that is in the following: You see ? Cheer u ...