一文了解:Scala语言开发Flink的单词统计案例

240 阅读12分钟

小小项目导读

在学习了Flink的发展历史和流式计算的基础知识以后,为了快速入门,所以本文章的主要任务是搭建Flink的集群部署环境,基于IDEA搭建Flink的开发和运行环境。本文章实现了两个基本的单词统计案例,一个是批处理方式,另一个是流式处理方式,批处理方式采用对文本文件中所有的单词进行一次性的统计和处理,流式处理方式采用的是对网络中的数据流进行实时的统计,Flink客户端程序每收到一个单词就对单词进行统计,这种处理方式更符合流式数据的实时分析应用场景。


省流:

  • 要掌握Flink集群的安装和部署方式

  • 要掌握基于Scala语言开发Flink程序的流程

  • 要理解基于Flink的单词统计案例的实现原理



任务很艰巨哈QAQ

1.来看看Flink集群安装及配置

主要是学习Flink集群的安装和配置,了解Flink集群的主要部署模式,掌握Flink独立集群模式的安装及配置方法。

我们先来讲讲集群部署模式到底是啥---->(省流:下面是一些官方知识)

Flink是一个分布式的并行流处理系统,由多个进程组成,这些进程一般分布运行在不同的服务器节点上。相对于单节点的系统而言,分布式系统面临很多棘手的问题,核心的问题主要有:集群中资源的分配和管理进程如何协调和调度,如何持久化和高可用的进行数据存储,在服务器节点失效以后如何进行故障恢复等,这些都是分布式系统的典型问题,工业界已经有比较成熟的解决方案。Flink在设计之初,基于“不要重复造轮子”的思想,尽可能使用分布式系统中成熟的解决方案,Flink并不会去处理所有分布式系统中的共性问题,而是利用现有的集群架构和服务,把精力集中在核心的工作上,即分布式数据流处理上。

Flink可以配置为独立集群模式运行,也可以和其他集群资源管理工具集成使用,如YARN、Kubernetes(K8s)和Mesos。分布式系统的持久化存储可以利用HDFS来实现,对于高可用的配置可以依赖ZooKeeper来实现。

Flink中由几个主要组件组成:客户端(Client)、作业管理器(JobManager)和任务管理器(TaskManager)。Flink程序由客户端获取并进行转换,然后提交给作业管理器,作业管理器是Flink集群里的领导,对任务进行调度和管理;而它获取到要执行的作业后,会进一步处理转换,然后分发任务给众多的任务管理器,任务管理器是实际的工作者,数据的计算操作是由任务管理器实现的。

Flink核心组件: image.png

独立集群(Standalone)模式:独立集群模式包含至少包含一个Master进程和至少一个任务管理器进程,任务管理器进程运行在一台或者多台服务器节点上,所有的进程都是JVM进程。Master进程在不同的线程中运行了一个分发器(Dispatcher)和一个资源管理器,一旦它们开始运行,所有任务管理器都将在资源管理器中进行注册。提交的主要流程:

  1. 客户端向分发器提交了一个任务,分发器将会启动一个作业管理器线程,并提供执行所需的作业图(JobGraph)。

  2. 作业管理器向资源管理器请求必要的任务插槽(slots)。一旦请求的插槽分配好,作业管理器就会部署作业(job)。

  3. 在这种部署方式中,进程在失败以后,并不会自动重启。如果有足够的插槽可供使用,作业是可以从一次Worker失败中恢复的。只要运行多个Worker就好了,但是,如果作业想从Master失败中恢复的话,则只能进行高可用(HA)的配置了。


独立集群模式:

image.png

独立集群模式任务提交流程: image.png

YARN方式:YARN是Hadoop的资源管理组件,用来计算集群环境所需要的CPU和内存资源,然后提供给应用程序请求的资源。下图展示了Flink的作业提交到YARN集群的流程。

