DataStream API【1】

一个Flink程序,就是对DataStream的各种转换。代码基本由以下几部分构成:

  • 获取执行环境
  • 读取数据源
  • 定义基于数据的各种转换操作
  • 定义计算结果的输出位置
  • 触发程序执行

执行环境

创建执行环境

执行环境是StreamExecutionEnvironment类的对象,是Flink程序的基础

  1. getExecutionEnvironment
    最简单的方式,就是直接调用getExecutionEnvironment方法。它会根据当前运行的上下文直接得到正确的结果
    //此处的 env 是 StreamExecutionEnvironment 对象
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    
  2. createLocalEnvironment
    这个方法返回一个本地执行环境。可以在调用时传入一个参数,指定默认的并行度;如果不传入,则默认并行度是本地的CPU核心数
    //此处的 localEnvironment 是 StreamExecutionEnvironment 对象
    val localEnvironment = StreamExecutionEnvironment.createLocalEnvironment()
    
  3. createRemoteEnvironment
    这个方法返回集群执行环境。需要在调用时指定JobManager的主机名和端口号,并指定要在集群中运行的Jar包
    //此处的 remoteEnv 是 StreamExecutionEnvironment 对象
    val remoteEnv = StreamExecutionEnvironment
    .createRemoteEnvironment(
    	"host", // JobManager 主机名
    	1234, // JobManager 进程端口号
    	"path/to/jarFile.jar" // 提交给 JobManager 的 JAR 包
    )
    

执行模式

  • 流执行模式(STREAMING)
    一般用于需要持续实时处理的无界数据流。默认情况下,程序使用的就是STREAMING执行模式
  • 批执行模式(BATCH)
    专门用于批处理的执行模式。Flink处理作业的方式类似MapReduce
  • 自动模式(AUTOMATIC)
    由程序根据输入数据源是否有界,自动选择执行模式

BATCH模式

  1. 通过命令行配置
    bin/flink run -Dexecution.runtime-mode=BATCH ...
    

在提交作业时,增加execution.runtime-mode参数,指定值为BATCH
2. 通过代码配置

	val env = StreamExecutionEnvironment.getExecutionEnvironment
	env.setRuntimeMode(RuntimeExecutionMode.BATCH)

在代码中,直接执行环境调用setRuntimeMode方法,传入BATCH模式

触发程序执行

需要显式调用执行环境的exeute()方法 ,来触发程序的执行。execute方法将会一直等待作业完成,然后返回一个在执行结果(JobExecutionResult)。

env.execute()

Source数据源(源算子)

DataStream API中直接提供了对一些基本数据源的支持,例如文件系统、Socket连接等;也提供了非常丰富的高级数据源连接器(Connector),例如Kafka Connector、Elasticsearch Connector等。用户也可以实现自定义Connector数据源,以便使Flink能够与其他外部系统进行数据交互

文件数据源

Flink可以将文件内容读取到系统中,并转换成分布式数据集DataStream进行处理。使用readTextFile(path)方法可以逐行读取文本文件内容,并作为字符串返回,代码如下:

//第一步:创建流处理的执行环境
val env=StreamExecutionEnvironment.getExecutionEnvironment
//第二步:读取流数据,创建DataStream
val data:DataStream[String]=env.readTextFile("hdfs://127.0.0.1:9000/input/words.txt")

Socket数据源

通过监听Socket端口接收数据创建DataStream。例如以下代码从本地的9999端口接收数据:

//第一步:创建流处理的执行环境
val senv=StreamExecutionEnvironment.getExecutionEnvironment
//第二步:读取流数据,创建DataStream
val data:DataStream[String]=senv.socketTextStream("localhost",9999)

集合数据源

  1. 集合数据源
    从java.util.collection集合创建DataStream。集合中的所有元素必须是相同类型的,例如以下代码:
//第一步:创建流处理的执行环境
val senv=StreamExecutionEnvironment.getExecutionEnvironment
//第二步:读取流数据,创建DataStream
val data:DataStream[String]=senv.fromCollection(
   List("hello","flink","scala")
)

//当然,也可以从迭代器中创建DataStream,例如以下代码:
//第一步:创建流处理的执行环境
val senv=StreamExecutionEnvironment.getExecutionEnvironment
//第二步:读取流数据,创建DataStream
val it = Iterator("hello","flink","scala")
val data:DataStream[String]=senv.fromCollection(it)

//还可以直接从元素集合中创建DataStream,例如以下代码:
//第一步:创建流处理的执行环境
val senv=StreamExecutionEnvironment.getExecutionEnvironment
//第二步:读取流数据,创建DataStream
val data:DataStream[String]=senv.fromElements("hello","flink","scala")

高级数据源

Flink可以从Kafka、Flume、Kinesis等数据源读取数据,使用时需要引入第三方依赖库。例如,在Maven工程中引入Flink针对Kafka的API依赖库,代码如下:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka_2.12</artifactId>
    <version>1.13.0</version>
</dependency>

