Flink1.10进阶:ProcessFunction介绍及KeyedProcessFunction实例

微信公众号:大数据开发运维架构

关注可了解更多大数据相关的资讯。问题或建议,请公众号留言;

如果您觉得“大数据开发运维架构”对你有帮助,欢迎转发朋友圈

从微信公众号拷贝过来,格式有些错乱,建议直接去公众号阅读


一、ProcessFunction介绍

    从之前的文章我们知道,转换算子是无法访问事件的时间戳信息和水位线信息的。而这在一些应用场景下,极为重要。例如我们常用的MapFunction转换算子就无法访问时间戳或者当前事件的事件时间。

 

    基于此,DataStream API提供了一系列的Low-Level转换算子。可以访问时间戳、watermark以及注册定时事件。还可以输出特定的一些事件,例如超时事件等。Process Function用来构建事件驱动的应用以及实现自定义的业务逻辑(使用之前的window函数和转换算子无法实现)。例如,Flink SQL就是使用Process Function实现的。
Flink提供了8个Process Function:

  • ProcessFunction dataStream

  • KeyedProcessFunction 用于KeyedStream,keyBy之后的流处理

  • CoProcessFunction 用于connect连接的流

  • ProcessJoinFunction 用于join流操作

  • BroadcastProcessFunction 用于广播

  • KeyedBroadcastProcessFunction keyBy之后的广播

  • ProcessWindowFunction 窗口增量聚合

  • ProcessAllWindowFunction 全窗口聚合

 

    ProcessFunction是一个低级的流处理操作,允许访问所有流应用程序的基本构件:

  • events (stream elements)

  • state (fault-tolerant, consistent, only on keyed stream)

  • timers (event time and processing time, only on keyed stream)

 

    ProcessFunction 可以被认为是一种提供了对 KeyedState 和定时器访问的 FlatMapFunction。每在输入流中接收到一个事件,就会调用来此函数来处理。对于容错的状态,ProcessFunction 可以通过 RuntimeContext 访问 KeyedState,类似于其他有状态函数访问 KeyedState。

 

    Timers 定时器可以对处理时间和事件时间的变化做一些处理。每次调用 processElement() 都可以获得一个 Context 对象,通过该对象可以访问元素的事件时间戳以及 TimerService。TimerService 可以为尚未发生的事件时间/处理时间实注册回调。当定时器到达某个时刻时,会调用 onTimer() 方法。在调用期间,所有状态再次限定为定时器创建的键,允许定时器操作 KeyedState。

 

二、KeyedProcessFunction简单使用

1.模拟案例场景:


    对于Hadoop运维人员,需要监控生产环境大数据各个组件的状态信息,由于Regionserver经常挂掉,这里模拟监控Regionserver的状态信息,每次Regionserver上线和下线都会发送一个状态信息,当然Ambari也有服务自动的功能,这里如果Regionserver挂掉后,Ambari服务自启动功能在30秒以内没有将Regionserver拉起(这里只是为了测试使用30秒),这里我们就进行持续的告警,一般我们的都是发送钉钉告警 ,直到收到上线消息,告警取消。

 

    1).这里消息模拟从socket接收告警消息,消息格式包含三个字段分别是:主机名hostname,告警时间time,状态status(RUNNING服务正常,DEAD服务停止);

 

    2).接收消息之后对数据流按照主机名进行分组,对于状态为DEAD的消息,设置定时器5分钟以内如果状态不恢复为RUNNING,则定时进行告警,如果30秒内恢复RUNNING状态,则认为上一条消息是误报,则删除定时器,取消报警。

2.消息类MessageInfo,代码如下:


package com.hadoop.ljs.flink110.window;

/**
 * @author: Created By lujisen
 * @company ChinaUnicom Software JiNan
 * @date: 2020-04-07 11:21
 * @version: v1.0
 * @description: com.hadoop.ljs.flink110.window
 */
public class MessageInfo {
    String  hostname;
    String  msgTime;
    String status;/*RUNNING 正常 DEAD 宕机*/


    MessageInfo(String hostname,String msgTime,String status){
        this.hostname=hostname;
        this.msgTime=msgTime;
        this.status=status;
    }

    public String getHostname() {
        return hostname;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public String getMsgTime() {
        return msgTime;
    }

    public void setMsgTime(String msgTime) {
        this.msgTime = msgTime;
    }

    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
}

3.自定义MyKeyedProcessFunction类继承自KeyedProcessFunction,代码如下:


package com.hadoop.ljs.flink110.window;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;
/**
 * @author: Created By lujisen
 * @company ChinaUnicom Software JiNan
 * @date: 2020-04-07 11:20
 * @version: v1.0
 * @description: com.hadoop.ljs.flink110.window
 */
public class MyKeyedProcessFunction extends KeyedProcessFunction<String, MessageInfo, String>{