image.png 当客户端提交任务时,客户端将建立和YARN资源管理器的连接,然后启动一个新的YARN应用的Master进程,进程中包含一个作业管理器线程和一个资源管理器。作业管理器向资源管理器请求所需要的插槽(slots),用来运行Flink的作业。接下来,Flink的资源管理器将向YARN的资源管理器请求容器,然后启动任务管理器进程。一旦启动,任务管理器会将插槽注册在Flink的资源管理器中,Flink的资源管理器将把插槽提供给作业管理器,最终,作业管理器把作业的任务提交给任务管理器执行。

在了解了Flink的部署方式以后,下面以独立集群模式说明Flink集群环境的搭建过程.

1.1环境配置

搭建Flink集群环境,需要使用多台服务器节点,服务器环境的主要配置如下所示:

  • 操作系统环境使用CentOS 7
  • Java运行环境使用JDK 8
  • Hadoop集群,建议使用Hadoop 2.7及以上版本
  • 建议在3个及以上服务器节点安装集群 本文使用3台服务器节点作为Flink的部署环境,集群规划如下表所示:
主机名IP地址
bigdata01192.168.26.100

主机名IP地址
bigdata02192.168.26.101

主机名IP地址
bigdata03192.168.26.102

1.2任务实施

  1. Flink集群安装 进入Flink官方网站,下载Flink安装版本,官方网站的网址:拼命戳这里!!!

image.png 选择正确的Scala版本,Flink支持Scala开发的两个版本2.11和2.12,本文中采用的Scala版本为2.12,需要下载支持Scala 2.12的软件包,文件包名为flink-1.13.1-bin-scala_2.12.tgz。

image.png

  • 上传安装包:将Flink安装包上传到服务器master节点任意的目录,这里上传到/home/softwares/目录下面。
  • 解压缩:进入/home/softwares/目录,执行tar命令解压缩进行安装,安装目录为/home/softwares/
[root@bigdata01 softwares]$ tar -zxvf flink-1.10.1-bin-scala_2.12.tgz -C /home/softwares/
  • 改文件夹名称,进入/home/softwares/,可以查看到flink的安装目录,文件夹名称为flink-1.10.1,为方便使用,使用mv命令将文件夹名称修改为flink。
[root@bigdata01 softwares]$ mv flink-1.10.1/ flink 
  • 修改环境变量,编辑/etc/profile文件,将Flink安装路径下面的bin文件夹加入到环境变量中,这样在任意文件夹下都可以执行bin下面的文件。
[root@bigdata01 softwares]$ sudo vi /etc/profile 
#flink

export FLINK_HOME=/home/softwares/flink

export PATH=$PATH:$FLINK_HOME/bin
  • 环境变量修改完成以后,执行source命令使修改立即生效。
[root@bigdata01 softwares]$ source /etc/profile 
  • 配置Flink,Flink的安装目录下面有一个conf文件夹,Flink主要的配置文件是flink-conf.yaml文件,切换到/home/softwares/flink/conf文件夹后,编辑这个文件修改配置文件。

image.png

[root@bigdata01 conf]$ vi flink-conf.yaml 

image.png

  • 配置Flink的slave节点,在安装文件下面conf文件夹下编辑slaves文件,配置slave节点。
[root@bigdata01 conf] $ vi slaves 
bigdata02
bigdata03
  • 在bigdata02和bigdata03节点分别执行以上操作
  • 执行start-cluster.sh命令,启动集群
[root@bigdata01 bin]$ start-cluster.sh 

验证启动情况,在Web浏览器中输入网址:http://bigdata01:8081/ ,如页面可以正常打开,说明集群启动成功。

image.png

2.Flink开发环境搭建

基于IntelliJ IDEA集成开发环境配置本文章项目的开发环境,掌握在IDEA开发环境中创建基于Flink项目的方法。

2.1 创建项目

在集成开发环境IDEA启动以后,首先要创建一个项目,点击File->New->Project 菜单创建一个新的项目。

