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,时间概念 ,画图解释比较好,还可以增加自己画图水平~美滋滋
总结就是: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了,这个有意思了~~~~