    ValueState<String> lastStatus;
    ValueState<Long> warningTimer;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
       lastStatus=getRuntimeContext().getState(new ValueStateDescriptor<>("lastStatus", String.class));
       warningTimer=getRuntimeContext().getState(new ValueStateDescriptor<>("warning-timer", Long.class));

    }

    @Override
    public void processElement(MessageInfo value, Context ctx, Collector<String> out) throws Exception {
        /*获取*/
        String currentStatus=value.getStatus();
        Long currentTimer=warningTimer.value();


        System.out.println("currentStatus:"+currentStatus);
        System.out.println("lastStatus:"+lastStatus.value());

        /*连续两次状态都是2 宕机状态,则新建定时器 30秒后进行告警*/

        if("DEAD".equals(currentStatus) && "DEAD".equals(lastStatus.value())){

            long  timeTs=Long.valueOf(ctx.timerService().currentProcessingTime())+30000L;
            ctx.timerService().registerProcessingTimeTimer(timeTs);
            warningTimer.update(timeTs);
        }
        /*如果不是连续告警,我们认为是误报警,删除定时器*/
        else if(("RUNNING".equals(currentStatus) && "DEAD".equals(lastStatus.value()))){
            if(null!=currentTimer){
              ctx.timerService().deleteProcessingTimeTimer(currentTimer);
            }
            warningTimer.clear();
        }
        /*更新上一次的状态信息*/
        lastStatus.update(value.getStatus());
    }

    @Override
    public void onTimer(long timestamp, OnTimerContext ctx, Collector<String> out) throws Exception {
        /*输出报警信息,Regionserver两次状态监测为2 宕机*/
        out.collect("主机IP:"+ctx.getCurrentKey()+" 两次Regionserver状态监测宕机,请监测!!!");
    }
}

 

4.主函数代码,仔细阅读代码注释:


package com.hadoop.ljs.flink110.window;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
/**
 * @author: Created By lujisen
 * @company ChinaUnicom Software JiNan
 * @date: 2020-04-07 11:13
 * @version: v1.0
 * @description: com.hadoop.ljs.flink110.window
 */
public class KeyedProcessFunctionMonitor {
    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment senv = StreamExecutionEnvironment.getExecutionEnvironment();
        /*设置使用EventTime作为Flink的时间处理标准,不指定默认是ProcessTime*/
        senv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        //这里为了便于理解,设置并行度为1,默认并行度是当前机器的cpu数量
        senv.setParallelism(1);
        /*指定数据源 从socket的9000端口接收数据,先进行了不合法数据的过滤*/
        DataStream<String> sourceDS = senv.socketTextStream("localhost", 9000)
                .filter(new FilterFunction<String>() {
                    @Override
                    public boolean filter(String line) throws Exception {
                        if (null == line || "".equals(line)) {
                            return false;
                        }
                        String[] lines = line.split(",");
                        if (lines.length != 3) {
                            return false;
                        }
                        return true;
                    }
                });

        /*做了一个简单的map转换,将数据转换成MessageInfo格式,第一个字段代表是主机IP,第二个字段的代表的是消息时间,第三个字段是Regionserver状态*/
        DataStream<String> warningDS = sourceDS.map(new MapFunction<String, MessageInfo>() {
            @Override
            public MessageInfo map(String line) throws Exception {
                String[] lines = line.split(",");
                return new MessageInfo(lines[0], lines[1],lines[2]);
            }
        }).keyBy(new KeySelector<MessageInfo, String>() {
            @Override
            public String getKey(MessageInfo value) throws Exception {
                return value.hostname;
            }
        }).process(new MyKeyedProcessFunction());
        /*打印报警信息*/
        warningDS.print();

        senv.execute();
    }
}

5.程序测试

    从socket端发送数据如下,这里只测试一台机器的消息:


192.168.1.101,2019-04-07 21:00,RUNNING
192.168.1.101,2019-04-07 21:02,DEAD
192.168.1.101,2019-04-07 21:03,DEAD

 

    测试结果,如果两次连续状态为DEAD,且30秒内没有恢复正常,则进行报警输出,如下图所示:

    

    这里只是讲了一种比较常用的KeyedProcessFunction,其他的后续会发出来,感谢关注!!!

 

   如果觉得我的文章能帮到您,请关注微信公众号“大数据开发运维架构”,并转发朋友圈,谢谢支持!

 

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆继森

您的鼓励将是我们创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值