image.png 要创建的项目是基于Maven的项目,在New Project窗口选择Maven,然后点击“Next”按钮进行下一步的配置 image.png 继续配置项目的名称(Name)、存储位置(Location)和Maven相关的配置。

  • Name:项目的名称,这里输入flink_project,也可以根据实际情况输入其他名称。
  • Location:项目存储的路径
  • Maven相关的配置包括GroupId、ArtifactId和Version。
  • GroupId:组织的域名,如果没有特殊的需求,默认内容即可
  • ArtifactId:项目的名称,输入flink_project
  • Version:项目的版本号,如果没有特殊的需求,默认内容即可。
    确认无误后点击“Finish”按钮完成配置。

image.png

2.2 安装Scala插件

在IDEA集成开发环境中,点击File->Settings菜单,打开设置窗口。

image.png

输入“Scala”进行搜索并查找插件,按照相应的提示安装插件即可。

image.png Scala插件安装以后会显示在插件列表中。

image.png

2.3 在全局类库中设置Scala库

在集成开发环境IDEA中选择File->Project Structure 打开项目结构窗口。

image.png

在项目结构窗口中选择Global Libraries,设置全局类库。选择Scala SDK菜单,添加Scala SDK。

image.png 选择Scala SDK,本项目的Scala版本是2.12版,选择相应的版本即可。

image.png 确认Scala 2.12版添加到flink_project项目中。

image.png

image.png

2.4 测试Scala环境

Scala插件安装和全局类库设置完成以后,还需要在项目中添加Scala框架的支持。在项目中点击鼠标右键,在弹出的菜单中选择“Add Framework Support”,添加框架支持。

image.png

打开窗口中选择Scala,确认Scala的版本号正确无误后,点击OK按钮完成设置。

image.png 创建scala文件夹,默认Maven项目创建的main文件夹下面只有java文件夹,这个文件夹一般存储java源文件,为了使用scala编写程序,可以在main文件夹下创建一个scala文件夹。

image.png

image.png

为了标记scala文件夹下面存储的是scala源文件,需要进一步进行设置。选中scala文件夹,点击鼠标右键,在弹出的菜单中选择Make Directory As->Sources Root 标记该文件夹为源代码的根目录。

image.png 选中scala文件夹,点击鼠标右键,在弹出的菜单中选择New->Scala Class创建一个Scala类对环境进行测试。

image.png Scala类名称命名为HelloScala,类型为Object。

image.png

编写main函数,在控制台输出hello scala,对环境进行测试。

object HelloScala {   
    def main(args: Array[String]): Unit = {        
		print("hello scala")  
    } 
}

在HelloScala类上点击鼠标右键,在弹出的菜单中,点击Run HelloScala运行程序。

image.png

在控制台可以看到输出结果hello scala,说明Scala环境已经安装完成。

image.png

3.单词统计程序

这是一个基于Flink开发入门案例,对文本文件和网络数据流中的单词进行统计,可以轻松掌握基于Flink API实现文本文件和网络数据流中的单词进行统计的方法。

3.1 Maven配置

Maven依赖的配置,主要配置flink相关的依赖包。

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    <flink.version>1.10.1</flink.version>
    <scala.version>2.12</scala.version>
