Zeppelin 源码分析-独立解释器 JVM 相关分析(2)

和流相关的类有 InterpreterOutput,InterpreterResultMessageOutput,InterpreterOutputStream,InterpreterResultMessageOutputListener。这些类的对象虽然在主进程中也存在,但是其实是没什么作用的,因此这里直接以解释器 JVM 的角度来对这几个类进行说明。

InterpreterOutputListener 类

这个接口的三个方法都是用于向解释器 JVM 的消息队列中添加 Event(参见 Event 相关类,之后的解析中,Event 队列和消息队列是同义词),实现方式就是直接使用 RemoteInterpreterEventClient 类的对象( eventClient )向其中添加消息。一个比较重要的是实现类是 RemoteInterpreterServer 中的匿名内部类,这个匿名内部类的对象作为 InterpreterOutput 类构造函数的参数传入 InterpreterOutput 类中,成为了 InterpreterOutput 类的属性 flushListener:

protected InterpreterOutput createInterpreterOutput(final String noteId, final String paragraphId) {
  return new InterpreterOutput(new InterpreterOutputListener() {
    // 省略
    @Override
    public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
      String output = new String(line);
      logger.debug("Output Append: {}", output);
      eventClient.onInterpreterOutputAppend(
          noteId, paragraphId, index, output);
    }
    // 省略
  });
}

InterpreterResultMessageOutputListener 类

这个接口也是用于向解释器 JVM 的消息队列中添加消息,但是实现方式是调用 InterpreterOutputListener 接口中的方法,唯一的实现类是 InterpreterOutput 类中的匿名内部类,匿名内部类中的 flushListener 在上面说过,是 InterpreterOutput 类的属性也就是上述的 InterpreterOutputListener 类的对象,这个匿名内部类的对象作为 InterpreterResultMessageOutput 类构造函数的参数传入 InterpreterResultMessageOutput 类中,成为了 InterpreterResultMessageOutput 类的属性 flushListener

public InterpreterResultMessageOutputListener createInterpreterResultMessageOutputListener(
    final int index) {
  return new InterpreterResultMessageOutputListener() {
    final int idx = index;
    @Override
    public void onAppend(InterpreterResultMessageOutput out, byte[] line) {
      if (flushListener != null) {
        flushListener.onAppend(idx, out, line);
      }
    }
    @Override
    public void onUpdate(InterpreterResultMessageOutput out) {
      if (flushListener != null) {
        flushListener.onUpdate(idx, out);
      }
    }
  };
}

InterpreterResultMessageOutput 类

这个类是对一种返回结果的封装(Zeppelin 支持的所有返回类型有 TEXT, HTML, ANGULAR, TABLE, IMG, SVG, NULL),它本身也是 OutputStream 的子类。当有多种返回类型的时候 InterpreterOutput 类将每一种返回类型的输出生成一个 InterpreterResultMessageOutput 对象。实际上调用 InterpreterOutput 类写的方法时,实际写入的是这个流的对象,该对象的属性中 buffer 是写入时的缓存,outList 是存储流中所有类型的源的数组(包括文件源,url源等),watcher 是监控文件的改变的线程,但是目前该属性没有用到,flushListener 是 InterpreterResultMessageOutputListener 类对象,用于向消息队列中添加消息。而且到目前为止,只有调用 toByteArray 方法( toInterpreterResultMessage 方法也直接调用这个方法)才会将各种源合并到一起发送到消息队列,调用 flush 方法只会将 buffer 中内容发送到消息队列。

InterpreterOutput 类

这个类就是大家在调用 InterpreterContext.out() 或者 InterpreterContext.out 时返回的对象,这个类有一个 InterpreterResultMessageOutput 类对象的引用 currentOut ,用 currentOut 来存储当前返回类型的所有消息,而且该类中基本上所有的方法都会再次调用 currentOut 的相应方法,只不过中间额外加了一些处理,比如:往流里写数据时,这个类会找出 % 开头的行,然后 new 一个 InterpreterResultMessageOutput 对象,表明接下来返回的数据类型是另一种类型了,然后才会调用 new 出来那个对象的写方法,往那个对象中写数据。这个过程是在 setType 方法中完成的:

public void setType(InterpreterResult.Type type) throws IOException {
  InterpreterResultMessageOutput out = null;

  synchronized (resultMessageOutputs) {
    int index = resultMessageOutputs.size();
    InterpreterResultMessageOutputListener listener =
        createInterpreterResultMessageOutputListener(index);

    if (changeListener == null) {
      out = new InterpreterResultMessageOutput(type, listener);
    } else {
      out = new InterpreterResultMessageOutput(type, listener, changeListener);
    }
    out.setResourceSearchPaths(resourceSearchPaths);

    buffer.reset();
    size = 0;

    if (currentOut != null) {
      currentOut.flush();
    }

    resultMessageOutputs.add(out);
    currentOut = out;
  }
}

现在再来从头看一下,当调用 InterpreterContext.out.write 方法时,究竟发生了什么(理想情况下,那些判断分支也很简单,一看就懂了,这里不再赘述)。理想情况下,当调用 InterpreterContext.out.write(b) 方法时:

  1. InterpreterOutput 类会先调用 getCurrentOutput 或者 getCurrentOutputForWriting 方法获得一个 InterpreterResultMessageOutput 对象并存储在该类的 currentOut 属性中( 两个方法返回的对象就是专门用来存储 b 对应的返回结果类型的所有消息,两种方法的区别就是前者直接返回了类之中的 currentOut 所引用的对象,后者是在开始写入一种新类型时由于类之中的 currentOut 是 null,因此会 new 一个新对象赋值给 currentOut 并返回)。
  2. 然后就是调用 1 返回的对象( currentOut 引用的对象 )的 write(b) 方法,然后这个 b 变量的内容就存储在了 currentOut 引用的对象的 buffer 中。
  3. 当你调用 InterpreterContext 的 flush 方法时,flush 方法中首先会调用 getCurrentOutput 方法获取 InterpreterContext 中 currentOut 引用的对象(类型为 InterpreterResultMessageOutput ),然后去调用这个 currentOut 引用的对象的 flush 方法,这个对象的 flush 方法调用了自身的 flushListener (前面说过这个 flushListener 的实际类型是 InterpreterResultMessageOutputListener )的相关方法,然后 InterpreterResultMessageOutputListener 又调用了自身的 flushListener 对象( 实际运行类型为 InterpreterOutputListener )的相关方法,最终调用了 InterpreterOutputListener 中的 eventClient 的相关方法将流的内容发送到消息队列中,消息队列在 Event 相关类中已经说明,这里不再赘述。

InterpreterOutputChangeWatcher

这个类其实没有用到,是监控文件变化的类,这里不说了。

LogOutputStream

InterpreterOutputStream

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值