一、

二、常见问题排查

1、数据反压 背压(Backpressure)机制排查

点击JobName

flink做大数据跑批 flink dashboard_flink做大数据跑批

点击某个算子

flink做大数据跑批 flink dashboard_flink_02

点击Backpressure查看,状态为HIGH时,则存在数据反压问题

flink做大数据跑批 flink dashboard_flink做大数据跑批_03

flink做大数据跑批 flink dashboard_数据_04

注:若流程为A->B->C->D->E->F ,BCDEF出现反压(即这里status为high),则表示A处理流程导致 B->C->D->E->F 相继慢

查看背压:当DAG的某个过程的背压状态为 low 或者 high 时,则说明下游的处理速度不及上游的输出速度。也就是说 下游的处理是整个任务的瓶颈所在,需要进行优化处理

 

2、数据倾斜

路径:点击JobName->点击某个算子->SubTasks看每个节点处理数据量。处理不均匀会导致部分窗口数据处理缓慢

flink做大数据跑批 flink dashboard_flink_05

处理方式:

数据标记分流[详细代码见通用优化]

窗口优化[详细代码见通用优化]

在不影响逻辑的前提下,keyby对数据分流时选择较为均匀的数据。

 

3、消费滞后

现象:尚未出现数据反压和数据倾斜的状况,但是flink的watermarks追不上实时时间,不能实时处理

需单进程确认点

  flink读取的数据是否产生的及时

  窗口Aggregate处理是否存在死循环或较慢的点(如:正则/redis/http等)

   flink计算结果的输出处理慢。   (如:使用.disablechain.addsink()后再在dashboard中查看窗口和输出分别处理的速率)

优化方式:将窗口的处理逻辑优化的简单一些,将较长时间的处理放在数据处理部分或windowFunction部分

 

4、在窗口内做大量的外连情况,如redis/es等,redis连接过多会慢或直接报错

解决方案1:可以在窗口外面申请全局redis连接池作为全局变量

查看代码

class MyProcessWindowFunction extends RichWindowFunction[Accumulator,String,String,TimeWindow] {
  @transient var config_redis = new JedisPoolConfig()
  config_redis.setMaxTotal(300)
  config_redis.setMaxWaitMillis (2*1000)

  @transient var jedisPool = new JedisPool(config_redis,"10.10.10.10.",1234,0,"yourpassword")
  @transient var client = Esinit() // 此处为es外联的申明
  @transient var log = LoggerFactory.getLogger(getClass)
  //其他的一些全局变量也可以在这里定义,如log
  LoginCheck_api.KeepSession() 
  //检查保持状态的函数也可以在这里处理,这样不会每个窗口都处理一遍。

  override def apply (key: String, window: TimeWindow, input: Iterable[Accumulator], out: Collector[String]): Unit = {
    ... 
    //窗口如果定义为null则重新做定义
    if(jedisPool==null){
      w_log = LoggerFactory.getLogger(getClass)
    
      config_redis = new JedisPoolConfig()
      config_redis.setMaxTotal(300)
      config_redis.setMaxWaitMillis (2*1000)
      jedisPool = new JedisPool(config_redis,"10.10.10.10.",1234,0,"yourpassword")
      LoginCheck_api.KeepSession()
    }
    if(client==null){
      client = Esinit()
    }
    ...

问题2:网络延时问题

查看代码

class MyProcessWindowFunction extends RichWindowFunction[Accumulator,String,String,TimeWindow] {
  @transient var config_redis = new JedisPoolConfig()
  config_redis.setMaxTotal(300)
  config_redis.setMaxWaitMillis (2*1000)

  @transient var jedisPool = new JedisPool(config_redis,"10.10.10.10.",1234,0,"yourpassword")
  @transient var client = Esinit() // 此处为es外联的申明
  @transient var log = LoggerFactory.getLogger(getClass)
  //其他的一些全局变量也可以在这里定义,如log
  LoginCheck_api.KeepSession() 
  //检查保持状态的函数也可以在这里处理,这样不会每个窗口都处理一遍。

  override def apply (key: String, window: TimeWindow, input: Iterable[Accumulator], out: Collector[String]): Unit = {
    ... 
    //窗口如果定义为null则重新做定义
    if(jedisPool==null){
      w_log = LoggerFactory.getLogger(getClass)
    
      config_redis = new JedisPoolConfig()
      config_redis.setMaxTotal(300)
      config_redis.setMaxWaitMillis (2*1000)
      jedisPool = new JedisPool(config_redis,"10.10.10.10.",1234,0,"yourpassword")
      LoginCheck_api.KeepSession()
    }
    if(client==null){
      client = Esinit()
    }

 

问题3、网络延时问题

场景:flink反压,且排查redis无太多慢查日志

检查提交集群对redis的延时情况,正常应该在0.099ms以内不会影响到程序的处理过程。

问题4:将对外操作放进单独多线程操作

 

4、通用优化

1.数据标记分流:

使用数据标记过滤进入窗口的数据,
而非使用filter,map等方式去筛选数据。
split分流 select选择分流. 

val frequency_ = Features.split(
        (s:Map[String,Any])=>
          s.get("method").get.toString  match{
          
            case "a"|"b"|"c"|
                  => List("str")
            case "1"|"2"
                  =>List("int")
            case _
                  =>List("normal")
          }

      )

val all = frequency_.select("str","int").assignTimestampsAndWatermarks(new TimestampExtractor())

all.keyby().aggregate()
      
      ...
Ps. https://ci.apache.org/projects/flink/flink-docs-release-1.6/dev/stream/operators/
  


2.窗口聚合计算

window apply窗口最后触发时进行一次性计算 aggregate来一条数据计算一次。

Ps.https://ci.apache.org/projects/flink/flink-docs-release-1.6/dev/stream/operators/


3.keyby关键词无法自行选择较均匀的情况下,
可以采用keyby(Random(20)+key)的形式进行分配窗口。

最好的方式:
原有DataStream中添加专门用于分窗口的字段,但是可能会影响你窗口聚合的结果。

def dealing_input(str):(String,String){
    val keyby_key = scala.util.Random.nextInt(20).toString+"-"+key
    return (data,keyby_key)
}
input.keyby(_._2).window().xxx