</properties>
<dependencies>
    <!-- flink的scala的api -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-scala_${scala.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <!-- flink streaming的scala的api -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-scala_${scala.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>

    <!-- flink table依赖包 -->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-planner_${scala.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-planner-blink_${scala.version}</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-csv</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-kafka-0.11_2.11</artifactId>
        <version>1.10.0</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-nop</artifactId>
        <version>1.7.2</version>
    </dependency>
</dependencies>

Maven插件的配置,主要配置和Scala编译相关的插件。scala-maven-plugin插件实现了将Scala源代码编译为class文件,maven-assembly-plugin插件配置了打包的方式,具体包含和过滤的文件类型。

<build>
    <plugins>
        <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>scala-maven-plugin</artifactId>
            <version>3.4.6</version>
            <executions>
                <execution>
                    <id>scala-compile-first</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>add-source</goal>
                        <goal>compile</goal>
                    </goals>
                </execution>
                <execution>
                    <id>scala-test-compile</id>
                    <phase>process-test-resources</phase>
                    <goals>
                        <goal>testCompile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

3.2 单词批量统计案例

创建文件,在本案例中,对文本文件中单词进行统计,首先要创建一个包含单词的文本文件wordcount.txt作为数据源.

hello world
hello world hello
hello world java
hello world hello
hello world
hello world hello
hello world flink
hello world hello
hello world scala
hello world hello hello world
hello world hello

3.3 编写程序

程序实现的主要功能是,读取文本文件中的内容,按照空格进行分词,对于每个单词记录为元组形式,例如文本内容是“hello world hello”构造成元组形式为:(hello,1),(world,1),(hello,1),元组构建完成以后,对所有元组进行统计,按照相同的单词进行分组,即两个(hello,1)分为一组,(world,1)分为一组。对相同单词的元组进行统计,得到最后的结果(hello,2)和(world,1)。读者如果还不理解程序中的某些语句也不用担心,后面的章节会详细介绍相应的算子,现阶段只需要按照程序清单编写程序并成功运行即可。

package chapter2

import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
import org.apache.flink.streaming.api.scala._
/**
 * 批量单词统计
 */
object BatchWordCount {
  def main(args: Array[String]): Unit = {
    //获取执行环境
    val env = ExecutionEnvironment.getExecutionEnvironment
    //文件的路径
    val filePath = "F:\flink_project\src\main\scala\data"
    //读取文件返回Dataset
    val inputDataSet: DataSet[String] = env.readTextFile(filePath)
    
    val wordCountDataSet = inputDataSet
      //按照空格进行分词
      .flatMap(_.split(" "))
      // _代表单词 (_,1)二元组
      .map((_, 1))
      // 根据第1个字段,即单词进行分组
      .groupBy(0)
      //求和,根据第2个字段,即数量进行求和
      .sum(1)

    wordCountDataSet.print()

  }
  
}

运行程序,并查看结果。程序运行结果,按照元组的形式输出了文本文件中单词的数量。

image.png 4. 单词实时统计案例

程序实现的主要功能是,从网络中读取数据流,按照空格进行分词,对于每个单词记录为元组形式,按照每收到一个单词就进行一次实时统计的方式进行统计。例如文本内容是“hello world hello”构造成元组形式,最终输出结果为(hello,1),(world,1),(hello,2)。

package chapter2

import org.apache.flink.streaming.api.scala.{StreamExecutionEnvironment, _}
/**
 * 流处理单词统计
 */
object StreamWordCount {

  def main(args: Array[String]): Unit = {
    
    //发送数据的主机名
    val host = "bigdata01"
    //发送数据的端口号
    val port = 5555
    //获取流处理的执行环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //设置并行度为:1
    env.setParallelism(1)
    //从Socket中读取一行
    val textDataSteam = env.socketTextStream(host, port)
    //读取数据,分词后进行统计
    val wordCountDataStream = textDataSteam
      //按照空格进行分词
      .flatMap(_.split(" "))
      //构造元组,(单词,1)
      .map((_, 1))
      //按照第一个字段进行分组聚合
      .keyBy(0)
      //按照第二个字段进行汇总
      .sum(1)

    wordCountDataStream.print()
    //执行
    env.execute()
  }
}

3.4 如果没有nc 则安装

第一种方法

[root@bigdata01 ~]# yum install -y nc

image.png

或者是。。。。。。。(这是一条分隔符)

第二种方法

安装netcat

因为在单词实时统计的程序中,需要借助netcat工具实现,netcat是一个简单的基于UDP和TCP协议的Unix网络工具,它是一个可靠的容易被其他程序所启用的后台操作工具,使用它可以轻易的建立任何网络连接,下面说明netcat的安装过程。

  • 上传netcat安装包到服务器指定目录:/home/softwares,并使用tar命令解压缩
[root@bigdata01 softwares]$ tar -zxvf netcat-0.7.1.tar.gz -C /home/softwares
  • 切换到解压后的源文件目录
[root@bigdata01 softwares]$ cd netcat-0.7.1
  • 使用configure设置安装路径为/home/softwares /netcat
[root@bigdata01 netcat-0.7.1]$ ./configure -prefix=/home/softwares /netcat
  • 使用make和make install命令进行编译安装
[root@bigdata01 netcat-0.7.1]make
[root@bigdata01 netcat-0.7.1]make install
  • 编辑/etc/profile文件,修改环境变量
[root@bigdata01 netcat-0.7.1]sudo vi /etc/profile
#netcat
export NETCAT_HOME=/home/softwares/netcat
export PATH=$PATH:$NETCAT_HOME/bin
  • 使用source命令,使环境变量立即生效
[root@bigdata01 netcat-0.7.1]source /etc/profile
  • 运行netcat -h命令查看帮助,如果能够正常显示,说明netcat安装成功
[root@bigdata01 netcat-0.7.1]netcat -h

image.png

3.5 测试单词实时统计程序

开启Hadoop集群,然后jps看看进程

image.png

创建网络连接,开放指定端口建立网络连接,这里指定的接口是5555,需要说明的是,端口号不一定是5555,只要端口号不冲突,任意的端口号都可以。端口号修改后,程序中的端口号也要进行相应的修改。

[root@bigdata01 ~]nc -l -p 5555 

在IDEA环境中运行单词统计程序。

image.png

程序运行后,控制台没有任何关于单词统计的输出,这是因为netcat还没有发送数据流。

image.png 输入一行文本进行测试,单词之间使用空格分隔。

[root@bigdata01 ~]# nc -l -p 5555
hello scala hello flink

在控制台查看结果,结果以元组的形式进行输出。

image.png 在结果中我们看到了4行的输出,下面对结果进行分析

  • 第1行输出(hello,1),客户端收到了单词“hello”,因为是第1次收到这个单词,统计次数是1次
  • 第2行输出(scala,1),客户端收到了单词“scala”,因为是第1次收到这个单词,统计次数是1次。
  • 第3行输出(hello,2),客户端收到了单词“hello”,因为是第2次收到这个单词,统计次数是2次。
  • 第4行输出(flink,1),客户端收到了单词“flink”,因为是第1次收到这个单词,统计次数是1次。

4.Flink项目部署

4.1 使用Maven打包

运行Maven的package命令将程序打包。

image.png

在控制台可以查看到生成的jar包文件所在路径,准备将打包文件上传到集群环境。

image.png

4.2 部署测试

  • 启动netcat,监听端口设置为:5555
[root@bigdata01 ~]# nc -l -p 5555

在Web浏览器中输入http://bigdata01:8081 打开Flink集群的管理页面,点击“Submit New Job” 菜单中“Add New”按钮提交新的作业。

image.png

  • 选择flink项目的软件包,flink_project-1.0-SNAPSHOT-jar-with-dependencies.jar

image.png 添加完成后,点击“Entity Class”列,配置作业参数。

  • Entry Class:程序入口类,形式为包名+类名, 例如,chapter2.StreamWordCount。
  • Parallelism:并行度,这里先设置为1,后续章节会讲解这个参数的含义。
  • Program Arguments:程序参数,这个程序没有入口参数,不用设置。
  • Savepoint Path:保存点路径,采用默认的路径即可,不用设置。

image.png

  • 配置作业参数完成以后,点击“Submit”按钮提交作业。

image.png 测试单词统计功能,在已经启动的netcat中,输入一行测试的单词,单词之间使用空格分隔。

[root@bigdata01 ~]# nc -l -p 5555
hello scala hello flink
  • 查看运行结果,点击“Task Managers”菜单,在任务管理器列表中,分别查看控制台输出的结果。

image.png

image.png