本文初次发表于storm-cn的google groups中,现以blog的方式再次发表,表明本人徽沪一郎确实读过这些代码,:).

Bolt作为task被executor执行,而executor是一个个的线程,所以executor必须存在于具体的process之中,而这个process就是worker。至于worker是如何被supervisor创建,尔后worker又如何创建executor线程,这些暂且按下不表。

 
假设同属于一个Topology的Spout与Bolt分别处于不同的JVM,即不同的worker中,不同的JVM可能处于同一台物理机器,也可能处于不同的物理机器中。为了让情景简单,认为JVM处于不同的物理机器中。
 
Spout的输出消息到达Bolt,作为Bolt的输入会经过这么几个阶段。
 
1. spout的输出通过该spout所处worker的消息输出线程,将tuple输入到Bolt所属的worker。它们之间的通路是socket连接,用ZeroMQ实现。
2. bolt所处的worker有一个专门处理socket消息的receive thread 接收到spout发送来的tuple
3. receive thread将接收到的消息传送给对应的bolt所在的executor。 在worker内部(即同一process内部),消息传递使用的是Lmax Disruptor pattern.
4. executor接收到tuple之后,由event-handler进行处理
 
下面是具体的源码
1. worker创建消息接收线程 
 
worker.clj
 
(defn launch-receive-thread [worker]
  (log-message "Launching receive-thread for " (:assignment-id worker) ":" (:port worker))
  (msg-loader/launch-receive-thread!
    (:mq-context worker)
    (:storm-id worker)
    (:port worker)
    (:transfer-local-fn worker)
    (-> worker :storm-conf (get TOPOLOGY-RECEIVER-BUFFER-SIZE))
    :kill-fn (fn [t] (halt-process! 11))))
 
注意加亮的行会将storm.yaml中配置使用ZMQ或其它
storm.messaging.transport:"backtype.storm.messaging.zmq"
 