然后使用addSource()方法接入Kafka数据源,代码示例如下:

val senv = StreamExecutionEnvironment.getExecutionEnvironment()
val myConsumer = new FlinkKafkaConsumer08[String](...)
val stream = senv.addSource(myConsumer)

自定义数据源

在Flink中,用户也可以自定义数据源,以满足不同数据源的接入需求。自定义数据源有3种方式:
1)实现SourceFunction接口定义非并行数据源(单线程)。SourceFunction是Flink中所有流数据源的基本接口。
2)实现ParallelSourceFunction接口定义并行数据源。
3)继承RichParallelSourceFunction抽象类定义并行数据源。该类已经实现了ParallelSourceFunction接口,是实现并行数据源的基类,在执行时,Flink Runtime将执行与该类源代码配置的并行度一样多的并行实例。
4)继承RichSourceFunction抽象类定义并行数据源。该类是实现并行数据源的基类,该数据源可以通过父类AbstractRichFunction的getRuntimeContext()方法访问上下文信息,通过父类AbstractRichFunction的open()和close()方法访问生命周期信息。
数据源定义好后,可以使用StreamExecutionEnvironment.addSource(sourceFunction)将数据源附加到程序中。这样就可以将外部数据转换为DataStream。

例如,自定义MySQL数据源,读取MySQL中的表数据,实现步骤如下。
(1)引入数据库驱动
在Maven工程中引入MySQL数据库连接驱动的依赖库,代码如下:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

(2)创建表
在MySQL数据库中创建一张student表并添加测试数据
(3)定义样例类
定义样例类Student用于存储数据,代码如下:

package flink.demo
object Domain {
    case class Student(id: Int, name: String, age: Int)
}

(4)创建JDBC工具类
创建一个JDBC工具类,用于获得MySQL数据库连接,代码如下:

import java.sql.DriverManager
import java.sql.Connection

/**
  * JDBC工具类
  */
object JDBCUtils {
    //数据库驱动类
    private val driver = "com.mysql.jdbc.Driver"
    //数据库连接地址
    private val url = "jdbc:mysql://localhost:3306/demo_db"
    //数据库账号
    private val username = "root"
    //数据库密码
    private val password = "xxxxxx"

    /**
      * 获得数据库连接
      */
    def getConnection(): Connection = {
        Class.forName(driver)//加载驱动
        val conn = DriverManager.getConnection(url, username, password)
        conn
    }
}

5)创建自定义数据源类
创建自定义MySQL数据源类MySQLSource,继承RichSourceFunction类,并重写open()、run()、cancel()方法,代码如下:

import java.sql.{Connection, PreparedStatement}
import flink.demo.Domain.Student
import org.apache.flink.streaming.api.functions.source.{RichSourceFunction, SourceFunction}
import org.apache.flink.configuration.Configuration

/**
  * 自定义MySQL数据源
  */
class MySQLSource extends RichSourceFunction[Student] {

    var conn: Connection = _//数据库连接对象
    var ps: PreparedStatement = _//SQL命令执行对象
    var isRunning=true//是否运行(是否持续从数据源读取数据)

    /**
      * 初始化方法
      * @param parameters 存储键/值对的轻量级配置对象
      */
    override def open(parameters: Configuration): Unit = {
        //获得数据库连接
        conn = JDBCUtils.getConnection
        //获得命令执行对象
        ps = conn.prepareStatement("select * from student")
    }

    /**
      * 当开始从数据源读取元素时,该方法将被调用
      * @param ctx 用于从数据源发射元素
      */
    override def run(ctx: SourceFunction.SourceContext[Student]): Unit = {
        //执行查询
        val rs = ps.executeQuery()
        //循环读取集合中的数据并发射出去
        while (isRunning&&rs.next()) {
            val student = Student(
                rs.getInt("id"),
                rs.getString("name"),
                rs.getInt("age")
            )
            //从数据源收集一个元素数据并发射出去,而不附加时间戳(默认方式)
            ctx.collect(student)
        }
    }

    /**
      * 取消数据源读取
     */
    override def cancel(): Unit = {
        this.isRunning=false
    }
}

6)测试程序
创建测试类StreamTest,从自定义数据源中读取流数据,打印到控制台,代码如下:

import org.apache.flink.streaming.api.scala.{DataStream, _}

/**
  * 测试类
  */
object StreamTest {
    def main(args: Array[String]): Unit = {
        //创建流处理执行环境
        val senv=StreamExecutionEnvironment.getExecutionEnvironment
         //从自定义数据源中读取数据,创建DataStream
        val dataStream: DataStream[Domain.Student] = senv.addSource(new MySQLSource)
        //打印流数据到控制台
        dataStream.print()
        //触发任务执行,指定作业名称
        senv.execute("StreamMySQLSource")
    }
}

直接在IDEA中运行上述测试类,控制台输出结果如下:
3> Student(2,李四,22)
2> Student(1,张三,19)
4> Student(3,王五,20)
结果前面的数字表示执行线程的编号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值