本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式。 
首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明了以下3个抽象方法用来约束ISpoutOutputCollector的实现类。接口定义与方法说明如下:

/**
* ISpoutOutputCollector:Spout输出收集器接口
*/
public interface ISpoutOutputCollector {
/**
* 改方法用来向外发送数据,它的返回值是该消息所有发送目标的taskID集合;
* 参数:
* streamId:消息Tuple将要被输出到的流
* tuple:要输出的消息,是一个Object列表
* messageId:输出消息的标记信息,如果messageId被设置为null,则Storm不会追踪该消息,
* 否则它会被用来追踪所发出的消息处理情况
*/
List<Integer> emit(String streamId, List<Object> tuple, Object messageId);
/**
* 该方法与上面emit方法类似,区别在于:
* 1.数据(消息)只由所指定taskId的Task接收;(这就意味着如果没有下游节点接收该消息,则该消息就没有被真正发送)
* 2.该方法要求参数streamId所对应的流必须为直接流,接收端的Task必须以直接分组的方式来接收消息,
* 否则会抛出异常.
*/
void emitDirect(int taskId, String streamId, List<Object> tuple, Object messageId);
/**
* 用来处理异常
*/
void reportError(Throwable error);
}

Storm提供了接口ISpoutOutputCollector的默认类SpoutOutputCollector,这个类实际上是一个代理类,该类持有一个ISpoutOutputCollector类型的对象,所有的操作实际上都过该对象来实现的。SpoutOutputCollector定义如下:

public class SpoutOutputCollector implements ISpoutOutputCollector {
/**
* 持有SpoutOutputCollector要代理的对象
*/
ISpoutOutputCollector _delegate; public SpoutOutputCollector(ISpoutOutputCollector delegate) {
_delegate = delegate;
}
/**
* 实现了接口中的emit方法,并且提供了它的几个重载方法
* eg.如果不指定streamId,默认使用default,如果不指定messageId,则默认使用空(null)
*/
public List<Integer> emit(String streamId, List<Object> tuple, Object messageId){
return _delegate.emit(streamId, tuple, messageId);
} public List<Integer> emit(List<Object> tuple, Object messageId) {
return emit(Utils.DEFAULT_STREAM_ID, tuple, messageId);
} public List<Integer> emit(List<Object> tuple) {
return emit(tuple, null);
} public List<Integer> emit(String streamId, List<Object> tuple) {
return emit(streamId, tuple, null);
}
/**
* 实现了接口中的emitDirect方法,同时也提供了几个重载方法,与上面emit方法一致.
*/
public void emitDirect(int taskId, String streamId, List<Object> tuple, Object messageId) {
_delegate.emitDirect(taskId, streamId, tuple, messageId);
} public void emitDirect(int taskId, List<Object> tuple, Object messageId) {
emitDirect(taskId, Utils.DEFAULT_STREAM_ID, tuple, messageId);
} public void emitDirect(int taskId, String streamId, List<Object> tuple) {
emitDirect(taskId, streamId, tuple, null);
} public void emitDirect(int taskId, List<Object> tuple) {
emitDirect(taskId, tuple, null);
}
/**
* 处理异常方法的实现
*/
@Override
public void reportError(Throwable error) {
_delegate.reportError(error);
}
}

PS: 
代理模式主要分为两种:静态代理和动态代理

静态代理: 
在程序运行前代理类与委托类的关系在运行前就确定,即在程序运行前就已经存在代理类的字节码文件了. 
代理模式角色: 
Subject(抽象主题角色):可以是抽象类也可以是接口,声明了被委托角色和委托类共有的处理方法; 
RealSubject(具体主题角色):又称被委托角色、被代理角色,是业务逻辑的具体执行者; 
ProxySubject(代理主题角色):又称委托类、代理类,负责对真实角色的应用, 
把所有抽象主题类定义的方法限制委托给具体主题角色来实现,并且在具体主题角色处理完毕前后做预处理和善后处理.

静态代理模式案例如下:

//抽象主题
public interface Subject {
public void process(String taskName);
}

被代理角色:

public class RealSubject implements Subject {
@Override
public void process(String taskName) {
System.out.println("正在执行任务:"+taskName);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

代理类:

public class ProxySubject implements Subject {
//代理类持有一个委托类的对象引用
private Subject delegate;
public ProxySubject(Subject delegate){
this.delegate=delegate;
}
@Override
public void process(String taskName) {
//预处理
this.before();
//将请求分派给委托类处理
delegate.process(taskName);
//善后处理
this.after();
}
private void before(){
System.out.println("预处理!");
}
private void after(){
System.out.println("善后处理!");
}
}

案例测试:

public class Test {
public static void main(String[] args) {
RealSubject subject = new RealSubject();
ProxySubject p = new ProxySubject(subject);
p.process("排水");
}
}

测试结果:

预处理!
正在执行任务:排水
善后处理!

静态代理类的优缺点: 
优点: 
业务类只需关注业务逻辑本身,这样就保证了业务类的重用性. 
缺点: 
代理对象的一个接口只服务于一种类型的对象.当要代理的方法很多,就要为每一种方法进行代理。因此静态代理在程序规模变大时就无法很好地胜任工作了.

动态代理: 
代理类和委托类的关系在程序运行时才确定的.动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成,所以不存在代理类的字节码文件.

动态代理模式案例如下:

public interface Service {
//目标方法
public void process();
}
public class UserServiceImpl implements Service {
@Override
public void process() {
System.out.println("用户service处理");
}
}

动态代理实现实例:

public class MyInvocatioHandler implements InvocationHandler {
private Object target; public MyInvocatioHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//System.out.println("-----before-----");
this.before();
Object result = method.invoke(target, args);
// System.out.println("-----end-----");
this.after();
return result;
}
// 生成代理对象
public Object getProxy() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
} private void before(){
System.out.println("预处理!");
}
private void after(){
System.out.println("善后处理!");
}
}

