executor在发送outbounding message的时候, 需要决定发送到next component的哪些tasks
这里就需要用到streaming grouping,

 

1. mk-grouper

除了direct grouping, 返回的是grouper function, 执行该grouper function得到target tasks list
direct grouping返回, :direct

(defn- mk-grouper
"Returns a function that returns a vector of which task indices to send tuple to, or just a single task index."
[^WorkerTopologyContext context component-id stream-id ^Fields out-fields thrift-grouping ^List target-tasks]
(let [num-tasks (count target-tasks)
random (Random.)
target-tasks (vec (sort target-tasks))]
(condp = (thrift/grouping-type thrift-grouping)
:fields ;;1.1 fields-grouping, 根据某个field进行grouping
(if (thrift/global-grouping? thrift-grouping) ;;1.2 fields为空时,代表global-grouping,所有tuple发到一个task
(fn [task-id tuple]
;; It's possible for target to have multiple tasks if it reads multiple sources
(first target-tasks)) ;;对于global-grouping,取排过序的第一个task, taskid最小的task
(let [group-fields (Fields. (thrift/field-grouping thrift-grouping))] ;;取出group-fields
(mk-fields-grouper out-fields group-fields target-tasks)
))
:all
(fn [task-id tuple] target-tasks) ;;1.3 all-grouping, 比较简单, 发送到所有task, 所以返回整个target-tasks
:shuffle
(mk-shuffle-grouper target-tasks) ;;1.4 shuffle-grouping
:local-or-shuffle ;;1.5 local优先, 如果目标tasks有local的则shuffle到local的tasks
(let [same-tasks (set/intersection
(set target-tasks)
(set (.getThisWorkerTasks context)))]
(if-not (empty? same-tasks)
(mk-shuffle-grouper (vec same-tasks))
(mk-shuffle-grouper target-tasks)))
:none ;;1.6 简单的版本的random,从target-tasks随机取一个
(fn [task-id tuple]
(let [i (mod (.nextInt random) num-tasks)]
(.get target-tasks i)
))
:custom-object
(let [grouping (thrift/instantiate-java-object (.get_custom_object thrift-grouping))]
(mk-custom-grouper grouping context component-id stream-id target-tasks))
:custom-serialized
(let [grouping (Utils/deserialize (.get_custom_serialized thrift-grouping))]
(mk-custom-grouper grouping context component-id stream-id target-tasks))
:direct
:direct
)))

 

1.1 fields-groups

使用.select取出group-fields在tuple中对应的values list, 你可以使用多个fields来进行group

使用tuple/list-hash-code, 对values list产生hash code

对num-tasks取mod, 并使用task-getter取出对应的target-tasks

(defn- mk-fields-grouper [^Fields out-fields ^Fields group-fields ^List target-tasks]
(let [num-tasks (count target-tasks)
task-getter (fn [i] (.get target-tasks i))]
(fn [task-id ^List values]
(-> (.select out-fields group-fields values)
tuple/list-hash-code
(mod num-tasks)
task-getter))))

Fields类, 除了存放fields的list, 还有个用于快速field读取的index

index的生成, 很简单, 就是记录fields以及自然排序

使用时调用select, 给出需要哪几个fields的value, 以及tuple

从index读出fields的index值, 直接从tuple中读出对应index的value (当然生成tuple的时候, 也必须安装fields的顺序生成)

public class Fields implements Iterable<String>, Serializable {
private List<String> _fields;
private Map<String, Integer> _index = new HashMap<String, Integer>(); private void index() {
for(int i=0; i<_fields.size(); i++) {
_index.put(_fields.get(i), i);
}
} public List<Object> select(Fields selector, List<Object> tuple) {
List<Object> ret = new ArrayList<Object>(selector.size());
for(String s: selector) {
ret.add(tuple.get(_index.get(s)));
}
return ret;
}
}

1.2 globle-groups

fields grouping, 但是field为空, 就代表globle grouping, 所有tuple都发送到一个task

默认选取第一个task

1.3 all-groups

发送到所有的tasks

1.4 shuffle-grouper

