flink学习笔记1-flink概述,kafka数据源以及MySQL作为程序输出

概述

Flink 是一个开源的分布式,高性能,高可用,准确的流处理框架。支持实时流处理和批处理。其针对数据流的分布式计算提供了数据分布、数据通信以及容错机制等功能。基于流执行引擎,Flink提供了诸多更高抽象层的API以便用户编写分布式任务:

  • DataSet API, 对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,用户可以方便地使用Flink提供的各种操作符对分布式数据集进行处理,支持Java、Scala和Python。
  • DataStream API,对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,用户可以方便地对分布式数据流进行各种操作,支持Java和Scala。
  • Table API,对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java和Scala。

从部署上讲,Flink支持local模式、集群模式(standalone集群或者Yarn集群)、云端部署。

在一般的流处理程序中,会有三种处理语义

  • at most once : 至多一次,表示一条消息不管后续处理成功与否只会被消费处理一次,那么就存在数据丢失可能
  • exactly once : 精确一次,表示一条消息从其消费到后续的处理成功,只会发生一次
  • at least once :至少一次,表示一条消息从消费到后续的处理成功,可能会发生多次

flink的checkpoint机制能把程序处理的中间状态保存下来,当程序失败可以从最新的checkpoint中恢复,通过checkpoint的机制,flink可以实现精确一次和至少一次的语义。

flink官网的第一个程序:计算单词出现的次数

public class WindowWordCount {
     public static void main(String[] args) throws Exception {

         //创建flink流执行的环境,获取环境对象
         StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

         //添加一个输入流,这里是让程序监控本机9999端口,可以在本机安装nc程序,然后在控制台执行nc -lk 9999
         DataStreamSource<String> source = env.socketTextStream("localhost", 9999);

         //读入localhost:9999输入的数据,格式为单词,然后根据splitter类进行单词拆分
         SingleOutputStreamOperator<Tuple2<String, Integer>> flatOperator = source.flatMap(new Splitter());

         //将拆分好的单词  按照单词进行分组,注意这里的keyby参数可以为数字,表示flatOperator流的某一个字段
         //也可以是字符串,表示flatOperator流表示的对象的字段名称
         KeyedStream<Tuple2<String, Integer>, Tuple> keyby = flatOperator.keyBy(0);

         //执行window操作,Windows包括计数窗口和时间窗口2大类,具体见窗口章节的说明,这里是一个时间窗口,窗口时间为5秒
         //表示将keyby流中获得的数据缓存起来,缓存5秒后再一起执行
         WindowedStream<Tuple2<String, Integer>, Tuple, TimeWindow> window = keyby.timeWindow(Time.seconds(5));
         //将Windows中的数据进行运算,求和操作,sum参数和keyby参数类似,可以是序号也可以是具体某个字段
         SingleOutputStreamOperator<Tuple2<String, Integer>> sum = window.sum(1);
         //将求和结果进行输出,这里是打印到屏幕,也可以输入文件和保存到数据库
         sum.print();

         //执行操作
         env.execute("Window WordCount");
     }

     public static class Splitter implements FlatMapFunction<String, Tuple2<String, Integer>> {
         @Override
         public void flatMap(String sentence, Collector<Tuple2<String, Integer>> out) throws Exception {
             for (String word: sentence.split(" ")) {
                 out.collect(new Tuple2<String, Integer>(word, 1));
             }
         }
     }
}

flink程序的步骤

  1. 创建flink执行环境,并设置相关全局配置
  2. 设置数据输入方式,一般对输入叫做dataSource 如从kafka读取、从某个端口读取
  3. 将读取到的数据进行计算,每一个计算逻辑一般叫做算子,如map、flatMap、keyby、window、sum等
  4. 将计算结果进行保存,一般对输出叫做dataSink 如保存到数据库或者打印到屏幕
  5. 调用执行环境的执行操作 env.execute()

注意:上述步骤都是程序启动时就开始执行了,相当于是一个剧本,先确定好剧本,当数据到来时,从DataSource中获取数据,然后依次执行算子进行数据计算,最后将数据输出到datasink中

DataSource

flink提供了如下几种最基本的DataSource:

  1. readTextFile、readFile:从文件中读取数据
  2. socketTextStream:从网络上读取数据
  3. fromCollection、fromElements、fromParallelCollection:从集合中读取数据
  4. addSource:用户自定义数据源,如kafka

DataSink:

flink提供了如下几种基本的datasink:

  1. writeAsText:当成文本写入外部文件
  2. writeAsCsv:写入Excel文件
  3. print:输出到屏幕
  4. writeUsingOutputFormat:输出到外部流中
  5. writeToSocket :输出到网络的某个socket上
  6. addSink:用户自定义输出,如数据库

