欢迎访问我的GitHub

https://github.com/zq2599/blog_demos

内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;

Flink处理函数实战系列链接

  1. 深入了解ProcessFunction的状态操作(Flink-1.10)
  2. ProcessFunction
  3. KeyedProcessFunction类
  4. ProcessAllWindowFunction(窗口处理)
  5. CoProcessFunction(双流处理)

关于处理函数(Process Function)

如下图,在常规的业务开发中,SQL、Table API、DataStream API比较常用,处于Low-level的Porcession相对用得较少,从本章开始,我们一起通过实战来熟悉处理函数(Process Function),看看这一系列的低级算子可以带给我们哪些能力?

关于ProcessFunction类

处理函数有很多种,最基础的应该ProcessFunction类,来看看它的类图,可见有RichFunction的特性open、close,然后自己有两个重要的方法processElement和onTimer:



常用特性如下所示:

  1. 处理单个元素;
  2. 访问时间戳;
  3. 旁路输出;

接下来写两个应用体验上述功能;

版本信息

  1. 开发环境操作系统:MacBook Pro 13寸, macOS Catalina 10.15.3
  2. 开发工具:IDEA ULTIMATE 2018.3
  3. JDK:1.8.0_211
  4. Maven:3.6.0
  5. Flink:1.9.2

源码下载

如果您不想写代码,整个系列的源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):

名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议

这个git项目中有多个文件夹,本章的应用在flinkstudy文件夹下,如下图红框所示:

创建工程

执行以下命令创建一个flink-1.9.2的应用工程:

  1. mvn \
  2. archetype:generate \
  3. -DarchetypeGroupId=org.apache.flink \
  4. -DarchetypeArtifactId=flink-quickstart-java \
  5. -DarchetypeVersion=1.9.2

按提示输入groupId:com.bolingcavalry,architectid:flinkdemo

第一个demo

第一个demo用来体验以下两个特性:

  1. 处理单个元素;
  2. 访问时间戳;

创建Simple.java,内容如下:

  1. package com.bolingcavalry.processfunction;
  2. import org.apache.flink.api.java.tuple.Tuple2;
  3. import org.apache.flink.streaming.api.TimeCharacteristic;
  4. import org.apache.flink.streaming.api.datastream.DataStream;
  5. import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
  6. import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
  7. import org.apache.flink.streaming.api.functions.ProcessFunction;
  8. import org.apache.flink.streaming.api.functions.source.SourceFunction;
  9. import org.apache.flink.util.Collector;
  10. public class Simple {
  11. public static void main(String[] args) throws Exception {
  12. final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
  13. env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
  14. // 并行度为1
  15. env.setParallelism(1);
  16. // 设置数据源,一共三个元素
  17. DataStream<Tuple2<String,Integer>> dataStream = env.addSource(new SourceFunction<Tuple2<String, Integer>>() {
  18. @Override
  19. public void run(SourceContext<Tuple2<String, Integer>> ctx) throws Exception {
  20. for(int i=1; i<4; i++) {
  21. String name = "name" + i;
  22. Integer value = i;
  23. long timeStamp = System.currentTimeMillis();
  24. // 将将数据和时间戳打印出来,用来验证数据
  25. System.out.println(String.format("source,%s, %d, %d\n",
  26. name,
  27. value,
  28. timeStamp));
  29. // 发射一个元素,并且戴上了时间戳
  30. ctx.collectWithTimestamp(new Tuple2<String, Integer>(name, value), timeStamp);
  31. // 为了让每个元素的时间戳不一样,每发射一次就延时10毫秒
  32. Thread.sleep(10);
  33. }
  34. }
  35. @Override
  36. public void cancel() {
  37. }
  38. });
  39. // 过滤值为奇数的元素
  40. SingleOutputStreamOperator<String> mainDataStream = dataStream
  41. .process(new ProcessFunction<Tuple2<String, Integer>, String>() {
  42. @Override
  43. public void processElement(Tuple2<String, Integer> value, Context ctx, Collector<String> out) throws Exception {
  44. // f1字段为奇数的元素不会进入下一个算子
  45. if(0 == value.f1 % 2) {
  46. out.collect(String.format("processElement,%s, %d, %d\n",
  47. value.f0,
  48. value.f1,
  49. ctx.timestamp()));
  50. }
  51. }
  52. });
  53. // 打印结果,证明每个元素的timestamp确实可以在ProcessFunction中取得
  54. mainDataStream.print();
  55. env.execute("processfunction demo : simple");
  56. }
  57. }

