如何在Apache NiFi onTrigger方法中使用RxJava?

时间:2017-04-06 02:38:32

标签: rx-java apache-nifi

如何让RxJava在NiFi中工作?或者我如何让NiFi和RxJava玩得很好?它们似乎是彼此完美的补充。

我遇到了一个我无法弄清楚如何解决的问题。 NiFi一直在抱怨IllegalStateExceptionFlowFileHandlingException,这取决于我从FlowFile输入流中读取的位置和方式。

我正在学习Apache NiFi和RxJava 2(即Flowables)。我想创建一个类似于现有SplitText处理器的Apache NiFi处理器 - 只是更简单。没有头处理,没有片段大小处理 - 只需拔出每一行数据 - 我称之为SplitLine。

这里没有花哨的线程 - 这意味着我没有尝试使用Flowable.observeOn()或Flowable.subscribeOn()做任何事情。一切都应该在一个线程上完成......当前的线程。

我以为我会用RxJava来解决这个问题。我会读取FlowFile中的字符并使用移位缓冲区发布它们;例如......

Flowable<Tuple<Long, Integer>> chars = 
    Flowable.generate(
        () -> 0L,
        (cnt, emitter) -> { 
            int ch = flowStream.read();
            emitter.onNext(new Tuple<>(cnt, ch);
            if (ch == -1) emitter.onComplete();
            return cnt++;
        });

 return chars.buffer(2, 1).publish().autoConnect();

我还尝试使用Flowable.create ...

等效
Flowable<Tuple<Long, Integer>> chars = 
    Flowable.create(emitter -> { 
        try {
            int ch;
            long cnt = 0;
            while ((ch = flowStream.read()) != -1) {
                emitter.onNext(new Tuple<>(cnt, ch);
                cnt++;
            }
            emitter.onComplete();
        } catch (IOException ex) { 
            ex.printStackTrace();
            emitter.onError(ex);
        } finally { 
            flowStream.close();
        }
    }, BackpressureStrategy.BUFFER);

return chars.buffer(2, 1).publish().autoConnect();

在上述情况中,我在处理器类的重写InputStream方法中传递了来自NiFi ProcessSession的{​​{1}}。

onTrigger

我也尝试过使用回调版本,但不出所料地收到了一个异常,因为该流是从回调以外的回调访问的。那是......

InputStream stream = session.read(flowFile)
RxLineSplitter splitter = new RxLineSplitter(stream);

为什么我要发布char流?为什么在成对的角色?我在char流上有两个订阅者。一个查找一行的开头,另一个查找一行的结尾。因为Windows,我需要找一个[\ r; \ N;或\ r \ n)]。基本上,这对中的第二个字符是预测。

如果您有兴趣,我的RxSplitLine的症结就像......

session.read(flowFile, stream -> { 
    RxLineSplitter splitter = new RxLineSplitter(stream);

    // RxLineSplitter contains the code above which is the other callback it is complaining about... 
}

足够漫无目的...我很感激能让NiFi和RxJava 2彼此玩得很好的任何帮助或指示。

1 个答案:

答案 0 :(得分:0)

我相信我找到了答案......至少我的SplitLine处理器显示它已收到流文件,读取的字节也是准确的!

如果您计划阅读或使用正常InputStreamCallback之外的输入流执行某些操作,则NiFi文档会指示您使用ProcessSession.read专门InputStream input = session.read(flowFile)上的其他重载之一。文档还声明您负责正确关闭流。对于那些尝试这一点的人,我可以添加... 尽可能快地和急切地关闭流

在RxJava2中,这意味着我的Flowable.create方法很接近,但还不够。您需要在Flowable.using周围打Flowable.create。下面是我修改过的构造函数和方法......

  

要注意几点:

     

您可能想要传递ProcessSession并将其用于resourceSupplier中的Flowable.using ...这给我带来了无数的麻烦,ymmv,我不知道推荐它(但如果你找到方法,请告诉我。)

     

我使用Flowable.using重载,允许您指定eager参数。我将我设置为true以急切关闭/处置资源(InputStream)。

RxLineSplitter(InputStream input, boolean removeEmptyLines) {

    this.inputStream = input;
    this.removeEmptyLines = removeEmptyLines;
}

private Flowable<List<Tuple<Long, Integer>>> getCharacters() {

    Flowable<Tuple<Long, Integer>> chars =
        Flowable.using(
            () -> this.inputStream,
            input -> Flowable.create(emitter -> {

                try {
                    long cnt = 0;
                    while (true) {
                        int ch = input.read();
                        if (isEOF.test(ch)) break;
                        emitter.onNext(new Tuple<>(cnt, ch));
                        ++cnt;
                    }
                    emitter.onComplete();

                } catch (Exception ex) {
                    emitter.onError(ex);
                }

            }, BackpressureStrategy.BUFFER),
            InputStream::close,
            true);

    return chars.buffer(2, 1);
}

最后的想法:

  • 我喜欢支持RxLineSplitter类与NiFi没有依赖关系。减少耦合。

  • 我不喜欢NiFi Processor.onTrigger方法获取InputStream,但需要RxLineSplitter关闭&amp;处置。这在文档中有一点讨论,但它感觉很脏并且容易出错。为了减轻上述情况,InputStream仅在一种方法中使用,并使用Flowable.using以非常明显和清晰的方式进行清理。

希望这有助于其他人...时间看看我遇到的其他[学习]障碍与NiFi和Rx。