Storm-源码分析- Messaging (backtype.storm.messaging)
先定义两个接口和一个类
TaskMessage类本身比较好理解, 抽象storm的message格式
对于IContext, 注释也说了, 定义messaging plugin, 通过什么渠道去发送message, storm这里设计成可替换的
默认定义storm实现了local和ZMQ两种plugin, 当然你可以实现更多的
local应该是用于local mode, 而ZMQ用于distributed mode
IContext接口主要是用于创建IConnection, 体现对socket的管理, 分别通过bind和connect定义服务器端和客户端的connection
IConnection接口主要用于定义, 真正收发message的逻辑
最终通过TransportFactory, 根据Config.STORM_MESSAGING_TRANSPORT的配置, 利用Java的reflection动态的创建不同类型的context
IContext接口
/**
* This interface needs to be implemented for messaging plugin.
*
* Messaging plugin is specified via Storm config parameter, storm.messaging.transport.
*
* A messaging plugin should have a default constructor and implements IContext interface.
* Upon construction, we will invoke IContext::prepare(storm_conf) to enable context to be configured
* according to storm configuration.
*/
public interface IContext {
/**
* This method is invoked at the startup of messaging plugin
* @param storm_conf storm configuration
*/
public void prepare(Map storm_conf); /**
* This method is invoked when a worker is unload a messaging plugin
*/
public void term(); /**
* This method establishes a server side connection
* @param storm_id topology ID
* @param port port #
* @return server side connection
*/
public IConnection bind(String storm_id, int port); /**
* This method establish a client side connection to a remote server
* @param storm_id topology ID
* @param host remote host
* @param port remote port
* @return client side connection
*/
public IConnection connect(String storm_id, String host, int port);
};
IConnection接口
public interface IConnection {
/**
* receive a message (consists taskId and payload)
* @param flags 0: block, 1: non-block
* @return
*/
public TaskMessage recv(int flags);
/**
* send a message with taskId and payload
* @param taskId task ID
* @param payload
*/
public void send(int taskId, byte[] payload);
/**
* close this connection
*/
public void close();
}
TaskMessage
TaskMessage如其名, 包含task和message字段, 以说明发送给哪个task的message
并且定义了序列化和反序列化的函数
public class TaskMessage {
private int _task;
private byte[] _message;
public TaskMessage(int task, byte[] message) {
_task = task;
_message = message;
}
public int task() {
return _task;
}
public byte[] message() {
return _message;
}
public ByteBuffer serialize() {
ByteBuffer bb = ByteBuffer.allocate(_message.length+2);
bb.putShort((short)_task);
bb.put(_message);
return bb;
}
public void deserialize(ByteBuffer packet) {
if (packet==null) return;
_task = packet.getShort();
_message = new byte[packet.limit()-2];
packet.get(_message);
}
}
TransportFactory
public class TransportFactory {
public static IContext makeContext(Map storm_conf) {
//get factory class name
String transport_plugin_klassName = (String)storm_conf.get(Config.STORM_MESSAGING_TRANSPORT);
LOG.info("Storm peer transport plugin:"+transport_plugin_klassName);
IContext transport = null;
try {
//create a factory class
Class klass = Class.forName(transport_plugin_klassName);
//obtain a context object
Object obj = klass.newInstance();
if (obj instanceof IContext) {
//case 1: plugin is a IContext class
transport = (IContext)obj;
//initialize with storm configuration
transport.prepare(storm_conf);
} else {
//case 2: Non-IContext plugin must have a makeContext(storm_conf) method that returns IContext object
Method method = klass.getMethod("makeContext", Map.class);
LOG.debug("object:"+obj+" method:"+method);
transport = (IContext) method.invoke(obj, storm_conf);
}
} catch(Exception e) {
throw new RuntimeException("Fail to construct messaging plugin from plugin "+transport_plugin_klassName, e);
}
return transport;
}
}
可以详细看看local和ZMQ的plugin的实现
Local
在local模式下使用的message plugin
实现比较简单, 所有都基于queues-map来实现, 这里的queue直接使用LinkedBlockingQueue, 因为local用于测试, 不用考虑高效性
所有的接收队列或发送队列都通过add-queue!加到queues-map里面(stormid+port作为key)
那么所有的recv和send, 都是基于queue的操作
(defn add-queue! [queues-map lock storm-id port]
(let [id (str storm-id "-" port)]
(locking lock
(when-not (contains? @queues-map id)
(swap! queues-map assoc id (LinkedBlockingQueue.))))
(@queues-map id)))
(deftype LocalConnection [storm-id port queues-map lock queue]
IConnection
(^TaskMessage recv [this ^int flags]
(when-not queue
(throw (IllegalArgumentException. "Cannot receive on this socket")))
(if (= flags 1)
(.poll queue)
(.take queue)))
(^void send [this ^int taskId ^bytes payload]
(let [send-queue (add-queue! queues-map lock storm-id port)]
(.put send-queue (TaskMessage. taskId payload))
))
(^void close [this]
)) (deftype LocalContext [^{:unsynchronized-mutable true} queues-map
^{:unsynchronized-mutable true} lock]
IContext
(^void prepare [this ^Map storm-conf]
(set! queues-map (atom {}))
(set! lock (Object.)))
(^IConnection bind [this ^String storm-id ^int port]
(LocalConnection. storm-id port queues-map lock (add-queue! queues-map lock storm-id port)))
(^IConnection connect [this ^String storm-id ^String host ^int port]
(LocalConnection. storm-id port queues-map lock nil))
(^void term [this]
))
这里使用Deftype, 而不是Defrecord, 即connection和context本身不需要对字典的支持
并且在IContext的实现中, 使用到了可变field, 据说是比较难用对的高级特性
我个人的理解, 是因为deftype和defrecord一样, 没有闭包的效果, 而只有field(对象成员)可以随时被接口函数访问, 所以有些场景下需要field的mutable, 比如这里的queues-map
之前类似的场景都是用reify实现的, 这里给出用deftype实现的版本
ZMQ
号称最快的消息队列, 接近socket API 的性能, 参考http://www.cnblogs.com/yjf512/archive/2012/03/03/2378024.html
在distributed mode时, storm使用ZMQ作为进程间和instrance间通信
(deftype ZMQConnection [socket]
IConnection
(^TaskMessage recv [this ^int flags]
(require 'backtype.storm.messaging.zmq)
(if-let [packet (mq/recv socket flags)]
(parse-packet packet)))
(^void send [this ^int taskId ^bytes payload]
(require 'backtype.storm.messaging.zmq)
(mq/send socket (mk-packet taskId payload) ZMQ/NOBLOCK)) ;; TODO: how to do backpressure if doing noblock?... need to only unblock if the target disappears
(^void close [this]
(.close socket)))
(deftype ZMQContext [^{:unsynchronized-mutable true} context
^{:unsynchronized-mutable true} linger-ms
^{:unsynchronized-mutable true} hwm
^{:unsynchronized-mutable true} local?]
IContext
(^void prepare [this ^Map storm-conf]
(let [num-threads (storm-conf ZMQ-THREADS)]
(set! context (mq/context num-threads))
(set! linger-ms (storm-conf ZMQ-LINGER-MILLIS))
(set! hwm (storm-conf ZMQ-HWM))
(set! local? (= (storm-conf STORM-CLUSTER-MODE) "local"))))
(^IConnection bind [this ^String storm-id ^int port]
(require 'backtype.storm.messaging.zmq)
(-> context
(mq/socket mq/pull)
(mq/set-hwm hwm)
(mq/bind (get-bind-zmq-url local? port))
mk-connection
))
(^IConnection connect [this ^String storm-id ^String host ^int port]
(require 'backtype.storm.messaging.zmq)
(-> context
(mq/socket mq/push)
(mq/set-hwm hwm)
(mq/set-linger linger-ms)
(mq/connect (get-connect-zmq-url local? host port))
mk-connection))
(^void term [this]
(.term context))
ZMQContextQuery
(zmq-context [this]
context))
Storm-源码分析- Messaging (backtype.storm.messaging)的更多相关文章
- Storm源码分析--Nimbus-data
nimbus-datastorm-core/backtype/storm/nimbus.clj (defn nimbus-data [conf inimbus] (let [forced-schedu ...
- JStorm与Storm源码分析(四)--均衡调度器,EvenScheduler
EvenScheduler同DefaultScheduler一样,同样实现了IScheduler接口, 由下面代码可以看出: (ns backtype.storm.scheduler.EvenSche ...
- JStorm与Storm源码分析(三)--Scheduler,调度器
Scheduler作为Storm的调度器,负责为Topology分配可用资源. Storm提供了IScheduler接口,用户可以通过实现该接口来自定义Scheduler. 其定义如下: public ...
- JStorm与Storm源码分析(二)--任务分配,assignment
mk-assignments主要功能就是产生Executor与节点+端口的对应关系,将Executor分配到某个节点的某个端口上,以及进行相应的调度处理.代码注释如下: ;;参数nimbus为nimb ...
- JStorm与Storm源码分析(一)--nimbus-data
Nimbus里定义了一些共享数据结构,比如nimbus-data. nimbus-data结构里定义了很多公用的数据,请看下面代码: (defn nimbus-data [conf inimbus] ...
- storm源码分析之任务分配--task assignment
在"storm源码分析之topology提交过程"一文最后,submitTopologyWithOpts函数调用了mk-assignments函数.该函数的主要功能就是进行topo ...
- Nimbus<三>Storm源码分析--Nimbus启动过程
Nimbus server, 首先从启动命令开始, 同样是使用storm命令"storm nimbus”来启动看下源码, 此处和上面client不同, jvmtype="-serv ...
- storm源码分析之topology提交过程
storm集群上运行的是一个个topology,一个topology是spouts和bolts组成的图.当我们开发完topology程序后将其打成jar包,然后在shell中执行storm jar x ...
- JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式
本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式. 首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明 ...
- Storm-源码分析- hook (backtype.storm.hooks)
task hook 在某些task事件发生时, 如果用户希望执行一些额外的逻辑, 就需要使用hook 当前定义如下事件, emit, cleanup, spoutAck-- 用户只需要开发实现ITas ...
随机推荐
- 【转】Elasticsearch5.0 安装问题集锦
Elasticsearch5.0 安装问题集锦 elasticsearch 5.0 安装过程中遇到了一些问题,通过查找资料几乎都解决掉了,这里简单记录一下 ,供以后查阅参考,也希望可以帮助遇到同样问题 ...
- 使用filter解决request.getParameter的中文乱码问题
注意:一般一个站点的所有页面的编码,包括数据库编码都要保持一致,下面默认的编码都是UTF-8 ----------------------------------例1:直接提交到jsp页面------ ...
- Ubuntu 文件文件夹查看权限和设置权限
ubuntu下查看权限的命令为: ls -l filename ls -ld folder ubuntu下设置权限的命令为: 一共有10位数 其中: 最前面那个 - 代表的是类型 中间那三个 rw- ...
- Android——手机尺寸相关的概念 +尺寸单位+关于颜色
手机的尺寸: 屏幕对角线的长度,单位为英寸(2.54cm) 手机的分辨率: 屏幕能显示的像素的数量, 一般用在长方向上数量*宽方向上数量来表达 手机的像素密度: pixels per inch,也称P ...
- maven编译插件版本配置案例
<!-- Build Settings 构建设置 --> <build> <finalName>${project.artifactId}</finalNam ...
- 深度剖析ConcurrentHashMap(转)
概述 还记得大学快毕业的时候要准备找工作了,然后就看各种面试相关的书籍,还记得很多面试书中都说到: HashMap是非线程安全的,HashTable是线程安全的. 那个时候没怎么写Java代码,所以根 ...
- LVM简介
3. 创建VG.. 7 4. 创建LV.. 9 5.LV格式化及挂载... 10 一.LVM简介 LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauel ...
- Hive三种不同的数据导出的方式
转自:http://blog.chinaunix.net/uid-27177626-id-4653808.html Hive三种不同的数据导出的方式,根据导出的地方不一样,将这些方法分为三类:(1)导 ...
- 【BZOJ】1672: [Usaco2005 Dec]Cleaning Shifts 清理牛棚(dp/线段树)
http://www.lydsy.com/JudgeOnline/problem.php?id=1672 dp很好想,但是是n^2的..但是可以水过..(5s啊..) 按左端点排序后 f[i]表示取第 ...
- error C2678: 二进制“+”: 没有找到接受“const char [22]”类型的左操作数的运算符(或没有可接受的转换)没有与这些操作数匹配的“+”运算符
错误:没有与这些操作数匹配的“+”运算符,操作数类型为const char [20]+CString 分析原因:其提示操作数类型为const char [20]+CString 可见是类型有问题 故加 ...