在上一篇文章中掌握了StreamTableEnvironment的基本概念,下面就进入代码实战环节:
一、实例中采用socket连接方式,获取数据流信息,使用 nc -l 端口号方式,开启端口监控,用来数据数据信息,实例中使用9999作为端口,命令行中输入:nc -l 9999
二、本实例主要功能是完成根据学生姓名实现分数统计
三、具体依赖实体类如下:
@Data
public class StudentInfo {
private String name;
private String sex;
private String course;
private Float score;
private Long timestamp;
}
@Data
public class StudentScoreResult {
public String name;
public float sum_total_score;
public StudentScoreResult() {}
}
四、流式tablesink包含三种方式
1、AppendStreamTableSink:定义外部表链接,以便仅通过插入更改发出流式处理表。如果更新或删除更改也修改了表,则将引发TableException
2、RetractStreamTableSink:定义外部表链接以发出包含插入、更新和删除更改的流式处理表。表将被转换为累加和收回消息流,这些消息被编码为Java Tuple2。第一个字段是指示消息类型的布尔标志(true表示插入,false表示删除)。第二个字段保存请求类型的记录
3、UpsertStreamTableSink:定义外部表链接以发出包含插入、更新和删除更改的流式处理表。表必须具有唯一的键字段(原子或复合)或只能追加。如果表没有唯一键且不是仅追加,则将引发TableException。表的唯一键由upsertstreamtablelink#setKeyFields()方法配置。表将被转换为upsert和delete消息流,这些消息被编码为Java Tuple2。第一个字段是指示消息类型的布尔标志。第二个字段保存请求的类型T的记录。带有真布尔字段的消息是配置的密钥的upsert消息。带有false标志的消息是配置密钥的删除消息。如果表是append only,则所有消息都将有一个true标志,并且必须解释为insertions。
RetractStreamTableSink中: Insert被编码成一条Add消息; Delete被编码成一条Retract消息;Update被编码成两条消息(先是一条Retract消息,再是一条Add消息),即先删除再增加。
UpsertStreamTableSink: Insert和Update均被编码成一条消息(Upsert消息); Delete被编码成一条Delete消息。
UpsertStreamTableSink和RetractStreamTableSink最大的不同在于Update编码成一条消息,效率上比RetractStreamTableSink高。
五、实现AppendStreamTableSink具体案例:
import com.springk.flink.bean.StudentInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.core.fs.FileSystem;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.java.StreamTableEnvironment;
import org.apache.flink.table.sinks.CsvTableSink;
import org.apache.flink.table.sinks.StreamTableSink;
import org.apache.flink.util.Collector;
public class TableStreamFlinkStudentAppendTest {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment streamTableEnvironment = StreamTableEnvironment.create(env);
//source,这里使用socket连接获取数据
DataStreamSource<String> text = env.socketTextStream("127.0.0.1", 9999, "\n");
//处理输入数据流,转换为StudentInfo类型,方便后续处理
SingleOutputStreamOperator<StudentInfo> dataStreamStudent = text.flatMap(new FlatMapFunction<String, StudentInfo>() {
@Override
public void flatMap(String s, Collector<StudentInfo> collector){
System.out.println(s);
String infos[] = s.split(",");
if(StringUtils.isNotBlank(s) && infos.length==4){
StudentInfo studentInfo = new StudentInfo();
studentInfo.setName(infos[0]);
studentInfo.setSex(infos[1]);
studentInfo.setCourse(infos[2]);
studentInfo.setScore(Float.parseFloat(infos[3]));
collector.collect(studentInfo);
}
}
});
//设置sink信息,对应字段,以及字段类型,案例中使用cvs写入
String[] fieldNamesStream={"name","sex","course","score"}; //dataStreamStudent 所有数据append,不会做修改
TypeInformation[] fieldTypesStream = {Types.STRING,Types.STRING, Types.STRING,Types.FLOAT};
StreamTableSink tableSinkStream = new CsvTableSink("/Users/user/Documents/student-sink"," ",1, FileSystem.WriteMode.OVERWRITE);
streamTableEnvironment.registerTableSink("studentinfostream", fieldNamesStream, fieldTypesStream, tableSinkStream);
//一、写入sink有两种方式,一种如下琐事,使用table.insert
//将dataStream转换为table
Table table = streamTableEnvironment.fromDataStream(dataStreamStudent);
table.insertInto("studentinfostream");
//二、写入sink有两种方式,另外一种如下写入,使用registerDataStream,最后使用sqlUpdate实现,效果和第一种一样
//注册dataStreamStudent流到表中,表名为:studentInfo
// streamTableEnvironment.registerDataStream("studentInfo",dataStreamStudent,"name,sex,course,score");
// streamTableEnvironment.sqlUpdate("insert into studentinfostream select name,sex,course,score from studentInfo"); env.execute("studentScoreInfo");
}
}
命令行中输入如下:
student-sink中获取内容如下:
上面实例只支持insert插入,当sink写入有修改的数据时,(例如:修改sql语句,变更为:insert into studentinfostream select name,sex,course,sum(score) as score from studentInfo group by name,sex,course
)会抛出异常:Exception in thread "main" org.apache.flink.table.api.TableException: AppendStreamTableSink requires that Table has only insert changes.