Flink-Streaming之StreamPartitioner

在介绍流分区器前,为方便大家理解,我们解释下文中会用到的Flink几个关键名词概念。
Parallelism:Flink中Parallelism表示并行度,假设source算子的parallelism=2,集群有2个TaskManager,每个TaskMananger有一个slot,那么source算子会同时在2个TaskManager中运行。
Operator:一个算子,比如map算子就是Operator
UpStream:上游流
DownStream:下游流
文中的例子upStream为source算子,downStream为map算子。现在回到分区器内容中来,假设parallelism=2,当source[0]中数据T需要emit给map时,给map[0]或者map[1]哪个最优? 

最优分区器评估标准:
  1. 本地化
  2. 数据均衡
本地化和数据均衡跟鱼和熊掌不能兼得一个道理,在数据倾斜不是很夸张的提前下优先选择本地化策略。 

所有分区器都实现了StreamPartitioner接口selectChannel方法,返回选择的Channel。(Channel指map[0]或者map[1]实际运行所在的进程)

在selectChannel方法

int selectChannel(T record);

方法名

入参

出参

selectChannel

需要emit到downStream的数据

数据被分配到Channel

本地化策略

ForwardPartitioner是完全本地化分区器,没有跨节点数据传输,最优选择的分区器。RescalePartitioner是局部本地化分区器,其它都是全局分区器。
假设source--->map两个算子各自的并行度决定可使用的分区器。使用本地化分区器的算子都可组成OperatorChain

ForwardPartitioner

本地化分区器,数据传输不会出现跨节点,upStream、downStream会组成一个OperatorChain,这是最优的Partitioner策略。

 

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
  
return 0;
}

出度为1,只有一个Channel

RescalePartitioner

         采用轮询路由规则,局部分区策略,个人理解是介于本地化分区策略和全局化分区策略之间。

路由规则:

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
  
if (++nextChannelToSendTo >= numberOfChannels) {
     
nextChannelToSendTo = 0;
   }
  
return nextChannelToSendTo;
}

自增变量nextChannelToSendTo决定数据选择channel的路由规则,当大于等于最大Channels数值时重置为0,在尽可能减少跨节点传输数据的情况下确保数据负载均衡。

GlobalPartitioner

全局分区器,数据只会路由到Partitioner为0的DownStream节点。

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
   return 0;
}

当所有数据要做串行处理的时,这是一个不错的选择。

BroadcastPartitioner

         广播分区器,会把同一份数据路由给downStream所有的节点,这相当于对数据进行了复制,复制份数由setParallelism决定。

适用场景:数据字典,配置项类的数据通过广播分区策略传播到downStream每个节点

KeyGroupStreamPartitioner

键-组流分区器,基于消息键的hash值做简单的计算决定Channel,属于全局分区器。

数据路由物理图:

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
   K key;
   try {
      key = keySelector.getKey(record.getInstance().getValue());
   } catch (Exception e) {
      throw new RuntimeException("Could not extract key from " + record.getInstance().getValue(), e);
   }
   return KeyGroupRangeAssignment.assignKeyToParallelOperator(key, maxParallelism, numberOfChannels);
}

算法:Murmurhash(key)%maxParallelism*parallelism/maxParallelism

缺点:当某个键的消息比较多时,会出现数据倾斜,解决数据倾斜的方法一般是对键加前缀打散处理,对数据聚合后再去掉前缀还原。

现在看重头戏,如何确定出度的数量。

RebalancePartitioner

       再平衡分区器,启动时随机选择一个Channel,然后使用取模算法选择Channel,达到了轮询效果

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
   nextChannelToSendTo = (nextChannelToSendTo + 1) % numberOfChannels;
   return nextChannelToSendTo;
}

数据均衡上表现最好的一个分区器,在均衡上比ShufflePartitioner更出色,最大的缺点是数据跨节点传输量过大

ShufflePartitioner

混洗分区器,随机的方式把数据打散,数据均衡上也是表现良好的,比RebalancePartitioner略差,共同的缺点就是数据跨节点传输量过大

@Override
public int selectChannel(SerializationDelegate<StreamRecord<T>> record) {
   return random.nextInt(numberOfChannels);
}

CustomPartitionerWrapper

自定义分区器包装类,用户在构建此类实例前需要实现Partitioner接口,在方法partition中定义分区器路由规则。
使用自定义分区规则:
只需在DataStream类实例方法partitionCustom传入Partitioner实现类实例即可
@Override

dataStream.partitionCustom(new Partitioner<String>(){

     public int partition(String key, int numPartitions){

         return hash(key)%numPartitions

}

})

出度:

env.setParallelism(4)

env.addSource(…).map(…).keyBy(…)

上图中并行度为4,那么Channel也为4。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值