Flink - DataStream State

685 阅读2分钟

Flink 包含了自动的状态管理

例如进行单词的计数:下面的例子时持续的从 socket 读取输入的单词的一个例子,每次的计数都会跟历史的计数数量进行相加的。

package com.learn.flink.source;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

import java.util.Arrays;

/**
 * 流数据 - 数据源 - socket
 * DataStream - source - socket
 */
public class SourceDemo_Socket {

    public static void main(String[] args) throws Exception {
        // 0: env
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        // 1: source
        DataStream<String> ds = env.socketTextStream("node01", 999);

        // 2. transformation
        // 切割
        DataStream<String> words = ds.flatMap((String value, Collector<String> out) -> {
            Arrays.stream(value.split(" ")).forEach(out::collect);
        }).returns(Types.STRING);
        // 每个词计数为1
        DataStream<Tuple2<String, Integer>> wordAndOne = words.map(value -> Tuple2.of(value, 1))
                .returns(Types.TUPLE(Types.STRING, Types.INT));
        // 分组
        final KeyedStream<Tuple2<String, Integer>, String> grouped = wordAndOne.keyBy(value -> value.f0);
        // 聚合
        final SingleOutputStreamOperator<Tuple2<String, Integer>> sum = grouped.sum(1);
        // 3: sink
        sum.print();
        // 4: execute
        env.execute();
    }


}

image.png 这个结果其实就是维护了一个历史数据的状态,这是 Flink 进行自动管理的。

那么在一些其他需要的场景下,状态也可以进行手动的维护。

无状态计算和有状态计算

image.png 无状态的计算:那么只要输入的数据相同,进行相同的计算,结果时一样的。例如 map, filter 等。无状态的计算时简单的。
有状态的计算有可以分为 2 种:

  • 输入的数据包含状态
  • 算子本身包含状态 有状态的计算时复杂的!

一般的使用场景有:

  • 访问的历史数据,需要与昨日进行对比
  • 窗口计算

State 的分类

  • Managed State:flink 进行自动管理和优化,支持多种数据结构,大多数情况下均可以使用
  1. Keyed State:在分组之后即使用了 keyBy 上使用
  2. Operator State: 可以在所有算子上使用,一般在 source上
  • Raw State:完全由用户自己管理,只支持 byte[],在自定义 operator 时可以使用,但是很少自定义。

详细图解: image.png

image.png

示例

KeyedState:

package com.learn.flink.state;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * 使用 keyedState 种的 valueState 来计算最大值,实际开发种使用 maxBy 即可
 */
public class StateDemo_KeyedState {

    public static void main(String[] args) throws Exception {
        //0:env
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        //1: source
        DataStream<Tuple2<String, Long>> ds = env.fromElements(
                Tuple2.of("北京", 1L),
                Tuple2.of("北京", 4L),
                Tuple2.of("上海", 8L),
                Tuple2.of("北京", 3L),
                Tuple2.of("上海", 2L)
        );
        //2: transformation
        //需求:求每个城市最大的销售额
        final SingleOutputStreamOperator<Tuple2<String, Long>> result = ds.keyBy(t -> t.f0).maxBy(1);
        // 使用 keyedState 种的 valueState 来计算最大值,模拟 maxBy 的实验原理
        final SingleOutputStreamOperator<Tuple3<String, Long, Long>> result2 = ds.keyBy(t -> t.f0).map(new RichMapFunction<Tuple2<String, Long>, Tuple3<String, Long, Long>>() {
            //1: 定义一个状态存储最大值
            private ValueState<Long> maxValueState;

            //2: 状态初始化
            @Override
            public void open(Configuration parameters) throws Exception {
                //2-1:创建状态描述器
                final ValueStateDescriptor<Long> maxValueStateDesc = new ValueStateDescriptor("maxValueState", Long.class);
                //2-2:根据状态描述初始化状态
                maxValueState = getRuntimeContext().getState(maxValueStateDesc);
            }

            //3: 使用状态
            @Override
            public Tuple3<String, Long, Long> map(Tuple2<String, Long> value) throws Exception {
                final Long currentValue = value.f1;
                //获取状态
                final Long historyMaxValue = maxValueState.value();
                if (null == historyMaxValue || historyMaxValue < currentValue) {
                    maxValueState.update(currentValue);
                }
                return Tuple3.of(value.f0, value.f1, maxValueState.value());
            }
        });
        //3: sink
        result2.print();
        //4: execute
        env.execute();

    }
}