第一讲 Flink基础核心知识
课程大纲 | 课程内容 | 学习效果 | 掌握目标 |
Flink概述 | Flink概述 | 了解 | |
Flink开发环境 | Flink开发环境 | 掌握 | |
Flink入门实战 | Flink入门实战 | 掌握 | |
Flink运行原理 | Flink运行原理 | 掌握 |
一、Flink概述
(一)什么是Flink
Apache Flink是一个分布式大数据处理引擎,可对有限数据流和无限数据流进行有状态计算。可部署在各种集群环境,对各种大小的数据规模进行快速计算。用下图1-1很好的诠释了flink。
1、Flink诞生历史
如下图1-2所示,Apache Flink起了个大早,赶了个晚集,在2015年突然出现在大数据舞台,然后似乎在一夜之间从一个无人所知的系统迅速转变为人人皆知的流式处理引擎。
右下图1-3所示,Flink诞生于欧洲的一个大数据研究项目,原名StratoSphere。该项目是柏林工业大学的一个研究性项目,早期专注于批计算。2014年,StratoSphere项目中的核心成员孵化出Flink,并在同年将Flink捐赠Apache,后来Flink顺利成为Apache的顶级大数据项目。同时Flink计算的主流方向被定位为流计算,即用流式计算来做所有大数据的计算工作,这就是 Flink 技术诞生的背景。
2014年Flink作为主攻流计算的大数据引擎开始在开源大数据行业内崭露头角。区别于Storm、Spark Streaming 以及其他流式计算引擎的是:它不仅是一个高吞吐、低延迟的 计算引擎,同时还提供很多高级功能。比如它提供有状态的计算,支持状态管理,支持强一致性的数据语义以及支持Event Time,WaterMark 对消息乱序的处理等。
2015年是流计算百花齐放的时代,各个流计算框架层出不穷。Storm, JStorm, Heron,Flink, Spark Streaming,Google Dataflow(后来的Beam)等等。其中Flink的一致性语义和最接近Dataflow模型的开源实现,使其成为流计算框架中最耀眼的一颗。也许这也是阿里看中Flink的原因,并决心投入重金去研究基于Flink的Blink框架。
Flink用处也是越来越多,如下图1-4所示。
1、Why Flink
- 支持批处理和数据流程序处理
- 优雅流畅的支持java和scala api
- 同时支持高吞吐量和低延迟
- 支持事件处理和无序处理通过DataStream API,基于DataFlow数据流模型
- 在不同的时间语义(事件时间,摄取时间、处理时间)下支持灵活的窗口(时间,滑动、翻滚,会话,自定义触发器)
- 仅处理一次的容错担保
- 自动反压机制
- 图处理(批) 机器学习(批) 复杂事件处理(流)
- 在dataSet(批处理)API中内置支持迭代程序(BSP)
- 高效的自定义内存管理,和健壮的切换能力在in-memory和out-of-core中
- 兼容hadoop的mapreduce和storm
(二)Flink的生态与未来
1、Flink生态
(1)Flink核心组件栈
如上图1-5所示,Flink以层级式系统形式组件其软件栈,不同层的栈建立在其下层基础上,并且各层接受程序不同层的抽象形式:
- 运行时层以JobGraph形式接收程序。JobGraph即为一个一般化的并行数据流图(data flow),它拥有任意数量的Task来接收和产生data stream
- DataStream API和DataSet API都会使用单独编译的处理方式(Separate compilation process)生成JobGraph。DataSet API使用Optimizer来决定针对程序的优化方法,而DataStream API则使用stream builder来完成该任务。
- 在执行JobGraph时,Flink提供了多种候选部署方案(如local,remote,YARN等)
- Flink附随了一些产生DataSet或DataStream API程序的的类库和API:处理逻辑表查询的Table,机器学习的FlinkML,图像处理的Gelly,事件处理的CEP。
(2)Flink生态
如上图1-6所示,可以看出Flink拥有更大更丰富的生态圈:
- 中间最底层Deploy模式包含 Local本地模式、Cluster(包含Standalone和YARN)集群模式以及Cloud云服务模式,然后它的上层是Flink runtime运行时,然后它的上层是Flink DataSet批处理和DataStream流处理,然后它的上层又扩展了Hadoop MR、Table、Gelly(图计算)、ML(机器学习)、Zoppelin(可视化工具)等等。
- 左边为输入Connectors。流处理方式包含Kafka(消息队列),AWS kinesis(实时数据流服务),RabbitMQ(消息队列),NIFI(数据管道),Twitter(API)。批处理方式包含HDFS(分布式文件系统),HBase(分布式列式数据库),Amazon S3(文件系统),MapR FS(文件系统),ALLuxio(基于内存分布式文件系统)。
- 右边为输出Connectors。流处理方式包含Kafka(消息队列),AWS kinesis(实时数据流服务),RabbitMQ(消息队列),NIFI(数据管道),Cassandra(NOSQL数据库),ElasticSearch(全文检索),HDFS rolling file(滚动文件)。批处理包含HBase(分布式列式数据库),HDFS(分布式文件系统)。
2、Flink未来
- Flink会进行批计算的突破、流处理和批处理无缝切换、界限越来越模糊、甚至混合。
- Flink会开发更多语言支持。
- Flink会逐步完善Machine Learning算法库,同时Flink也会向更成熟的机器学习、深度学习去集成(比如Tensorflow On Flink)。
(三)Flink使用场景
Apache Flink 功能强大,支持开发和运行多种不同种类的应用程序。它的主要特性包括:批流一体化、精密的状态管理、事件时间支持以及精确一次的状态一致性保障等。Flink 不仅可以运行在包括 YARN、 Mesos、Kubernetes 在内的多种资源管理框架上,还支持在裸机集群上独立部署。在启用高可用选项的情况下,它不存在单点失效问题。事实证明,Flink 已经可以扩展到数千核心,其状态可以达到 TB 级别,且仍能保持高吞吐、低延迟的特性。世界各地有很多要求严苛的流处理应用都运行在 Flink 之上。
主要应用在如下相关场景:
- 事件驱动型应用
- 数据分析应用
- 数据管道应用
1、事件驱动型应用
(1)什么是事件驱动型应用?
事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。
事件驱动型应用是在计算存储分离的传统应用基础上进化而来。在传统架构中,应用需要读写远程事务型数据库。
相反,事件驱动型应用是基于状态化流处理来完成。在该设计中,数据和计算不会分离,应用只需访问本地(内存或磁盘)即可获取数据。系统容错性的实现依赖于定期向远程持久化存储写入checkpoint。下图1-7描述了传统应用和事件驱动型应用架构的区别。
(2)事件驱动型应用的优势?
事件驱动型应用无须查询远程数据库,本地数据访问使得它具有更高的吞吐和更低的延迟。而由于定期向远程持久化存储的checkpoint工作可以异步、增量式完成,因此对于正常事件处理的影响甚微。事件驱动型应用的优势不仅限于本地数据访问。传统分层架构下,通常多个应用会共享同一个数据库,因而任何对数据库自身的更改(例如:由应用更新或服务扩容导致数据布局发生改变)都需要谨慎协调。反观事件驱动型应用,由于只需考虑自身数据,因此在更改数据表示或服务扩容时所需的协调工作将大大减少。
(3)Flink如何支持事件驱动型应用
事件驱动型应用会受制于底层流处理系统对时间和状态的把控能力,Flink诸多优秀特质都是围绕这些方面来设计的。它提供了一系列丰富的状态操作原语,允许以精确一次的一致性语义合并海量规模(TB级别)的状态数据。此外,Flink还支持事件时间和自由度极高的定制化窗口逻辑,而且它内置的ProcessFunction 支持细粒度时间控制,方便实现一些高级业务逻辑。同时,Flink还拥有一个复杂事件处理(CEP)类库,可以用来检测数据流中的模式。
Flink中针对事件驱动应用的明星特性当属savepoint。Savepoint是一个一致性的状态映像,它可以用来初始化任意状态兼容的应用。在完成一次后,即可放心对应用升级或扩容,还可以启动多个版本的应用来完成A/B测试。
(4)典型的事件驱动型应用实例
- 反欺诈
- 异常检测
- 基于规则的报警
- 业务流程监控
- (社交网络)Web 应用
2、数据分析应用
(1)什么是数据分析应用
数据分析任务需要从原始数据中提取有价值的信息和指标。传统的分析方式通常是利用批查询,或将事件记录下来并基于此有限数据集构建应用来完成。为了得到最新数据的分析结果,必须先将它们加入分析数据集并重新执行查询或运行应用,随后将结果写入存储系统或生成报告。
借助一些先进的流处理引擎,还可以实时地进行数据分析。和传统模式下读取有限数据集不同,流式查询或应用会接入实时事件流,并随着事件消费持续产生和更新结果。这些结果数据可能会写入外部数据库系统或以内部状态的形式维护。仪表展示应用可以相应地从外部数据库读取数据或直接查询应用的内部状态。
如下图1-8所示,Apache Flink 同时支持流式及批量分析应用。
(2)流式分析应用的优势?
和批量分析相比,由于流式分析省掉了周期性的数据导入和查询过程,因此从事件中获取指标的延迟更低。不仅如此,批量查询必须处理那些由定期导入和输入有界性导致的人工数据边界,而流式查询则无须考虑该问题。
另一方面,流式分析会简化应用抽象。批量查询的流水线通常由多个独立部件组成,需要周期性地调度提取数据和执行查询。如此复杂的流水线操作起来并不容易,一旦某个组件出错将会影响流水线的后续步骤。而流式分析应用整体运行在 Flink 之类的高端流处理系统之上,涵盖了从数据接入到连续结果计算的所有步骤,因此可以依赖底层引擎提供的故障恢复机制。
(3)Flink 如何支持数据分析类应用?
Flink 为持续流式分析和批量分析都提供了良好的支持。具体而言,它内置了一个符合 ANSI 标准的 SQL 接口,将批、流查询的语义统一起来。无论是在记录事件的静态数据集上还是实时事件流上,相同 SQL 查询都会得到一致的结果。同时 Flink 还支持丰富的用户自定义函数,允许在 SQL 中执行定制化代码。如果还需进一步定制逻辑,可以利用 Flink DataStream API 和 DataSet API 进行更低层次的控制。此外,Flink 的 Gelly 库为基于批量数据集的大规模高性能图分析提供了算法和构建模块支持。
(4)典型的数据分析应用实例
- 电信网络质量监控
- 移动应用中的产品更新及实验评估分析
- 消费者技术中的实时数据即席分析
- 大规模图分析
3.数据管道应用
(1)什么是数据管道?
提取-转换-加载(ETL)是一种在存储系统之间进行数据转换和迁移的常用方法。ETL 作业通常会周期性地触发,将数据从事务型数据库拷贝到分析型数据库或数据仓库。
数据管道和 ETL 作业的用途相似,都可以转换、丰富数据,并将其从某个存储系统移动到另一个。但数据管道是以持续流模式运行,而非周期性触发。因此它支持从一个不断生成数据的源头读取记录,并将它们以低延迟移动到终点。例如:数据管道可以用来监控文件系统目录中的新文件,并将其数据写入事件日志;另一个应用可能会将事件流物化到数据库或增量构建和优化查询索引。
下图1-9描述了周期性 ETL 作业和持续数据管道的差异。
(2)数据管道的优势?
和周期性 ETL 作业相比,持续数据管道可以明显降低将数据移动到目的端的延迟。此外,由于它能够持续消费和发送数据,因此用途更广,支持用例更多。
(3)Flink 如何支持数据管道应用?
很多常见的数据转换和增强操作可以利用 Flink 的 SQL 接口(或 Table API)及用户自定义函数解决。如果数据管道有更高级的需求,可以选择更通用的 DataStream API 来实现。Flink 为多种数据存储系统(如:Kafka、Kinesis、Elasticsearch、JDBC数据库系统等)内置了连接器。同时它还提供了文件系统的连续型数据源及数据汇,可用来监控目录变化和以时间分区的方式写入文件。
(4)典型的数据管道应用实例
- 电子商务中的实时查询索引构建
- 电子商务中的持续 ETL
(四)Flink V.S. Spark
1、基本比较
(1)常见不同技术栈比较
(2)api比较
(3)连接器Connectors丰富度
(4)运行时环境
(5)社区发展
2020年以前,Spark社区在规模和活跃程度上都是领先的,毕竟多了几年发展时间,同时背后的商业公司Databricks由于本土优势使得Spark在美国的影响力明显优于Flink;而且作为一个德国公司,Data Artisans 想在美国扩大影响力要更难一些。不过 Flink 社区也有一批稳定的支持者,达到了可持续发展的规模。
在中国情况可能会不一样一些。比起美国公司,中国公司做事情速度更快,更愿意尝试新技术。中国的一些创新场景也对实时性有更高的需求。这些都对 Flink 更友好一些。
近期 Flink 的中国社区有一系列动作,是了解 Flink 的好机会。
Flink 的中文社区在 http://flink-china.org/。
另外,2018年12月20日-21日在国家会议中心举办的首届Flink Forward China峰会(千人规模),参与者将有机会了解阿里巴巴、腾讯、华为、滴滴、美团、字节跳动等公司为何将 Flink 作为首选的流处理引擎。
但是自2020年,Flink在社区的发展已经超过了Spark,在Apache社区的活跃度已经排名第一,在GitHub上的持续关注度排名第二。
(6)总结
Spark 和 Flink 都是通用的开源大规模处理引擎,目标是在一个系统中支持所有的数据处理以带来效能的提升。两者都有相对比较成熟的生态系统。是下一代大数据引擎最有力的竞争者。 Spark 的生态总体更完善一些,在机器学习的集成和易用性上暂时领先。 Flink 在流计算上有明显优势,核心架构和模型也更透彻和灵活一些。 在易用性方面两者也都还有一些地方有较大的改进空间。接下来谁能尽快补上短板发挥强项就有更多的机会。
2、如何选择
没有谁强谁弱,只有哪个更适合当前的场景
- 需要关注流数据是否需要进行状态管理
- At-least-once或者Exectly-once消息投递模式是否有特殊要求
- 对于小型独立的项目,并且需要低延迟的场景,建议使用storm
- 如果你的项目已经使用了spark,并且秒级别的实时处理可以满足需求的话,建议使用sparkStreaming
- 要求消息投递语义为 Exactly Once 的场景;数据量较大,要求高吞吐低延迟的场景;需要进行状态管理或窗口统计的场景,建议使用flink
二、Flink开发环境
下载地址:http://archive.apache.org/dist/flink/
使用的版本是flink-1.9.1-bin-scala_2.11.tgz 。
Flink 有三种部署模式,分别是 Local、Standalone Cluster 和 Yarn Cluster。对于 Local 模式来说,JobManager 和 TaskManager 会共用一个 JVM 来完成 Workload。如果要验证一个简单的应用,Local 模式是最方便的。实际应用中大多使用 Standalone 或者 Yarn Cluster。
系统要求:Java 1.8.x或者更高即可,分布式模式需要配置ssh免密码登录。
(一)Local模式
[root@node01 ~]$ tar -zxvf flink-1.9.1-bin-scala_2.11.tgz -C /export/servers/ [root@node01 ~]$ mv flink-1.9.1/ flink |
安装
解压&重命名
添加进环境变量
vim ~/.bash_profile export FLINK_HOME=/export/servers/flink export PATH=$PATH:$FLINK_HOME/bin |
启动&停止
启动
${FLINK_HOME}/bin/start-cluster.sh
查看web界面
Flink的web端口默认是8081,因此访问地址为:http://<ip>:8081,如下图1-10所示。
本地模式已经成功的运行起来了。
- 停止
${FLINK_HOME}/bin/stop-cluster.sh
2、验证
(1)安装netcat服务
我们可以使用官网提供的默认案例SocketWindowWordCount来统计从socket流中读取到的文本来进行单词计数。
首先需要使用netcat命令来启动本地服务via,centOS默认没有该命令,需要进行yum安装:sudo yum -y install nc。
(2)提交flink程序
启动nc服务
nc -l 9999
提交flink程序
[root@node01 ~]$ bin/flink run \
examples/streaming/SocketWindowWordCount.jar \
--host node01 --port 9999
计算结果如下图1-13所示:
验证完毕之后,可以关闭释放资源了。
(二)Standalone Cluster
1、配置
1)修改 flink/conf/flink-conf.yaml 文件:
jobmanager.rpc.address: node01
2)修改 /conf/slaves 文件:
node01
node02
node03
3)分发给另外两台机子:
scp -r flink-1.7.0 node02:$PWD
scp -r flink-1.7.0 node03:$PWD
4)启动:
./start-cluster.sh
2、启动
(1)启动集群
在JobManager节点上启动,bin/start-cluster.sh
同样,要想进行暂停操作,bin/stop-cluster.sh。
(2)单节点启动方式JobManager/TaskManager
- 启动JobManager
bin/jobmanager.sh (start cluster)|stop|stop-all
- 启动TaskManager
bin/taskmanager.sh start|stop|stop-all
eg. flink-1.1.2]# bin/taskmanager.sh start
- 启动后的Web GUI
(三)Standalone HA
对于一个企业级的应用,稳定性是首要要考虑的问题,然后才是性能,因此HA机制是必不可少的。另外,对于已经了解Flink架构的读者,可能会更担心Flink架构背后的单点问题。和Hadoop一代一样,从架构中我们可以很明显的发现JobManager有明显的单点问题(SPOF,single point of failure)。JobManager肩负着任务调度以及资源分配,一旦JobManager出现意外,其后果可想而知。Flink对JobManager HA的处理方式,原理上基本和Hadoop一样(一代和二代)。
首先,我们需要知道Flink有两种部署的模式,分别是Standalone以及Yarn Cluster模式。对于Standalone来说,Flink必须依赖于Zookeeper来实现 JobManager的HA(Zookeeper已经成为了大部分开源框架HA必不可少的模块)。在 Zookeeper的帮助下,一个Standalone的Flink集群会同时有多个活着的 JobManager,其中只有一个处于工作状态,其他处于Standby状态。当工作中的 JobManager失去连接后(如宕机或 Crash),Zookeeper会从Standby中选举新的 JobManager 来接管Flink集群。
对于Yarn Cluster模式来说,Flink 就要依靠Yarn本身来对JobManager做HA了。其实这里完全是Yarn的机制。对于Yarn Cluster模式来说,JobManager和 TaskManager都是被Yarn启动在Yarn的Container中。此时的JobManager,其实应该称之为Flink Application Master。也就说它的故障恢复,就完全依靠着Yarn 中的ResourceManager(和MapReduce的AppMaster 一样)。
1.集群说明
关于集群资源说明如下图1-16所示:
2.安装过程
在standalone的基础之上修改配置文件flink-conf.yaml、masters。
flink-conf.yaml
#开启HA,使用文件系统作为快照存储
state.backend: filesystem
#启用检查点,可以将快照保存到HDFS
state.checkpoints.dir: hdfs://node01:8020/flink-checkpoints
#使用zookeeper搭建高可用
high-availability: zookeeper
# 存储JobManager的元数据到HDFS
high-availability.storageDir: hdfs://node01:8020/flink/ha/
high-availability.zookeeper.quorum: node01:2181,node02:2181,node03:2181
conf/master
node01:8081
node02:8082
资源同步
flink ~]$ scp -r conf/ node02:$PWD
flink ~]$ scp -r conf/ node03:$PWD
到node02中将JobManager设置为自己节点的名称
jobmanager.rpc.address:node02
服务启动
查看日志
查看日志,如下图1-17所示,发现flink和hadoop兼容依赖性有问题,主要原因在于flink自1.8之后不再提供对应的hadoop编译版本,需要用户自己重新编译或者提供一个flink和hadoop连接器。
这里提供的是flink-shaded-hadoop-2-uber-2.7.5-9.0.jar,可以到maven中央仓库下载。将该jar拷贝到$FLINK_HOME/lib/目录下,同步到每一台flink机器即可。然后重启flink。
再次查看进程
查看各节点上的进程资源,如下图1-8所示,正常显示。
Web查看
bd-offcn-01机器
(四)Flink On Yarn
参考地址:
Apache Flink 1.10 Documentation: YARN Setup
flink的yarn模式部署也分为两种方式,一种是yarn-session,一种是yarn-per-job。大致如下图:
在一个企业中,为了最大化的利用集群资源,一般都会在一个集群中同时运行多种类型的Workload。因此Flink也支持在Yarn上面运行。首先,让我们通过下图1-19了解下Yarn 和Flink的关系。
在图1-19中可以看出,Flink与Yarn的关系与MapReduce和Yarn的关系是一样的。Flink通过Yarn的接口实现了自己的App Master。当在Yarn中部署了Flink,Yarn 就会用自己的Container来启动Flink的JobManager(也就是App Master)和 TaskManager。
了解了Flink与Yarn的关系,我们就简单看下部署的步骤。在这之前需要先部署好 Yarn的集群,这里我就不做介绍了。我们可以通过以下的命令(yarn application -list)查看Yarn中现有的 Application,并且来检查Yarn的状态,结果如下图1-20所示。
如果命令正确返回了,就说明yarn的RM目前已经在启动状态。针对不同的yarn版本,Flink有不同的安装包。我们可以在Apache Flink的下载页中找到对应的安装包。我的yarn版本为 2.7.6。再介绍具体的步骤之前,我们需要先了解Flink有两种在 Yarn 上面的运行模式。一种是让yarn直接启动JobManager和TaskManager,另一种是在运行Flink Workload的时候启动Flink的模块。前者相当于让Flink的模块处于Standby的状态。
内存集中管理模式
在Yarn中初始化一个Flink集群,开辟指定的资源,之后我们提交的Flink Job都在这个Flink yarn-session中,也就是说不管提交多少个job,这些job都会共用开始时在yarn中申请的资源。这个Flink集群会常驻在Yarn集群中,除非手动停止。
步骤一:执行$FLINK_HOME/bin/yarn-session.sh 开启资源
$FLINK_HOME/bin/yarn-session.sh -n 2 -jm 1024 -tm 1024 -d
参数解释:
- -n 2 表示指定两个容器
- -jm 1024 表示jobmanager 1024M内存
- -tm 1024表示taskmanager 1024M内存
- -d 任务后台运行
- -nm,--name YARN上为一个自定义的应用设置一个名字
- -q,--query 显示yarn中可用的资源 (内存, cpu核数)
- -z,--zookeeperNamespace <arg> 针对HA模式在zookeeper上创建NameSpace
- -id,--applicationId <yarnAppId> YARN集群上的任务id,附着到一个后台运行的yarn session中
步骤二:查看web
查看web页面:http://node01:8081,因为此时jobManager是Yarn中的一个进程,所以每次启动yarn-session.sh的时候,地址都会发生变化,页面如下图1-21所示。
需要注意的是,因为没有向yarn申请资源运行程序,故而在web ui中是看不到taskManager资源的,只有作业提交起来之后,才会向yarn申请资源。
步骤三:运行flink-job
执行运行脚本:
[root@node01 flink]$ bin/flink run examples/batch/WordCount.jar \
-input hdfs://node01:8020/wordcount/input/words.txt \
-output hdfs://node01:8020/wordcount/input/output
HA验证:
杀死bd-offcn-02的YarnSessionClusterEntrypoint,会在bd-offcn-03上面自动启动一个新的YarnSessionClusterEntrypoint进程来作为JobManager,如下图1-23所示。
关闭yarn模式
yarn application -kill jobID
2.内存Job管理模式
在Yarn中,每次提交job都会创建一个新的Flink集群,任务之间相互独立,互不影响并且方便管理。任务执行完成之后创建的集群也会消失。
第二种模式其实也分为两个部分,依然是开辟资源和提交任务,但是在Job模式下,这两步都合成一个命令了。
${FLINK_HOME} bin/flink run -m yarn-cluster \
-yn 1 -yjm 1024 -ytm 1024 \
examples/batch/WordCount.jar \
-input hdfs://node01:8020/wordcount/input/words.txt \
-output hdfs://node01:8020/wordcount/input/output
查看结果:
- 命令行终端
在命令行中的执行如下图1-24所示。
- yarn-web
在yarn的web-ui上面,纪录着job的运行,如下图1-25所示。
- 程序运行结果
查看作业运行的结果,如下图1-26所示。
3.flink-on-yarn问题说明
在做flink-on-yarn整合的时候,可能会出现由于yarn集群虚拟内存不足而导致flink启动失败的问题,现象如下图1-27所示:
修改yarn-site.xml配置文件,添加如下内容:
<property>
<name>yarn.nodemanager.pmem-check-enabled</name>
<value>false</value>
</property>
<property>
<name>yarn.nodemanager.vmem-check-enabled</name>
<value>false</value>
</property>
重启yarn框架,再次执行上述的yarn-session.sh脚本。
三、Flink入门实战
(一)、Flink项目构建
1、基于Maven+Idea创建项目
创建Java项目或者Scala项目都可以,这里使用maven进行项目构建,如下图1-29所示。
输入项目中的maven的坐标和存储坐标,如下图1-30所示。
3.Maven依赖
<properties>
<flink.version>1.9.1</flink.version>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-scala_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka-0.10_2.11</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/java</source>
<source>src/main/scala</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
(二)Flink基础API概念
Flink编程是在分布式集合的基础的规律的编程模型(比如,执行filtering, mapping,updating,state,joining,grouping,defining,windows,aggregating)。这些集合可以通过外部数据源(比如从文件,kafka的topics、本地或者内存的集合)。通过下沉算子返回结果,比如将数据写入到一个分布式的文件中,或者控制台。Flink程序可以基于各种context、stanalone或者嵌入其他程序进行运行。可以在本地的jvm或者在多台机器间分布式运行。
基于外部的数据源,比如有界或者无界的数据源,我们可能会选择使用批处理的DataSet API或者流处理的DataStream API来处理。
需要注意的是,在DataStream和DataSet中的绝大多数的API是一致的,只需要替换对应的ExecutionEnvironment或者StreamExecutionEnvironment即可。
1、DataSet and DataStream
Flink在编程的过程中使用特定类——DataSet和DataStream来体现数据,类似Spark中的RDD。可以将其认为是一个可以拥有重复的不可变的集合。其中DataSet表示的是一个有界的数据集,DataStream则表示的是无界的集合。
这些集合在一些关键的地方和Java中的普通集合不同。首先,DataSet和DataStream是不可变的,这就意味着一旦被创建,便不能进行add或者remove的操作。同样也不能简单的查看集合内部的元素。
Flink可以通过外部的数据源来创建DataSet或者DataStream,也可以通过在一个已知的集合上面执行一系列的Transformation操作来转换产生新的集合。
2、Flink编程步骤
Flink程序看起来就是一个普通的程序,进行数据的转换,每一个程序包含如下相同的集合基础概念,通用编程步骤如下。
1) 创建一个执行环境ExecutionEnvironment
2) 加载或者创建初始化数据——DataSet或者DataStream
3) 在此数据基础之上进行特定的转化操作
4) 将计算的结果输出到特定的目的地
5) 触发作业的执行
Flink编程的入口,便是ExecutionEnvironment,不同之处在于,DataSet和DataStream使用的ExecutionEnvironment不同。DataSet使用ExecutionEnviroment,而DataStream使用StreamExectionEnvironment。
获得ExecutionEnvironment可以通过ExecutionEnvironment的如下方法:
getExecutionEnvironment()
createLocalEnvironment()
createRemoteEnvironment(String host, int port, String... jarFiles)
通常情况下,我们只需要使用getExecutionEnvironment()即可,因为这种方式会自动选择正确的context。如果我们在IDE中执行,则会创建一个Local的Context,如果打包到集群中执行,会返回一个Cluster的Context。
加载数据源的方式有多种。可以一行一个的读入,比如CSV文件,或者自定义格式。如果只是从一个文本文件中按顺序读取行数据。只需要如下操作即可。
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<String> text = env.readTextFile("file:///path")
创建了一个DataStream或者DataSet,接下来便可以执行各种transformation转换操作。比如执行一个map操作。
创建一个新的DataStream,类型为Integer的集合。
DataStream中包含了最终的结果,我们可以将结果通过创建一个sink操作,写入外部系统中,比如:
writeAsText(path)
一旦我们完成整个程序,我们需要通过调用StreamExecutionEnvironment的execute()方法来触发作业的执行。基于ExecutionEnvironment会在本地或者集群中执行。
execute()方法返回值为JobExecutionResult,包含本次执行时间或者累加器结果信息。
3、Lazy Evaluation
与Spark中的Transformation操作相同,Flink中的Transformation操作是Lazy懒加载的,需要execute()去触发。基于此,我们可以创建并添加程序的执行计划。进行任务调度和数据分离,执行更加高效。
4、Flink支持的数据类型
目前Flink支持7中数据类型,分别为:
- Java Tuples和Scala Case Classes
- Java POJOS(一种数据结构类型)
- Primitive Types(Java的基本数据类型)
- Regular Classes(普通类)
- Values
- Hadoop Writables
- SpecialTypes
(三)、DataSet批处理API
Flink中的DataSet程序是实现数据集转换的常规程序(例如,Filter,映射,连接, 分组)。数据集最初是从某些来源创建的(例如,通过读取文件或从本地集合创建)。结果通过接收器返回,接收器可以例如将数据写入(分布式)文件或标准输出(例如命令行终端)。Flink程序可以在各种环境中运行,独立运行或嵌入其他程序中。执行可以在本地JVM中执行,也可以在许多计算机的集群上执行。
1、Scala版本
import org.apache.flink.api.scala._
object WordCountOps {
def main(args: Array[String]): Unit = {
val env = ExecutionEnvironment.getExecutionEnvironment
val text = env.fromElements("Who's there?",
"I think I hear them. Stand, ho! Who's there?"
)
val wordCounts:DataSet[(String, Int)] = text
.flatMap(line => line.split("\\s+")).map((_, 1))
.groupBy(0)
.sum(1)
wordCounts.print()
}
}
(四)、Streaming流式处理API
Flink中的DataStream程序是实现数据流转换的常规程序(例如 filtering, updating state, defining windows, aggregating)。最初从各种源(例如, message queues, socket streams, files)创建数据流。结果通过接收器返回,接收器可以例如将数据写入文件或标准输出(例如命令行终端)。Flink程序可以在各种环境中运行,独立运行或嵌入其他程序中。执行可以在本地JVM中执行,也可以在许多计算机的集群上执行。
1、 Scala版本
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
object WindowWordCount {
def main(args: Array[String]) {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val text = env.socketTextStream("localhost", 9999)
val counts = text.flatMap { _.toLowerCase.split("\\W+") filter { _.nonEmpty } }
.map{(_, 1)}
.keyBy(0)
.sum(1)
counts.print()
env.execute("Window Stream WordCount")
}
}
要运行示例程序,首先从终端使用netcat启动输入流:
nc -lk 9999
只需键入一些单词就可以返回一个新单词。这些将是字数统计程序的输入。
(五)、Flink程序提交到集群
1、Web提交方式
可以在Flink WebUI上面完成jar的提交,如下图1-31所示(小)。
2.脚本方式
#!/bin/sh
FLINK_HOME=/home/bigdata/apps/flink
$FLINK_HOME/bin/flink run \
-c chapter1.BatchWordCount \
/home/offcn/wc.jar \
hdfs://bd-offcn-01:8020/wordcount/input/words.txt \
hdfs://bd-offcn-01:8020/wordcount/input/output15
四、Flink运行原理
(一)、Standalone模式任务调度
1、Standalone运行原理
官网学习地址:
Apache Flink 1.10 Documentation: Distributed Runtime Environment
运行原理图如下图1-32所示,其中的俗语说明如下:
- Program Code:我们编写的 Flink 应用程序代码。
- Job Client:Job Client 不是 Flink 程序执行的内部部分,但它是任务执行的起点。 Job Client 负责接受用户的程序代码,然后创建数据流,将数据流优化并提交给 Job Manager 以便进一步执行。 执行完成后,Job Client 将结果返回给用户。
- JobManager:主进程(也称为作业管理器)协调和管理程序的执行。 它的主要职责包括安排任务,管理checkpoint ,故障恢复等。机器集群中至少要有一个 master,master 负责调度 task,协调 checkpoints 和容灾,高可用设置的话可以有多个 master,但要保证一个是active, 其他是 standby; Job Manager 包含 Actor system(通信系统)、Scheduler(调度)、Check pointing 三个重要的组件。
- Task Manager:从 Job Manager 处接收需要部署的 Task。Task Manager 是在 JVM 中的一个或多个线程中执行任务的工作节点。 任务执行的并行性由每个 Task Manager 上可用的任务槽(task slot)决定。 每个任务代表分配给任务槽的一组资源。 例如,如果 Task Manager 有四个插槽,那么它将为每个插槽分配 25% 的内存。 可以在任务槽中运行一个或多个线程。 同一插槽中的线程共享相同的 JVM。 同一 JVM 中的任务共享 TCP 连接和心跳消息。Task Manager 的一个 Slot 代表一个可用线程,该线程具有固定的内存,注意 Slot 只对内存隔离,没有对 CPU 隔离。默认情况下,Flink 允许子任务共享 Slot,即使它们是不同 task 的 subtask,只要它们来自相同的 job。这种共享可以有更好的资源利用率。
2、Standalone job提交流程
提交流程如下图1-33所示:
其提交流程可归纳如下:
1、用户提交程序到jobClient
2、JobClient进行处理、解析、优化提交到JobManager
3、jobmanager给与jobclient回馈提交结果
4、jobmanager将接收到的jobgraph继续进行处理、优化,解析成task拓扑,并发送给taskmanager中
5、taskmanager执行接收到的task,并定时汇报task的状态给jm
6、jobmanager将job的任务结果返回给jobclient
7、jobclient将结果返回给用户
(二)、Flink On Yarn
1、Flink和Yarn的交互
二者的交互过程如下图1-34所示。
2、Yarn模式下提交流程
具体步骤:
1. Flink任务提交后,Client向HDFS上传Flink的Jar包和配置,
2. 之后向Yarn ResourceManager提交任务,
3. ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster,
4. ApplicationMaster启动后加载Flink的Jar包和配置构建环境,然后启动JobManager,
5.之后ApplicationMaster向ResourceManager申请资源启动TaskManager,
6. ResourceManager分配Container资源后,由ApplicationMaster通知资源所在节点的NodeManager启动TaskManager,
7. NodeManager加载Flink的Jar包和配置构建环境并启动TaskManager,
8. TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务。
(三)、TaskManager和Slots
Flink 中每一个 worker(TaskManager)都是一个 JVM 进程,它可能会在独立的线程上执行一个或多个 subtask。如下图1-35所示。
TaskManager为了对资源进行隔离和增加允许的task数,引入了slot的概念,这个slot对资源的隔离仅仅是对内存进行隔离,策略是均分。
为了控制一个 taskManager能接收多少个 task,taskManager通过task slot来进行控制(一个 taskManager至少有一个 task slot)。
默认情况下,flink允许如果任务是不同的task的时候,允许任务共享slot,当然,前提是必须在同一个job内部。如下图1-36所示。
Task Slot 是静态的概念,是指 TaskManager 具有的并发执行能力。
举例说明:
可以通过参数taskmanager.numberOfTaskSlots进行配置,而并行度parallelism是动态概念,即TaskManager运行程序时实际使用的并发能力,可以通过参数parallelism.default进行配置。
Flink 集群所需的taskslots数与job中最高的并行度一致,不需要再去计算一个程序总共会起多少个task了。
假设一共有3个TaskManager,每一个TaskManager中的分配3个TaskSlot,也就是每个TaskManager可以接收3个task,一共9个TaskSlot,如果我们设置parallelism.default=1,即运行程序默认的并行度为1,9个TaskSlot只用了1个,有8个空闲,因此,设置合适的并行度才能提高效率。
几种设置并行的方式:
法一:
#taskmanager.numberOfTaskSlots: 1
parallelism.default: 1
法二:
[offcn@bd-offcn-01 flink]$ flink run -p 1 ......
法三:
env.setParallelism(2) //设置并行度为2
法四:设置sink并行度
ret.writeAsText(args(0)).setParallelism(1)