Hi................又来写了,嗯~ 今天有点晚了,但是还是得坚持不是~~~

接着上一篇,目前到了Flink的物理分区操作:

1,  常见的分区分为5种:

随机分区 dataStream.shuffle   

循环分区,达到平衡 dataStream.rebalance()    发生数据倾斜的时候使用这个策略是比较有效的方法

Rescaling partitioning 分区    跟上者有点像,不过是接收上游的数据 平衡到下游,比如上游是2个分区 下游是4个分区,上游的每个分区数据会等比例发送到下游。

dataStream.rescale()

广播分区,广播策略,将数据复制到下游算子的并行Task实例中,注意哦,下游的Tasks 只需要从内存中获取数据,通常我们会广播小的数据集,这个在实战开发中用的比较多,重点:dataStream.broadcast

有个思考题哈,我们在学习spark的时候 一定会遇到分区 shuffle的问题,可是接触Flink 一直没有提到shuffle这个概率,有认知的朋友们可以留言评论讨论哈。。。。。

自定义分区,继承partitioner:


import org.apache.flink.api.common.functions.Partitioner


object PartitionDemo  extends Partitioner[String]{
  val random =  scala.util.Random
  override def partition(key: String,  numpartition: Int): Int = {
    if (key.contains("appkey")) 0 else random.nextInt(10)
  }

  
}


2,数据输出 sink  kafka ,直接贴代码吧


input.addSink(new FlinkKafkaProducer010<String>(
                "dianyou_wxgz_test3",
                new SimpleStringSchema(),
                parameterTool.getProperties()));

        env.execute("111");


 

3,时间概念 ,画图解释比较好,还可以增加自己画图水平~美滋滋

flink reduce数据 flink rescale_ide

 

总结就是:Event Time 时间产生的时间

               Ingestion Time 数据刚进入Flink 还没处理的时间

              Process Time Flink算子真正在处理的时候

实际开发中我们一般使用 Event Time 跟Process Time

指定时间策略:


env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);


 

  4,WaterMark, 翻译为水印。  这个玩意.....我跟人讲过,可能表达能力欠缺,讲不明白,新手可以理解为是为了解决数据乱序的存在,非常的重要,参考两篇文章吧



好了,我们在代码中怎么使用这个玩意呢,这个玩意看着麻烦,其实使用起来还是很简单的,哈哈哈,主要是你不理解的话,你在使用测试使用过程中,发现什么情况,明明发送数据了,怎么不出结果,采坑了才知道原来水印是必须满足了条件才会下发数据。

1)我们直接在Source Function 直接定义 时间戳 和 水印。

代码呈上:


import org.apache.flink.api.scala._
import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.streaming.api.watermark.Watermark

object WaterMarkDemo {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val input  = List(("a", 2L,1), ("b", 2L,1),("c", 3L,1))
     env.addSource(new SourceFunction[(String, Long, Int)] {
       override def run(ctx: SourceFunction.SourceContext[(String, Long, Int)]): Unit = {
         input.foreach(value=> {
           //固定套路 调用 collectWithTimestamp 指定哪个字段是水印字段
           ctx.collectWithTimestamp(value,value._2)
           ctx.emitWatermark(new Watermark(value._2-1)) //设置最大延迟为1
         })
         //设置默认水印
         ctx.emitWatermark(new Watermark(Long.MaxValue))
       }

       override def cancel(): Unit = {}
     })
  }
}


2) 通过Flink自带的时间戳 Assigner指定时间戳和生成水印

env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);  //先要指定时间策略


val data = env.fromCollection(List(("a", 2L,1), ("b", 2L,1),("c", 3L,1)))

//使用系统默认的Ascend 分配时间信息和水印,我们只需指定一个字段就可以了
val watermark: DataStream[(String, Long, Int)] = data.assignAscendingTimestamps(t =>t._3)
watermark.keyBy(0).timeWindow(Time.seconds(10)).sum("_2")


3) 使用固定时延间隔的时间戳 Assigner 指定时间戳和水印 :


val data = env.fromCollection(List(("a", 2L,1), ("b", 2L,1),("c", 3L,1)))
data.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[(String, Long, Int)](Time.seconds(10)) {
  
  //定义抽取EventTIme 时间戳逻辑
  override def extractTimestamp(element: (String, Long, Int)): Long = {
    element._2
  }
})


 

4) 好了,接下来是自定义的时间戳 Assigner 和水印,这个比较多,慢慢理解哈。先会用,慢慢加深理解,反正写来写去这个就是模板,剩余的就是调参数,是把:


//抽取timestamp和生成watermark
DataStream<Tuple2<String, Long>> waterMarkStream = inputMap.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple2<String, Long>>() {

    Long currentMaxTimestamp = 0L;
    final Long maxOutOfOrderness = 10000L;// 最大允许的乱序时间是10s

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    /**
     * 定义生成watermark的逻辑
     * 默认100ms被调用一次
     */
    @Nullable
    @Override
    public Watermark getCurrentWatermark() {
        return new Watermark(currentMaxTimestamp - maxOutOfOrderness);
    }

    //定义如何提取timestamp
    @Override
    public long extractTimestamp(Tuple2<String, Long> element, long previousElementTimestamp) {
        long timestamp = element.f1;
        currentMaxTimestamp = Math.max(timestamp, currentMaxTimestamp);
        long id = Thread.currentThread().getId();
        System.out.println("currentThreadId:"+id+",key:"+element.f0+",eventtime:["+element.f1+"|"+sdf.format(element.f1)+"],currentMaxTimestamp:["+currentMaxTimestamp+"|"+
                sdf.format(currentMaxTimestamp)+"],watermark:["+getCurrentWatermark().getTimestamp()+"|"+sdf.format(getCurrentWatermark().getTimestamp())+"]");

        return timestamp;
    }
});


 5)自定义 根据特殊条件生成,比如判断某个数据袁术的当前状态,状态为0 生成水印,状态不为0 ,不触发逻辑:

自定义一个类


class  demo1 extends  AssignerWithPunctuatedWatermarks[(String,Long,Int)]{
  //定义生成逻辑 
  override def checkAndGetNextWatermark(lastElement: (String, Long, Int), extractedTimestamp: Long): Watermark = {
    if (lastElement._3==0) new  Watermark(extractedTimestamp) else  null
  }
  
  
  //todo 指定字段
   
  override def extractTimestamp(element: (String, Long, Int), previousElementTimestamp: Long): Long = {
    element._2
  }
}


 

好了,差不多了,今天就到这里把。明天继续,到了window了,这个有意思了~~~~