2. worker从socket接收到新消息
vthread (async-loop
                 (fn []
                   (let [socket (.bind ^IContext context storm-id port)]
                     (fn []
                       (let [batched (ArrayList.)
                             init (.recv ^IConnection socket 0)]
                         (loop [packet init]
                           (let [task (if packet (.task ^TaskMessage packet))
                                 message (if packet (.message ^TaskMessage packet))]
                             (if (= task -1)
                               (do (log-message "Receiving-thread:[" storm-id ", " port "] received shutdown notice")
                                 (.close socket)
                                 nil )
                               (do
                                 (when packet (.add batched [task message]))
                                 (if (and packet (< (.size batched) max-buffer-size))
                                   (recur (.recv ^IConnection socket 1))
                                   (do (transfer-local-fn batched)
                                     0 ))))))))))
 
加亮行使用的transfer-local-fn会将接收的TaskMessage传递给相应的executor
 
3. transfer-local-fn
 
(defn mk-transfer-local-fn [worker]
  (let [short-executor-receive-queue-map (:short-executor-receive-queue-map worker)
        task->short-executor (:task->short-executor worker)
        task-getter (comp #(get task->short-executor %) fast-first)]
    (fn [tuple-batch]
      (let [grouped (fast-group-by task-getter tuple-batch)]
        (fast-map-iter [[short-executor pairs] grouped]
          (let [q (short-executor-receive-queue-map short-executor)]
            (if q
              (disruptor/publish q pairs)
              (log-warn "Received invalid messages for unknown tasks. Dropping... ")
              )))))))
 
用disruptor在线程之间进行消息传递。
 
多费一句话,mk-transfer-local-fn表示将外部世界的消息传递给本进程内的线程。而mk-transfer-fn则刚好在方向上反过来。
 
4. 消息被executor处理
 
executor.clj
==========================================================
(defn mk-task-receiver [executor-data tuple-action-fn]
  (let [^KryoTupleDeserializer deserializer (:deserializer executor-data)
        task-ids (:task-ids executor-data)
        debug? (= true (-> executor-data :storm-conf (get TOPOLOGY-DEBUG)))
        ]
    (disruptor/clojure-handler
      (fn [tuple-batch sequence-id end-of-batch?]
        (fast-list-iter [[task-id msg] tuple-batch]
          (let [^TupleImpl tuple (if (instance? Tuple msg) msg (.deserialize deserializer msg))]
            (when debug? (log-message "Processing received message " tuple))
            (if task-id
              (tuple-action-fn task-id tuple)
              ;; null task ids are broadcast tuples
              (fast-list-iter [task-id task-ids]
                (tuple-action-fn task-id tuple)
                ))
            ))))))
 
加亮行中tuple-action-fn定义于mk-threads(源文件executor.clj)中。因为当前以Bolt为例,所以会调用的tuple-action-fn定义于defmethod mk-threads :bolt [executor-data task-datas]
 
那么mk-task-receiver是如何与disruptor关联起来的呢,可以见定义于mk-threads中的下述代码
(let [receive-queue (:receive-queue executor-data)
              event-handler (mk-task-receiver executor-data tuple-action-fn)]
          (disruptor/consumer-started! receive-queue)
          (fn []            
            (disruptor/consume-batch-when-available receive-queue event-handler)
            0)))
 
到了这里,消息的发送与接收处理路径打通。

Twitter Storm中Bolt消息传递路径之源码解读的更多相关文章

  1. go 中 sort 如何排序,源码解读

    sort 包源码解读 前言 如何使用 基本数据类型切片的排序 自定义 Less 排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源 ...

  2. Spark-1.6.0中的Sort Based Shuffle源码解读

    从Spark-1.2.0开始,Spark的Shuffle由Hash Based Shuffle升级成了Sort Based Shuffle.即Spark.shuffle.manager从Hash换成了 ...

  3. springBoot 日志中关于profiles设置的源码解读

    在启动SpringBoot应用是看到到如下日志,于是出于好奇查看了下源代码: 首先,StartpInfoLogger类,采用jcl-over-slf4j[即Apache Common Log]中的Lo ...

  4. Twitter Storm中Topology的状态

    Twitter Storm中Topology的状态 状态转换如下,Topology 的持久化状态包括: active, inactive, killed, rebalancing 四个状态. 代码上看 ...

  5. Handlebars模板引擎中的each嵌套及源码浅读

    若显示效果不佳,可移步到愚安的小窝 Handlebars模板引擎作为时下最流行的模板引擎之一,已然在开发中为我们提供了无数便利.作为一款无语义的模板引擎,Handlebars只提供极少的helper函 ...

  6. Mybatis源码解读-SpringBoot中配置加载和Mapper的生成

    本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...

  7. 【原】Spark中Job的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...

  8. eclipse中tomcat调试正确关联源码

    1.build path中jar包关联本地源码 2.tomcat中添加source关联工程lib下的jar包 以上两步即可. 可解决tomcat直接关联本地源码debug时无法计算表达式的情况. 错误 ...

  9. 动态语言切换(续)-designer中的retranslateUi(带源码)

    本站所有文章由本站和原作者保留一切权力,仅在保留本版权信息.原文链接.原文作者的情况下允许转载,转载请勿删改原文内容, 并不得用于商业用途. 谢谢合作.原文链接:动态语言切换(续)-designer中 ...

随机推荐

  1. (*p)++和*(p++)和*p++的区别

    * 和++优先级是同一级别,同一级别按照从右往左的顺序计算 所以:  *p++等价于*(p++) #define debug(x) cout << #x << " a ...

  2. 学习BigDecimal用法

    一.简介 Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算.双精度浮点型变量double可以处理16位有效数.在实际应用中,需要对更大或者更 ...

  3. Linux命令行下创建纳入版本控制下的新目录

    [root@ok 资料库]# svn mkdir test A test [root@ok 资料库]# svn ci -m "mkdir test" Adding 资料库/test ...

  4. 使用javascript的stack数据结构,实现进制转换

    function Stack() { var items = []; this.push = function(element){ items.push(element); } this.pop = ...

  5. PHP GBK UTF8互转

    function gbk_to_utf8($str){     return mb_convert_encoding($str, 'utf-8', 'gbk'); }   function utf8_ ...

  6. Java判断文件编码格式

    转自:http://blog.csdn.net/zhangzh332/article/details/6719025 一般情况下我们遇到的文件编码格式为GBK或者UTF-8.由于中文Windows默认 ...

  7. IIS-如果外网访问不到 域名

    如果访问不到 域名 , 可以 给域名的目录 增加“IIS_IUSERS”权限.

  8. ML 05、分类、标注与回归

    机器学习算法 原理.实现与实践 —— 分类.标注与回归 1. 分类问题 分类问题是监督学习的一个核心问题.在监督学习中,当输出变量$Y$取有限个离散值时,预测问题便成为分类问题. 监督学习从数据中学习 ...

  9. Python实现的粒子群优化算法

    01.from numpy import array 02.from random import random 03.from math import sin, sqrt 04. 05.iter_ma ...

  10. python 添加类属性

    类属性必须赋值. 创建类属性 类是模板,而实例则是根据类创建的对象. 绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有 ...