3、编程模型以及核心概念

核心概念

官网链接

DataSet and DataStream

Flink具有特殊类DataSet并DataStream在程序中表示数据。您可以将它们视为可以包含重复项的不可变数据集合。在DataSet数据有界的情况下(批处理),对于一个DataStream元素的数量可以是无界的(流处理)。

这些集合(数据)在某些关键方面与常规Java集合不同。首先,它们是不可变的,这意味着一旦创建它们就无法添加或删除元素。你也不能简单地检查里面的元素。

集合最初通过source添加源创建和新的集合从这些通过将它们使用API方法如衍生map,filter等等。

flink编程模型

Flink程序看起来像是转换数据集合的常规程序。每个程序包含相同的基本部分:

  1. 获得一个execution environment
  2. 加载/创建初始数据
  3. 指定此数据的转换
  4. 指定放置计算结果的位置
  5. 触发程序执行

延迟执行

所有Flink程序都是懒惰地执行:当执行程序的main方法时,数据加载和转换不会直接发生。而是创建每个操作并将其添加到程序的计划中。当execute()执行环境上的调用显式触发执行时,实际执行操作。程序是在本地执行还是在集群上执行取决于执行环境的类型

延迟执行使您可以构建Flink作为一个整体计划单元执行的复杂程序。

指定key

某些转换(join,coGroup,keyBy,groupBy)要求在元素集合上定义key。其他转换(Reduce,GroupReduce,Aggregate,Windows)允许数据在应用之前在key上分组。

DataSet指定key

DataSet<...> input = // [...]
DataSet<...> reduced = input
  .groupBy(/*define key here*/)
  .reduceGroup(/*do something*/);

使用DataStream指定key

DataStream<...> input = // [...]
DataStream<...> windowed = input
  .keyBy(/*define key here*/)
  .window(/*window specification*/);

Flink的数据模型不基于键值对。因此,您无需将数据集类型物理打包到键和值中。键是“虚拟的”:它们被定义为作用在实际数据上的函数通过group操作符。

注意:在下面的讨论中,我们将使用DataStreamAPI和keyBy。对于DataSet API,您只需要替换为DataSet和groupBy。

为元组指定key

最简单的情况是在元组的一个或多个字段上对元组进行分组:

//元组在第一个字段(整数类型)上分组。
val input: DataStream[(Int, String, Long)] = // [...]
val keyed = input.keyBy(0)
//在这里,我们将元组分组在由第一个和第二个字段组成的复合键上。
val input: DataSet[(Int, String, Long)] = // [...]
val grouped = input.groupBy(0,1)
//关于嵌套元组的注释:如果你有一个带有嵌套元组的DataStream,例如:
DataStream<Tuple3<Tuple2<Integer, Float>,String,Long>> ds;
//指定keyBy(0)将导致系统使用full Tuple2作为键(以Integer和Float为键)。如果要指定到嵌套中Tuple2种的键,则必须使用下面解释的字段表达式键。

使用Field Expressions指定key

您可以使用基于字符串的字段表达式来引用嵌套字段,并定义用于 grouping, sorting, joining或coGrouping的键。

字段表达式可以非常轻松地选择(嵌套)复合类型中的字段,例如Tuple和POJO类型。

在下面的示例中,我们有一个WCPOJO,其中包含两个字段“word”和“count”。要按字段分组word,我们只需将其名称传递给keyBy()函数即可。

// some ordinary POJO (Plain old Java Object)
class WC(var word: String, var count: Int) {
  def this() { this("", 0L) }
}
val words: DataStream[WC] = // [...]
val wordCounts = words.keyBy("word").window(/*window specification*/)

// or, as a case class, which is less typing
case class WC(word: String, count: Int)
val words: DataStream[WC] = // [...]
val wordCounts = words.keyBy("word").window(/*window specification*/)

具体java代码案例

package com.kun.flink.java.chapter03;

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;

/**
 * 使用java api来开发Flink的实时处理应用程序
 *
 * wc统计的数据源自socket
 */
public class StreamingWCJavaAPP {
    public static void main(String[] args) throws Exception {
        //step1:获取执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //step2:读取数据
        DataStreamSource<String> text = env.socketTextStream("hadoop",9999);
        //step3:transform
        //每一行数据按照指定的分隔符拆分
        text.flatMap(new FlatMapFunction<String, WC>() {
            @Override
            public void flatMap(String s, Collector<WC> collector) throws Exception {
                String[] strings = s.toLowerCase().split(",");
                for(String string:strings){
                    if(string.length()>0){
                        //为每个单词附上次数1
                        collector.collect(new WC(string,1));
                    }
                }

            }
        })
        //合并操作
        .keyBy("word")
                .timeWindow(Time.seconds(5))
                .sum("count")
                .print()
        ;
        env.execute("StreamingWCJavaAPP");
    }

    public static class WC{
        private String word;
        private int count;

        public WC(){}

        public WC(String word, int count) {
            this.word=word;
            this.count=count;
        }

        @Override
        public String toString() {
            return "WC{" +
                    "word='" + word + '\'' +
                    ", count=" + count +
                    '}';
        }

        public String getWord() {
            return word;
        }

        public void setWord(String word) {
            this.word = word;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }
    }
}

测试输入
在这里插入图片描述
结果

2> WC{word='b', count=1}
6> WC{word='a', count=3}
1> WC{word=', count=1}
4> WC{word='c', count=2}

具体scala代码案例

package com.kun.flink.scala.chapter02

import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.streaming.api.windowing.time.Time

object StreamingWCScalaAPP {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val text=env.socketTextStream("hadoop",9999)
    import org.apache.flink.streaming.api.scala._
    text.flatMap(_.split(","))
      .map(x=>WC(x,1))
      .keyBy("word")
      .timeWindow(Time.seconds(5))
      .sum("count").print()
    env.execute("StreamingWCScalaAPP")
  }
  case class WC(word: String, count: Int)

}

测试输入
在这里插入图片描述
结果

7> WC(ccc,1)
6> WC(a,5)
2> WC(b,1)

使用键选择器功能指定key

java小代码

// some ordinary POJO
public class WC {public String word; public int count;}
DataStream<WC> words = // [...]
KeyedStream<WC> keyed = words
  .keyBy(new KeySelector<WC, String>() {
     public String getKey(WC wc) { return wc.word; }
   });

scala小代码

// some ordinary case class
case class WC(word: String, count: Int)
val words: DataStream[WC] = // [...]
val keyed = words.keyBy( _.word )

指定 Transformation Functions

1、实现一个function(java)

class MyMapFunction implements MapFunction<String, Integer> {
  public Integer map(String value) { return Integer.parseInt(value); }
};
//自己实现new MyMapFunction()这个接口
data.map(new MyMapFunction());

2、匿名类(java)
可以直接new出来这个function

data.map(new MapFunction<String, Integer> () {
  public Integer map(String value) { return Integer.parseInt(value); }
});

3、使用Lambdas表达式(java和scala)
4、Rich functions(java和scala)

支持的数据类型

具体详情

  1. Java Tuples and Scala Case Classes
  2. Java POJOs
  3. Primitive Types
  4. Regular Classes
  5. Values
  6. Hadoop Writables
  7. Special Types
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值