没有采用比较简单的直接用random取值的方式(区别于none-grouping)

因为考虑到load balance, 所以采用下面这种伪随机的实现方式

对target-tasks, 先随机shuffle, 打乱次序

在acquire-random-range-id, 会依次读所有的task, 这样保证, 虽然顺序是随机的, 但是每个task都会被选中一次

当curr越界时, 清空curr, 并从新shuffle target-tasks

(defn- mk-shuffle-grouper [^List target-tasks]
(let [choices (rotating-random-range target-tasks)]
(fn [task-id tuple]
(acquire-random-range-id choices))))
(defn rotating-random-range [choices]
(let [rand (Random.)
choices (ArrayList. choices)]
(Collections/shuffle choices rand)
[(MutableInt. -1) choices rand])) (defn acquire-random-range-id [[^MutableInt curr ^List state ^Random rand]]
(when (>= (.increment curr) (.size state))
(.set curr 0)
(Collections/shuffle state rand))
(.get state (.get curr)))

1.5 local-or-shuffle

local tasks优先选取, 并采用shuffle的方式 

1.6 none-grouping

不care grouping的方式, 现在的实现就是简单的random 

1.7 customing-grouping

可以自定义CustomStreamGrouping, 关键就是定义chooseTasks逻辑, 来实现自己的tasks choose策略

(defn- mk-custom-grouper [^CustomStreamGrouping grouping ^WorkerTopologyContext context ^String component-id ^String stream-id target-tasks]
(.prepare grouping context (GlobalStreamId. component-id stream-id) target-tasks)
(fn [task-id ^List values]
(.chooseTasks grouping task-id values)
))
public interface CustomStreamGrouping extends Serializable {
/**
* Tells the stream grouping at runtime the tasks in the target bolt.
* This information should be used in chooseTasks to determine the target tasks.
*
* It also tells the grouping the metadata on the stream this grouping will be used on.
*/
void prepare(WorkerTopologyContext context, GlobalStreamId stream, List<Integer> targetTasks); /**
* This function implements a custom stream grouping. It takes in as input
* the number of tasks in the target bolt in prepare and returns the
* tasks to send the tuples to.
*
* @param values the values to group on
*/
List<Integer> chooseTasks(int taskId, List<Object> values);
}

:custom-object 和:custom-serialized 的不同仅仅是, thrift-grouping是否被序列化过

没有就可以直接读出object, 否则需要反序列成object

1.8 direct-grouping

producer of the tuple decides which task of the consumer will receive this tuple.

Direct groupings can only be declared on streams that have been declared as direct streams.

这里直接返回:direct, 因为direct-grouping, 发送到哪个tasks, 是由producer产生tuple的时候已经决定了, 所以这里不需要做任何grouping相关工作 
 

2 stream->component->grouper

outbound-components

一个executor只会对应于一个component, 所以给出当前executor的component-id

getTargets, 可以得出所有outbound components, [streamid, [target-componentid, grouping]]

调用outbound-groupings,

最终返回[streamid [component grouper]]的hashmap, 并赋值给executor-data中的stream->component->grouper

task在最终发送message的时候, 就会通过stream->component->grouper来产生真正的target tasks list

(defn outbound-components
"Returns map of stream id to component id to grouper"
[^WorkerTopologyContext worker-context component-id]
(->> (.getTargets worker-context component-id) ;;[streamid, [target-componentid, grouping]]
clojurify-structure
(map (fn [[stream-id component->grouping]]
[stream-id
(outbound-groupings
worker-context
component-id
stream-id
(.getComponentOutputFields worker-context component-id stream-id)
component->grouping)]))
(into {})
(HashMap.)))

 

outbound-groupings

对每个task不为空的target component调用mk-grouper

mk-grouper返回的是grouper fn, 所以, 最终的返回, [component, grouper]

(defn- outbound-groupings [^WorkerTopologyContext worker-context this-component-id stream-id out-fields component->grouping]
(->> component->grouping
(filter-key #(-> worker-context ;;component对应的tasks不为0
(.getComponentTasks %)
count
pos?))
(map (fn [[component tgrouping]]
[component
(mk-grouper worker-context
this-component-id
stream-id
out-fields
tgrouping
(.getComponentTasks worker-context component)
)]))
(into {})
(HashMap.)))

