Flink卡夫卡定制技巧

<强>动态路由:
方案1:定制一个特殊的KafkaDynamicSink,内嵌多个原生的FlinkKafkaProducer,每个对应一个下游的卡夫卡队列
在开放的方法中读取所有卡夫卡渠道配置并构建FlinkKafkaProducer并构建一个地图:卡夫卡channelId→FlinkKafkaProducer

重载调用方法
根据路由规则找到当前流数据对应所有的ChannelId(允许多个),再从地图中获取对FlinkKafkaProducer并调用其调用方法

<强>核心代码:




<强>注意:
地图不能在构造函数中初始化,而要在开放的方法中初始化,FLINK分布式特性决定了构造函数和不开放在同一个JVM里执行
类级别的变量需要可序列化,否则需要声明为瞬态

每个新构建的FlinkKafkaProducer需要先调用
setRuntimeContext (this.getRuntimeContext ())
再调用开放的方法才能被使用


<>强优点:
可以路由到不同的代理上的话题,在不同的代理上隔离性更好

<强>缺陷:
所有的FlinkKafkaProducer只在开放的时候创建一次,后面如果添加了新的卡夫卡队列无法被动态感知并路由
更改了FlinkKafkaProducer创建和初始化的过程,从主要函数中转到了KafkaDynamicSink的开放的方法里,未经过全面测试,可能存在问题


方案2:方案1的升级版,利用FLINK分流的特性,根据路由规则将原生数据流分成多个,每个子数据流对应一个下游卡夫卡队列
在FLINK主要函数中读取所有卡夫卡渠道配置并构建FlinkKafkaProducer并构建一个地图:卡夫卡ChannelId→FlinkKafkaProducer
在输入流上构建一个SplitStream, OutputSelector中根据路由逻辑返回一组ChannelId
遍历地图,对于地图中的每个关键(ChannelId)调用SplitStream的选择方法获取对应的分支流数据,然后路由到对应的FlinkKafkaProducer

<强>核心代码:



<>强优点:
可以路由到不同的代理上的话题,在不同的代理上隔离性更好
完全利用FLINK原生的特性,更加简洁优雅,解决了方案1的第二点不足

<强>缺陷:
所有的FlinkKafkaProducer只在主函数中创建一次,后面如果添加了新的卡夫卡队列无法被动态感知并路由


方案3:利用FLINK的KeyedSerializationSchema中的getTargetTopic函数,KeyedSerializationSchema除了将对象转化卡夫卡ProducerRecord
的键值对之外还可以动态指定主题
在FLINK主要函数中将输入流通过flatMap转化为Tuple2,其中关键是目标所属的话题,值是原生数据
实现一个KeyedSerializationSchema作为构造函数传给FlinkKafkaProducer,重载getTargetTopic方法:返回Tuple2。f0

<强>核心代码:


<>强优点:
完全利用FLINK原生的特性,代码量非常少
新增加的话题也可以被路由到,不需要启停流处理

<强>缺陷:
无法像前两个方案实现代理级别的路由,只能做到主题级别的路由


<强>断流功能:

有时系统升级或者其他组件不可用,需要暂时停止卡夫卡生产商
FLINK原生机制:
被动反压:
Kafka09Fetcher包含了一根独立的KafkaConsumerThread,从卡夫卡中读取数据,再交给交接
交接可以理解为一个大小为1的队列,Kafka09Fetcher再从队列中获取并处理数据,一旦当处理速度变慢,KafkaConsumerThread
无法将数据写入交接,线程就会被阻塞

另外KeyedDeserializationSchema定义了一个isEndOfStream方法,如果返回真,Kafka09Fetcher就会停止循环并退出,导致整个流处理结束


<强>设计思路:

SignalService:,注册SignalListener,利用馆长TreeCache监听一个饲养员路径获取起动/停止流处理的信号量

SignalListener:接收饲养员变更信息的回调接口

PausableKafkaFetcher:继承Flink原生的KafkaFetcher,监听到信号变化阻塞ConsumerThread的处理

PausableKafkaConsumer:继承Flink原生的KafkaConsumer,创建PausableKafkaFetcher


<强>核心代码:


<强>




Flink卡夫卡定制技巧