sink算子: 把计算之后的最终结果写入到某个位置
弃用的方法,但是可以使用
如果并行度为1,直接以一个文件的形式体现,内容是追加的,因为设置了并行度env.setParallelism(1)
如果并行度大于1,以一个目录的形式出现,并行度是几就有几个文件,不设置并行度时,CPU的核数为并行度数目
dataStreamOriginal.writeAsText("E://path")
StreamingFileSink.forRowFormat获取内部类RowFormatBuilder的对象,内部类RowFormatBuilder的对象调用build()获取到单利SimpleStringEncoder
目前建议使用的方法,不管并行度是多少,都以目录形式出现
dataStreamOriginal.addSink(StreamingFileSink.forRowFormat( new Path("E://path"), new SimpleStringEncoder[SensorReading]() ).build()
/**
* 把结果写入到文件,下沉到sink
* @param dataStreamOriginal
*/
def sink_File(dataStreamOriginal: DataStream[SensorReading]): Unit ={
//文件:E://path 只有一个分区,因为设置了并行度env.setParallelism(1)
//不设置并行度时,CPU的核数为并行度数目,此时路径为目录级别,文件数为CPU核数
//分区:则是以目录形式,每一个分区产生一个文件
//被弃用的写入文件的方法
dataStreamOriginal.writeAsText("E://path")
//dataStreamOriginal.writeAsCsv()
//官方建议我们写入文件使用的方法
dataStreamOriginal.addSink(
//StreamingFileSink.forRowFormat获取内部类RowFormatBuilder的对象
//内部类RowFormatBuilder的对象调用build()获取到单利SimpleStringEncoder
StreamingFileSink.forRowFormat(
new Path("E://path"),
new SimpleStringEncoder[SensorReading]()
).build()
)
}
def main(args: Array[String]): Unit = {
//创建实时流处理的环境
val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//设置全局并行度为1
env.setParallelism(1)
//创建原始数据流
val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
//直接把字符串的流转成sensorReading的流
val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
sink_File(dataStream_sensorReading)
//实时数据流必须设置开启启动执行的语句
env.execute("transform")
}
由flink本身提供的方法,本质也是addSink
流程:
1)把数据下沉到socket 从8888端口获取原数据,下沉到9999端口
2)telnet -> 控制台 -> 服务端
注意:在socket上接收的数据基本上都是字符串(String)
/**
* 把数据下沉到socket
* 从8888端口获取原数据,下沉到9999端口
* @param dataStreamOriginal
*/
def sink_Socket(dataStreamOriginal: DataStream[String]): Unit ={
//服务端:nc -lk 8888
//控制台:客户端
//服务端:nc -lk 9999
//telnet -> 控制台 -> 服务端
//Socket接收的数据类型为String,使用的是最原始的数据
dataStreamOriginal.writeToSocket(
"node160",
9999,
new SimpleStringSchema()
)
}
def main(args: Array[String]): Unit = {
//创建实时流处理的环境
val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//设置全局并行度为1
env.setParallelism(1)
//创建原始数据流
val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
//直接把字符串的流转成sensorReading的流
val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
//向socket送数据应该都是String,把最原始的数据送出去
sink_Socket(dataStream_Original)
//实时数据流必须设置开启启动执行的语句
env.execute("transform")
}
有第三方jar包提供的类,通过addSink添加
流程:代码(把数据下沉到kafka中 ,充当kafka的生产者)当前kafka生产者的构造方法,是弃用的,但是比较简单
服务器发送数据 -> 控制台下沉 -> kafka生产者进行处理 -> kafka消费者进行消费
前提步骤:1).开启zookeeper 2).开启kafka服务 3).开启kafka的消费者 4).nc -lk 8888
/**
* 把数据下沉到kafka中
* 充当kafka的生产者
* @param dataStreamOriginal
*/
def sink_kafka(dataStreamOriginal:DataStream[String]) = {
//当前kafka生产者的构造方法,是弃用的,但是比较简单
//1.开启zookeeper
//2.开启kafka服务
//3.开启kafka的消费者
//4.nc -lk 8888
//服务器发送数据 -> 控制台下沉 -> kafka生产者进行处理 -> kafka消费者进行消费
dataStreamOriginal.addSink(new FlinkKafkaProducer[String](
"node160:9092",
"myTopic",
new SimpleStringSchema()
))
}
def main(args: Array[String]): Unit = {
//创建实时流处理的环境
val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//设置全局并行度为1
env.setParallelism(1)
//创建原始数据流
val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
//直接把字符串的流转成sensorReading的流
val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
sink_kafka(dataStream_Original)
//实时数据流必须设置开启启动执行的语句
env.execute("transform")
}
自己定义一个类,继承 [Rich]SinkFunction,重写 invoke 方法
流程:把数据下沉到mysql数据表中,flink本身没有提供此方法的下沉方式,需要自定义下沉逻辑,继承需要继承RichSinkFunction 开启执行一次连接,最后关闭一次连接,所以此逻辑需要使用 富函数
需求: 从socket读取SenorRending的数据,id相同做update操作,id不同做insert操作
/**
* 自定义下沉到mysql的逻辑
* 继承需要继承RichSinkFunction 开启执行一次连接,最后关闭一次连接
* 当前逻辑下需要开始的时候连接一次数据库和关闭一次数据库
* 所以此逻辑需要使用 富函数
*/
class MySinkToMysql extends RichSinkFunction[SensorReading]{
var conn:Connection = null
var ps_update:PreparedStatement = null
var ps_insert:PreparedStatement = null
/**
* 开始只执行一次的方法,用来做数据库连接
* @param parameters
*/
override def open(parameters: Configuration): Unit = {
//java驱动
Class.forName("com.mysql.jdbc.Driver")
//连接mysql
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/flink","root","1234")
//对数据进行修改数据
ps_update = conn.prepareStatement(" update sensor_temp set temp=? where id=? ")
//插入数据
ps_insert = conn.prepareStatement(" insert into sensor_temp value(?,?)")
}
/**
* 对于每条数据库的操作
* @param value
*/
override def invoke(value: SensorReading,context:SinkFunction.Context[_]): Unit = {
//首先进行修改数据
ps_update.setDouble(1,value.temperature)
ps_update.setString(2,value.id)
//如果修改成功的话,updateCount>0
val updateCount = ps_update.executeUpdate()
//updateCount=0,表示没有插入成功
if(updateCount==0){
ps_insert.setString(1,value.id)
ps_insert.setDouble(2,value.temperature)
//insertCount>0表示插入成功
val insertCount = ps_insert.executeUpdate()
if(insertCount==1){
println("id不存在,添加成功")
}
}else if(updateCount==1){
println("id存在,更新成功")
}
}
/**
* 最后只执行一次的方法
* 用于数据库的连接
* @return
*/
override def clone():Unit = {
if(null!=ps_insert){
ps_insert.close()
}
if(null!=ps_update){
ps_update.close()
}
if(null!=conn){
conn.close()
}
}
}
def main(args: Array[String]): Unit = {
//创建实时流处理的环境
val env:StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//设置全局并行度为1
env.setParallelism(1)
//创建原始数据流
val dataStream_Original:DataStream[String]= env.socketTextStream("node160",8888)
//直接把字符串的流转成sensorReading的流
val dataStream_sensorReading = Demo4_TransformOperator.transform_simple(env,dataStream_Original)
)
sink_MySql(dataStream_sensorReading)
//实时数据流必须设置开启启动执行的语句
env.execute("transform")
}