Storm-源码分析-Streaming Grouping (backtype.storm.daemon.executor)的更多相关文章

  1. Storm源码分析--Nimbus-data

    nimbus-datastorm-core/backtype/storm/nimbus.clj (defn nimbus-data [conf inimbus] (let [forced-schedu ...

  2. JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler

    EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSche ...

  3. JStorm与Storm源码分析(三)--Scheduler,调度器

    Scheduler作为Storm的调度器,负责为Topology分配可用资源. Storm提供了IScheduler接口,用户可以通过实现该接口来自定义Scheduler. 其定义如下: public ...

  4. JStorm与Storm源码分析(二)--任务分配,assignment

    mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: ;;参数nimbus为nimb ...

  5. JStorm与Storm源码分析(一)--nimbus-data

    Nimbus里定义了一些共享数据结构,比如nimbus-data. nimbus-data结构里定义了很多公用的数据,请看下面代码: (defn nimbus-data [conf inimbus] ...

  6. storm源码分析之任务分配--task assignment

    在"storm源码分析之topology提交过程"一文最后,submitTopologyWithOpts函数调用了mk-assignments函数.该函数的主要功能就是进行topo ...

  7. storm源码分析之topology提交过程

    storm集群上运行的是一个个topology,一个topology是spouts和bolts组成的图.当我们开发完topology程序后将其打成jar包,然后在shell中执行storm jar x ...

  8. Nimbus<三>Storm源码分析--Nimbus启动过程

    Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus”来启动看下源码, 此处和上面client不同, jvmtype="-serv ...

  9. JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式

    本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式. 首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明 ...

随机推荐

  1. Oracle PLSQL Demo - 01.定义变量、打印信息

    declare v_sal ) :; begin --if you could not see the output in console, you should set output on firs ...

  2. Spring Boot干货系列:(二)配置文件解析

    Spring Boot干货系列:(二)配置文件解析 2017-02-28 嘟嘟MD 嘟爷java超神学堂   前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用“习惯优于 ...

  3. OpenLdap 相关命令

    相关命令: |-slapd 目录服务的主要程序|-slurpd 目录服务进行复制的程序|-slapadd 向目录中添加数据|-slapcat 把目录中的条目导出成ldif文件|-slapindex 重 ...

  4. R ggplot2 线性回归

    摘自  http://f.dataguru.cn/thread-278300-1-1.html library(ggplot2) x=1:10y=rnorm(10)a=data.frame(x= x, ...

  5. java原生序列化和Kryo序列化性能比较

    简介 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf,Thrift, ...

  6. dubbox-admin-2.8.4和dubbox-monitor安装

    一.安装zookeeper 安装过程参照以前写的一篇博客http://www.cnblogs.com/520playboy/p/6235415.html 二.dubbox 1.准备工作下载dubbox ...

  7. java方法——重载2

    什么是Java方法重载 方法重载的定义 1 对于同一个类,如果这个类里面有两个或者多个重名的方法,但是方法的参数个数.类型.顺序至少有一个不一样,这时候局构成方法重载. END 方法重载示例 1 pu ...

  8. BusyBox getty

    linux的登录主要是由两个文件在控制,/usr/sbin/getty来获得用户名,并进行检查用户名是否存在,然后将用户名传递给/usr/bin/login来获取用户输入密码和检查密码是否正确. 所以 ...

  9. java多线程有几种实现方法?线程之间如何同步

    java中多线程的实现方法有两种:1.直接继承thread类:2.实现runnable接口: 同步的实现方法有五种:1.同步方法:2.同步代码块:3.使用特殊域变量(volatile)实现线程同步:4 ...

  10. Elasticsearch 5

    Elasticsearch 5常见问题解决方案     安装运行 1.前置安装java8 jdk-8u112-linux-x64.rpm 下载地址:http://www.oracle.com/tech ...