这里对上述代码做个介绍:

  1. 创建一个数据源,每个10毫秒发出一个元素,一共三个,类型是Tuple2,f0是个字符串,f1是整形,每个元素都带时间戳;
  2. 数据源发出元素时,提前把元素的f0、f1、时间戳打印出来,和后面的数据核对是否一致;
  3. 在后面的处理中,创建了ProcessFunction的匿名子类,里面可以处理上游发来的每个元素,并且还能取得每个元素的时间戳(这个能力很重要),然后将f1字段为奇数的元素过滤掉;
  4. 最后将ProcessFunction处理过的数据打印出来,验证处理结果是否符合预期;

直接执行Simple类,结果如下,可见过滤和提取时间戳都成功了:

第二个demo

第二个demo是实现旁路输出(Side Outputs),对于一个DataStream来说,可以通过旁路输出将数据输出到其他算子中去,而不影响原有的算子的处理,下面来演示旁路输出:

创建SideOutput类:

  1. package com.bolingcavalry.processfunction;
  2. import org.apache.flink.api.java.tuple.Tuple2;
  3. import org.apache.flink.streaming.api.datastream.DataStream;
  4. import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
  5. import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
  6. import org.apache.flink.streaming.api.functions.ProcessFunction;
  7. import org.apache.flink.util.Collector;
  8. import org.apache.flink.util.OutputTag;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. public class SideOutput {
  12. public static void main(String[] args) throws Exception {
  13. final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
  14. // 并行度为1
  15. env.setParallelism(1);
  16. // 定义OutputTag
  17. final OutputTag<String> outputTag = new OutputTag<String>("side-output"){};
  18. // 创建一个List,里面有两个Tuple2元素
  19. List<Tuple2<String, Integer>> list = new ArrayList<>();
  20. list.add(new Tuple2("aaa", 1));
  21. list.add(new Tuple2("bbb", 2));
  22. list.add(new Tuple2("ccc", 3));
  23. //通过List创建DataStream
  24. DataStream<Tuple2<String, Integer>> fromCollectionDataStream = env.fromCollection(list);
  25. //所有元素都进入mainDataStream,f1字段为奇数的元素进入SideOutput
  26. SingleOutputStreamOperator<String> mainDataStream = fromCollectionDataStream
  27. .process(new ProcessFunction<Tuple2<String, Integer>, String>() {
  28. @Override
  29. public void processElement(Tuple2<String, Integer> value, Context ctx, Collector<String> out) throws Exception {
  30. //进入主流程的下一个算子
  31. out.collect("main, name : " + value.f0 + ", value : " + value.f1);
  32. //f1字段为奇数的元素进入SideOutput
  33. if(1 == value.f1 % 2) {
  34. ctx.output(outputTag, "side, name : " + value.f0 + ", value : " + value.f1);
  35. }
  36. }
  37. });
  38. // 禁止chanin,这样可以在页面上看清楚原始的DAG
  39. mainDataStream.disableChaining();
  40. // 取得旁路数据
  41. DataStream<String> sideDataStream = mainDataStream.getSideOutput(outputTag);
  42. mainDataStream.print();
  43. sideDataStream.print();
  44. env.execute("processfunction demo : sideoutput");
  45. }
  46. }

这里对上述代码做个介绍:

  1. 数据源是个集合,类型是Tuple2,f0字段是字符串,f1字段是整形;
  2. ProcessFunction的匿名子类中,将每个元素的f0和f1拼接成字符串,发给主流程算子,再将f1字段为奇数的元素发到旁路输出;
  3. 数据源发出元素时,提前把元素的f0、f1、时间戳打印出来,和后面的数据核对是否一致;
  4. 将主流程和旁路输出的元素都打印出来,验证处理结果是否符合预期;

执行SideOutput看结果,如下图,main前缀的都是主流程算子,一共三条记录,side前缀的是旁路输出,只有f1字段为奇数的两条记录,符合预期:



上面的操作都是在IDEA上执行的,还可以将flink单独部署,再将上述工程构建成jar,提交到flink的jobmanager,可见DAG如下:



至此,处理函数中最简单的ProcessFunction类的学习和实战就完成了,接下来的文章我们会尝试更多了类型的处理函数;

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...

https://github.com/zq2599/blog_demos

Flink处理函数实战之二:ProcessFunction类的更多相关文章

  1. Flink处理函数实战之三:KeyedProcessFunction类

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. Flink处理函数实战之四:窗口处理

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. Flink处理函数实战之一:深入了解ProcessFunction的状态(Flink-1.10)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. Flink处理函数实战之五:CoProcessFunction(双流处理)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. Flink的sink实战之二:kafka

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. Flink的sink实战之一:初探

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. Flink的sink实战之三:cassandra3

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. Flink的sink实战之四:自定义

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. [Java聊天室server]实战之二 监听类

    前言 学习不论什么一个稍有难度的技术,要对其有充分理性的分析,之后果断做出决定---->也就是人们常说的"多谋善断":本系列尽管涉及的是socket相关的知识,但学习之前,更 ...

随机推荐

  1. File、Blob、ArrayBuffer等文件类的对象有什么区别和联系

    前言 在前端中处理文件时会经常遇到File.Blob.ArrayBuffer以及相关的处理方法或方式如FileReader.FormData等等这些名词,对于这些常见而又不常见的名词,我相信大多数人对 ...

  2. nginx优化:使用expires在浏览器端缓存静态文件

    一,nginx中expires指令的作用 网站的图片等静态文件一旦发布,通常很少改动, 为了减小对服务器请求的压力,提高用户浏览速度, 我们可以设置nginx中的expires, 使用户访问一次后,将 ...

  3. python 保存登录状态 cookie

    import requests from lxml import etree import faker url = "https://www.yeves.cn/admin/Articles& ...

  4. 忘记MySQL密码怎么办?一招教你搞定!

    在安装完 MySQL 或者是在使用 MySQL 时,最尴尬的就是忘记密码了,墨菲定律也告诉我们,如果一件事有可能出错,那么它一定会出错.那如果我们不小心忘记了 MySQL 的密码,该如何处理呢?别着急 ...

  5. centos7源码编译安装LNMP+ZABBIX4.0LTS(1)——nginx

    环境:192.168.117.132--zabbix server192.168.117.133--zabbix proxy 安装路径为/zabbix 安装nginx 1.安装包下载http://ng ...

  6. 最全Python基础知识点梳理

    本文主要介绍一些平时经常会用到的python基础知识点,用于加深印象,也算是对于学习这门语言的一个总结与回顾.python的详细语法介绍可以查看官方编程手册,也有一些在线网站可以学习 python语言 ...

  7. 2020-2021-1 20209306 《linux内核原理与分析》第二周作业

    一.实验一内容及分析 1.实验一内容过程截图 2.实验一完成后收获 可以看到汇编代码中出现了eax.esp.ebp.eax是累加寄存器,esp是堆栈指针寄存器,ebp是基指针寄存器.汇编代码中用到了m ...

  8. 老板,来几道web玩玩

    好久没做web了,没想到还能自己做出来555 [MRCTF2020]Ez_bypass 签到题8 给了源码,一个md5强类型比较,然后post传参,弱类型判断,直接1234567a绕过了 I put ...

  9. robotframework执行UI自动化时不能运行谷歌浏览器的问题

    robotframework执行UI自动化时报错,查看日志显示Parent suite setup failed: Variable '${browser}' not found. Did you m ...

  10. 走在深夜的小码农 Second Day

    HTML5 Second Day writer:late at night codepeasant 学习大纲 表格 表格的主要作用 表格主要用于显示.展示数据,因为它可以让数据显示的非常的规整,可读性 ...