案列测试:

public class ProxyTest {
public static void main(String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHandler(service);
Service serviceProxy = (Service)handler.getProxy();
serviceProxy.process();
}
}

测试结果:

预处理!
用户service处理
善后处理!

动态代理的优缺点: 
优点: 
接口中的所有方法都被转移到调用处理器一个集中的方法中在方法“运行时”动态的加入,决定你是什么类型,较灵活 
缺点: 
1. 与静态代理相比,效率降低了 
2. JDK动态代理只能对实现了接口的类进行代理

欢迎关注下面二维码进行技术交流:

JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式的更多相关文章

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

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

  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. Storm源码分析--Nimbus-data

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

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

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

  7. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  8. ABP源码分析五:ABP初始化全过程

    ABP在初始化阶段做了哪些操作,前面的四篇文章大致描述了一下. 为个更清楚的描述其脉络,做了张流程图以辅助说明.其中每一步都涉及很多细节,难以在一张图中全部表现出来.每一步的细节(会涉及到较多接口,类 ...

  9. MPTCP 源码分析(五) 接收端窗口值

    简述:      在TCP协议中影响数据发送的三个因素分别为:发送端窗口值.接收端窗口值和拥塞窗口值. 本文主要分析MPTCP中各个子路径对接收端窗口值rcv_wnd的处理.   接收端窗口值的初始化 ...

随机推荐

  1. Elasticsearch重要配置

    虽然Elasticsearch需要很少的配置,但是有一些设置需要手动配置,并且必须在进入生产之前进行配置. path.data  and path.logs cluster.name node.nam ...

  2. 使用 bufferedreader 的好处

    简单的说,一次IO操作,读取一个字节也是读取,读取8k个字节也是读取,两者花费时间相差不多.而一次IO的来回操作却要耗费大量时间.好比是一辆大型汽车(设装100人),要去车站接人到公司,接一个人也是接 ...

  3. [转] .NET领域驱动设计—初尝(原则、工具、过程、框架)

    阅读目录: 1.原则 1.1.精简聚合 1.2.分离用例与接口功能(设计模式的用武之地) 2.工具.框架.组件 3.过程 1]原则 原则对于任何一项技术实现来说都是至关重要的,在设计某一个系统功能的时 ...

  4. centos ios镜像文件 安装详细

    1.挂载iOS镜像(先打开VM 选择虚拟机---->设置-->CD ---->使用ISO镜像文件 用浏览打开) 2.开始界面选择 3出现下面的界面 这是提示你是否扫描文件的完整性 我 ...

  5. VMware安装CentOS 提示:已将该虚拟机配置为使用 64 位客户机操作系统。但是,无法执行 64 位操作。解决方案

    安装虚拟机遇到错误: 在网上查了查资料,发现CPU支持VT技术的就能支持vmware中安装64位虚拟机. 以下是操作步骤: 1)到网上下载一个securable.exe,测试以下机器是否支持VT. l ...

  6. 点击页面其它地方隐藏该div的方法

    思路一 第一种思路分两步 第一步:对document的click事件绑定事件处理程序,使其隐藏该div 第二步:对div的click事件绑定事件处理程序,阻止事件冒泡,防止其冒泡到document,而 ...

  7. ecshop商品页增加编辑器fckeditor

    最近在做ecshop的项目,需要在商品单页中增加一项FCKEditor的文本编辑器,但在ecshop的论坛和百度里搜出的方法,试了好几个都没有用,终于找到一个可以正确使用的,和大家分享. ecshop ...

  8. 数据的ID名生成新的引用索引树

    <?php $arr= [ '0'=>[ "id"=>2, "name"=>"建材", "pid" ...

  9. Streaming输入输出

    Structured Streaming 输入输出 输入 SparkSession.readStream() 返回一个 DataStreamReader 接口对象,可以通过该对象对输入源进行参数配置, ...

  10. ==,=和equals()区别

    equals和=,==的区别   一. ==和equals的区别 1. ==是运算符 2. equals是String对象的方法 一般有两种类型的比较 1. 基本数据类型的比较 2. 引用对象的比较 ...