kafka作为flink数据源

依赖包maven引入

<dependency>
   <groupId>org.apache.flinkgroupId>
   <artifactId>flink-connector-kafka_2.11artifactId>
   <version>1.10.0version>
dependency>

代码片段

public class KafkaSourceTest {
     public static void main(String[] args) throws Exception {
         StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
         //设置kafka相关参数 当然 这是最基础的配置 还可以添加其他的配置
         Properties props = new Properties();
         props.setProperty("bootstrap.servers","");
         props.setProperty("group.id", "groupId");
         props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
         props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
         //如果禁用检查点,可以设置自动提交偏移量,true表示自动提交偏移量,且每隔1000提交一次
         props.put("enable.auto.commit", true);
         props.put("auto.commit.interval.ms", 1000);

         //开启检查点,每1秒提交一次,且需要事先有且只有一次的消息消费
         env.enableCheckpointing(1000,CheckpointingMode.EXACTLY_ONCE);

         //创建kafka的DataSource 使用addsource方法
         //反序列化方案 SimpleStringSchema 将字节流转换成简单的字符串
         DataStreamSource<String> kafkaSource = env.addSource(new FlinkKafkaConsumer010<>("mytopic", new SimpleStringSchema(), props));
         //直接输出数据
         kafkaSource.print();
         //执行任务
         env.execute();
     }
}

检查点知识

何为检查点:

就是将程序运行过程中的状态数据以快照的形式周期性的保存起来(后一次状态数据会覆盖前一次的状态数据),用于程序重启时恢复相关中间状态,每一次状态数据的保存即触发了一次检查点

  • 如果启用检查点,props中配置的自动提交偏移量的配置将无效
  • 启用检查点,并设置检查点提交周期,最后设置检查点模式,精确一次还是最少一次
  • 启用检查点后,将在检查点提交后再提交kafka偏移量,以保证kafka中的偏移量和检查点中的偏移量一致
  • 当程序重启后,会先从检查点中获取偏移量,如果检查点中没有偏移量,则再从kafka中获取偏移量

检查点数据保存到哪里呢?默认保存到job manager的内存中,也可以在集群中进行配置,可以保存到文件系统中、HDFS中。

MySQL作为程序输出

代码片段如下:

public class MysqlSink {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //设置kafka相关参数 当然 这是最基础的配置 还可以添加其他的配置
        Properties props = new Properties();
        props.setProperty("bootstrap.servers","");
        props.setProperty("group.id", "groupId");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        //创建kafka的DataSource 使用addsource方法
        //反序列化方案 SimpleStringSchema 将字节流转换成简单的字符串
        DataStreamSource<String> kafkaSource = env.addSource(new FlinkKafkaConsumer010<>("mytopic", new SimpleStringSchema(), props));

        //将字符串转换成对象
        SingleOutputStreamOperator<List<JavaBeanPo>> operator = kafkaSource.flatMap(new RichFlatMapFunction<String, List<JavaBeanPo>>() {
            @Override
            public void flatMap(String value, Collector<List<JavaBeanPo>> out) throws Exception {
                JavaBeanPo result = JSONObject.parseObject(value, JavaBeanPo.class);
                out.collect(Arrays.asList(result));
            }
        });
        //将结果保存到MySQL数据库中
        operator.addSink(new RichSinkFunction<List<JavaBeanPo>>() {

            protected Connection connection = null;
            protected PreparedStatement ps = null;
            DruidDataSource dataSource = null;
            //只执行一次,用于创建数据库连接
            @Override
            public void open(Configuration parameters) throws Exception {
                dataSource = new DruidDataSource();
                dataSource.setDriverClassName("com.mysql.jdbc.Driver");
                dataSource.setUrl("jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&useAffectedRows=true&characterEncoding=utf8");
                dataSource.setUsername("");
                dataSource.setPassword("");
                try {
                    connection = dataSource.getConnection();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            //只执行一次,用于关闭数据库连接
            @Override
            public void close() throws Exception {
                if (dataSource != null) {
                    dataSource.close();
                }

                if(connection!=null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }

            //执行数据入库操作
            @Override
            public void invoke(List<JavaBeanPo> results, Context context) throws Exception {
                //构建sql
                String sql = "INSERT INTO tablename (colum1,colum2,...column) " +
                        " values (?,?,?,?,?,?) on duplicate key " +
                        " update colum1= values(colum1),column=values(column) ";
                ps = connection.prepareStatement(sql);

                for(JavaBeanPo record : results){
                    ps.setObject(1, record.getXX());
                    ps.setObject(n, record.getXX());
                    ps.addBatch();
                }
                ps.executeBatch();
            }
        });
        //执行任务
        env.execute();
    }
}

你可能感兴趣的:(flink学习笔记,flink)