Data Source 就是数据来源。 Flink 作为一款流式计算框架,它可用来做批处理,即处理静态的数据集、历史的数据集; 也可以用来做流处理,即实时的处理些实时数据流,实时的产生数据流结果,只要数据源源不断的过来,Flink 就能够一直计算下去,这个 Data Source就是数据的来源地。 Flink 中可以使用 StreamExecutionEnvironment.addSource(sourceFunction)来为你的程序添加数据来源。 Flink 已经提供了若干实现好了的 source functions,当然也可以通过实现 SourceFunction 来 自定义非并行的 source 或者扩展 RichParallelSourceFunction 或者实现 ParallelSourceFunction 接口来自定义并行的 source。 Source:数据源,Flink 在流处理和批处理上的 source 大概有 4 类:
Maven依赖
<properties>
<flink.version>1.7.2flink.version>
properties>
<dependencies>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-javaartifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-scala_2.11artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-streaming-java_2.11artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>org.apache.flinkgroupId>
<artifactId>flink-streaming-scala_2.11artifactId>
<version>${flink.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.39version>
dependency>
dependencies>
student.txt
学号,姓名,性别,年龄,部门
95002,刘晨,女,19,IS
95017,王风娟,女,18,IS
95018,王一,女,19,IS
95013,"欧阳,少恭",男,21,CS
#古剑奇谭BOSS(测试ignoreComments)
这里是错误数据(测试lenient)
95014,王小丽,女,19,CS
95019,邢小丽,女,19,IS
95020,赵钱,男,21,IS
95003,王敏,女,22,MA
95004,张立,男,19,IS
95012,孙花,女,20,CS
95010,孔小涛,男,19,CS
95005,刘刚,男,18,MA
95006,孙庆,男,23,CS
95007,易思玲,女,19,MA
95008,李娜,女,18,CS
95021,周二,男,17,MA
95022,郑明,男,20,MA
95001,李勇,男,20,CS
95011,包小柏,男,18,MA
95009,梦圆圆,女,18,MA
95015,王君,男,18,MA
95016,黄渤,男,23,MA
fromCollection(Collection) 从 Java 的 Java.util.Collection 创建数据流。集合中的所有元素类型必须相同
fromCollection(Iterator, Class) 从一个迭代器中创建数据流。Class 指定了该迭代器返回元素的类型
fromElements(T„) 从给定的对象序列中创建数据流。所有对象类型必须相同
fromParallelCollection(SplittableIterator, Class) 从一个迭代器中创建并行数据流。Class 指定了该迭代器返回元素的类型。
generateSequence(from, to) 创建一个生成指定区间范围内的数字序列的并行数据流
FromCollection.scala
package blog.source
import java.lang
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment, _}
import org.apache.flink.util.NumberSequenceIterator
/**
* @Author Daniel
* @Description Flink Source——基于集合
*
**/
object FromCollection {
def main(args: Array[String]): Unit = {
val dSEnvironment = ExecutionEnvironment.getExecutionEnvironment
val dataset1: DataSet[Int] = dSEnvironment.fromElements(1, 2, 3, 4, 5)
val dataset2: DataSet[String] = dSEnvironment.fromCollection(Iterator("Baidu", "Google", "Bing", "Yahoo"))
val dataset3: DataSet[String] = dSEnvironment.fromCollection(Array[String]("Flink", "Spark", "Hadoop"))
val dataset4: DataSet[lang.Long] = dSEnvironment.fromParallelCollection(new NumberSequenceIterator(1, 10))
val dataset5: DataSet[Long] = dSEnvironment.generateSequence(10, 20)
println("---------------------从Java.util.Collection创建数据流-----------------------")
dataset1.print()
println("---------------------从一个迭代器中创建数据流-----------------------")
dataset2.print()
println("---------------------从给定的对象序列中创建数据流-----------------------")
dataset3.print()
println("---------------------从一个迭代器中创建并行数据流-----------------------")
dataset4.print()
println("---------------------创建一个生成指定区间范围内的数字序列的并行数据流-----------------------")
dataset5.print()
}
}
readTextFile(path) 读取文本文件,即符合 TextInputFormat 规范的文件,并将其作为字符串返回
readTextFileWithValue(path)/ TextValueInputFormat 按行读取文件并将它们作为 StringValues 返回。StringValues 是可变字符串
readCsvFile(path)/ CsvInputFormat 解析逗号(或其他字符)分隔的文件。返回元组,case class 对象或 POJO 的 DataSet。支持基本 java 类型及其 value 对应作为字段类型
readFileOfPrimitives(path,delimiter)/ PrimitiveInputFormat 使用给定的分隔符来解析 new-line (或则 另外 char sequence) 文件。被分割的原始数据类 型,如 String 或 Integer。此方法类似于具有单个字段的 readCsvFile(String),但它不通过 Tuple1 生成 DataSet。
readFile(fileInputFormat, path) 根据指定的文件输入格式读取文件(一次)
createInput(inputFormat)/ InputFormat 接受通用输入格式读取数据
FromFile.scala
package blog.source
import org.apache.flink.api.java.io.TextInputFormat
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment, _}
import org.apache.flink.types.StringValue
import template.sink.Batch.Student
import org.apache.flink.core.fs.Path
/**
* @Author Daniel
* @Description Flink Source——基于文件
*
**/
object FromFile {
def main(args: Array[String]): Unit = {
val localFilePath: String = "file:///F:/IdeaProjects/flink_project/student.txt"
val dSEnvironment = ExecutionEnvironment.getExecutionEnvironment
val dS1: DataSet[String] = dSEnvironment.readTextFile(localFilePath)
val dS2: DataSet[StringValue] = dSEnvironment.readTextFileWithValue(localFilePath, "UTF-8")
val dS3: DataSet[Student] = dSEnvironment.readCsvFile[Student](
//文件路径
filePath = "file:///F:/IdeaProjects/flink_project/student.txt",
//行分隔符
lineDelimiter = "\n",
//列分隔符
fieldDelimiter = ",",
//不解析掉包裹在该字符中的一切字符,默认为null
quoteCharacter = '"',
//去掉第一行(表头),默认为false
ignoreFirstLine = true,
//忽略掉"#"开头的注释,默认为null
ignoreComments = "#",
//启用松散解析,是否忽略错误的格式,默认为false
lenient = true,
//选择需要的字段,默认所有字段
includedFields = Array[Int](0, 1, 2, 3),
//将CSV文件的字段映射为POJO字段,一定要和泛型类的结构对应
pojoFields = Array[String]("id", "name", "sex", "age")
)
val dS4: DataSet[String] = dSEnvironment.readFileOfPrimitives[String](localFilePath, delimiter = "\n")
val dS5: DataSet[String] = dSEnvironment.readFile(new TextInputFormat(new Path(localFilePath)), localFilePath)
val dS6: DataSet[String] = dSEnvironment.createInput(new TextInputFormat(new Path(localFilePath)))
println("---------------------读取文本文件-----------------------")
dS1.print()
println("---------------------按行读取文件并将它们作为StringValues返回-----------------------")
dS2.print()
println("---------------------解析逗号(或其他字符)分隔的文件-----------------------")
dS3.print()
println("---------------------使用给定的分隔符来解析文件-----------------------")
dS4.print()
println("---------------------根据指定的文件输入格式读取文件(一次)-----------------------")
dS5.print()
println("---------------------接受通用输入格式读取数据-----------------------")
dS6.print()
}
}
补充两点:
1.递归遍历输入路径目录
对于基于文件的输入,当输入路径是目录时,默认情况下不会枚举嵌套文件。 相反,只读取基目录中的文件,而忽略嵌套文件。可以通过 recursive.file.enumeration 配置参数启用嵌 套文件的递归遍历,如下例所示:
// enable recursive enumeration of nested input files
val env = ExecutionEnvironment.getExecutionEnvironment
// create a configuration object
val parameters = new Configuration
// set the recursive enumeration parameter
parameters.setBoolean("recursive.file.enumeration", true)
// pass the configuration to the data source
env.readTextFile("file:///path/with.nested/files").withParameters(parameters)
2.基于压缩文件
Flink 目前支持输入文件的透明解压缩,如果它们标有适当的文件扩展名,不过并不建议使用。这意味着不需要进一步配置输入格式,并且任何 FileInputFormat 都支持压缩,包括自定义输入格式。 请注意,压缩文件可能无法并行读取,从而影响作业可伸缩性。 下表列出了当前支出的压缩方法:
压缩方法 | File 格式 |
---|---|
DEFLATE | .deflate |
GZip | .gz, .gzip |
Bzip2 | .bz2 |
Xz | .xz |
socketTextStream(String hostname, int port) 从 socket 读取。元素可以用分隔符切分
FromSocket.scala
package blog.source
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
/**
* @Author Daniel
* @Description Flink Source——基于Socket
*
**/
object FromSocket {
def main(args: Array[String]): Unit = {
val streamEnv = StreamExecutionEnvironment.getExecutionEnvironment
val dS = streamEnv.socketTextStream("hadoop01", 9999)
println("---------------------从网络端口读取流数据-----------------------")
dS.print()
}
}
自定义的 source 常见的有 Apache Kafka、Amazon Kinesis Streams、RabbitMQ、Apache NiFi、 Twitter Streaming API 等,当然也可以定义自己的 source
addSource(new SourceFunction()) 添加一个新的 source function。例如:addSource(new FlinkKafkaConsumer011<>(„)) 以从 Apache Kafka 读取数据
MySQLSource.scala
package blog.source
import java.sql.{Connection, DriverManager, PreparedStatement}
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.functions.source.{RichSourceFunction, SourceFunction}
import template.sink.Batch.Student
/**
* @Author Daniel
* @Description Flink Source——自定义
*
**/
class MySQLSource extends RichSourceFunction[Student] {
var ps: PreparedStatement = _
var conn: Connection = _
// open()方法中建立连接,这样不用每次invoke的时候都要建立连接和释放连接。
override def open(parameters: Configuration): Unit = {
val driver = "com.mysql.jdbc.Driver"
val url = "jdbc:mysql://localhost:3306/flink"
val username = "root"
val password = "root"
//1.加载驱动
Class.forName(driver)
//2.创建连接
conn = DriverManager.getConnection(url, username, password)
//3.获得执行语句
val sql = "select id,name,sex,age, department from student;"
ps = conn.prepareStatement(sql)
}
override def cancel(): Unit = {
println("cancel...")
}
//DataStream调用run()方法用来获取数据
override def run(sourceContext: SourceFunction.SourceContext[Student]): Unit = {
try { //4.执行查询,封装数据
val resultSet = ps.executeQuery
while (resultSet.next) {
val student = Student(
resultSet.getString("id"),
resultSet.getString("name").trim,
resultSet.getString("sex").trim,
resultSet.getString("age"))
sourceContext.collect(student)
}
} catch {
case e: Exception =>
e.printStackTrace()
}
}
//程序执行完毕后关闭连接和释放资源
override def close(): Unit = { //5.关闭连接和释放资源
super.close
if (conn != null) conn.close
if (ps != null) ps.close
}
}
dept.sql
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`sex` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(30) NULL DEFAULT NULL,
`department` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (95002, '刘晨', '女', 19, 'IS');
INSERT INTO `student` VALUES (95013, '欧阳少恭', '男', 21, 'CS');
INSERT INTO `student` VALUES (95017, '王风娟', '女', 18, 'IS');
INSERT INTO `student` VALUES (95018, '王一', '女', 19, 'IS');
MySource.scala
package blog.source
import org.apache.flink.streaming.api.scala.{StreamExecutionEnvironment, _}
/**
* @Author Daniel
* @Description Flink Source——自定义
*
**/
object MySource {
def main(args: Array[String]): Unit = {
//1.创建流执行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
//2.从自定义source中读取数据
val students = env.addSource(new MySQLSource)
//3.显示结果
students.print().setParallelism(1)
//4.触发流执行
env.execute("Flink Custom Source")
}
}
总结: