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

Rx java 如何在Apache NiFi onTrigger方法中使用RxJava?,rx-java,apache-nifi,Rx Java,Apache Nifi,如何让RxJava在NiFi中工作??或者如何让NiFi和RxJava玩得更好?它们看起来是如此完美的互补 我遇到了一个我不知道如何解决的问题。NiFi不断抱怨IllegalStateException或FlowFileHandlingException,这取决于我在哪里以及如何从FlowFile输入流读取数据的方法 我正在学习ApacheNIFI和RxJava2(即Flowables)。我想创建一个ApacheNIFI处理器,它的操作与现有的SplitText处理器类似——只是更简单。没有标题

如何让RxJava在NiFi中工作??或者如何让NiFi和RxJava玩得更好?它们看起来是如此完美的互补

我遇到了一个我不知道如何解决的问题。NiFi不断抱怨
IllegalStateException
FlowFileHandlingException
,这取决于我在哪里以及如何从FlowFile输入流读取数据的方法

我正在学习ApacheNIFI和RxJava2(即Flowables)。我想创建一个ApacheNIFI处理器,它的操作与现有的SplitText处理器类似——只是更简单。没有标题处理,没有片段大小处理——只需拉出每一行数据——我称之为SplitLine

这里没有什么奇特的线程——这意味着我不想用Flowable.observeOn()或Flowable.subscribeOn()做任何事情。一切都应该在一个线程上完成…当前线程

我想我会用RxJava解决这个问题。我将从流文件中读取字符,并使用移位的缓冲区发布它们;例如

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();
我也尝试过使用回调版本,但不出意外地收到一个异常,因为流是从回调而不是读取回调访问的。那就是

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

    // RxLineSplitter contains the code above which is the other callback it is complaining about... 
}
为什么我要发布字符流?为什么是成对的?我在char流上有两个订阅者。一个查找行首,另一个查找行尾。由于Windows的原因,我需要查找[\r;\n;或\r\n]中的一个。基本上,该对中的第二个字符是一个前瞻字符

如果你感兴趣的话,我的RxSplitLine的核心看起来像

Flowable<Tuple<Long, Integer>> findLineMarkers(
    Flowable<List<Tuple<Long, Integer>>> charPairs, 
    BiFunction<Tuple<Long, Integer>, Optional<Tuple<Long, Integer>>, Optional<Tuple<Long, Integer>>> strategy) { 

    return charPairs().map(pair -> {
            Tuple<Long, Integer> fst = pair.get(0);
            Optional<Tuple<Long, Integer>> snd = pair.size() > 1 ? Optional.of(pair.get(1)) : Optional.empty();

            return strategy.apply(fst, snd);
    }).filter(Optional::isPresent).map(Optional::get);
}

Flowable<SplitInfo> split(InputStream stream) throws IOException {

    return findLineMarkers(stream, startingPositionStrategy)
               .zipWith(findLineMarkers(stream, endingPositionStrategy), 
                        (s, e) -> new Split(s.item1, e.item1 - s.item1))
               .filter(split -> !removeEmptyLines || split.length > 0)
               .zipWith(counter(), Tuple::new)
               .timeInterval(TimeUnit.MILLISECONDS)
               .map(x -> new SplitInfo(x.value().item1.start,
                                       x.value().item1.length, 
                                       x.value().item2,
                                       x.time(), x.unit()));
}
可流动findLineMarkers(
可流动碳对,
双功能策略{
返回charPairs().map(对->{
Tuple fst=pair.get(0);
Optional snd=pair.size()>1?Optional.of(pair.get(1)):Optional.empty();
返回策略。应用(fst、snd);
}).filter(可选::isPresent).map(可选::get);
}
可流动拆分(InputStream)引发IOException{
返回findLineMarkers(流、开始位置策略)
.zipWith(findLineMarkers(流、结束位置策略),
(s,e)->新拆分(s.item1,e.item1-s.item1))
.filter(split->!removeMptyline | | split.length>0)
.zipWith(计数器(),元组::新建)
.timeInterval(时间单位为毫秒)
.map(x->new SplitInfo(x.value().item1.start,
x、 value().item1.length,
x、 value().item2,
x、 时间(),x.单位();
}

别胡扯了。。。我将非常感谢在让NiFi和RxJava 2友好相处方面提供的任何帮助或建议。

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

如果您计划在正常的
InputStreamCallback
之外读取或处理输入流,NiFi文档将指示您在
ProcessSession.read
上使用其他重载之一,具体地说是
InputStream input=session.read(flowFile)
。文档还声明您有责任正确关闭流。对于那些尝试这一点的人,我可能会补充尽可能快地关闭流

在RxJava2中,这意味着我的
Flowable.create
方法很接近,但还不够。您需要将一个
可流动。在
可流动的周围使用
。创建
。下面是我修改过的构造函数和有效的方法

需要注意的几个要点:

您可能会尝试将
ProcessSession
传递给
Flowable中的
resourceSupplier
并将其用于
。。。这让我头疼不已,ymmv,我不推荐(但如果你能找到办法,请告诉我)

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

RxLineSplitter(InputStream输入,布尔RemoveEmptyline){
this.inputStream=输入;
this.removemptylines=removemptylines;
}
私有可流动getCharacters(){
流动炭=
流动(
()->这个.inputStream,
输入->可流动。创建(发射器->{
试一试{
长cnt=0;
while(true){
int ch=input.read();
如果(isEOF.试验(ch))断裂;
onNext(新元组(cnt,ch));
++碳纳米管;
}
emitter.onComplete();
}捕获(例外情况除外){
发射极误差(ex);
}
},背压等级缓冲器),
InputStream::close,
正确的);
返回字符缓冲区(2,1);
}
最后的想法:

  • 我喜欢支持RxLineSplitter类对NiFi没有依赖性。减少耦合

  • 我不喜欢NiFi Processor.onTrigger方法获取InputStream,但需要RxLineSplitter关闭和处置。文档中对此进行了一些讨论,但我觉得它肮脏且容易出错。为了缓解上述问题,InputStream只在一种方法中使用,并使用
    Flowable.using
    以非常明显和清晰的方式进行清理


希望这对其他人有帮助。。。是时候看看我在使用NiFi和Rx时遇到的其他[学习]障碍了。

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

如果您计划读取或处理no之外的输入流
session.read(flowFile, stream -> { 
    RxLineSplitter splitter = new RxLineSplitter(stream);

    // RxLineSplitter contains the code above which is the other callback it is complaining about... 
}
Flowable<Tuple<Long, Integer>> findLineMarkers(
    Flowable<List<Tuple<Long, Integer>>> charPairs, 
    BiFunction<Tuple<Long, Integer>, Optional<Tuple<Long, Integer>>, Optional<Tuple<Long, Integer>>> strategy) { 

    return charPairs().map(pair -> {
            Tuple<Long, Integer> fst = pair.get(0);
            Optional<Tuple<Long, Integer>> snd = pair.size() > 1 ? Optional.of(pair.get(1)) : Optional.empty();

            return strategy.apply(fst, snd);
    }).filter(Optional::isPresent).map(Optional::get);
}

Flowable<SplitInfo> split(InputStream stream) throws IOException {

    return findLineMarkers(stream, startingPositionStrategy)
               .zipWith(findLineMarkers(stream, endingPositionStrategy), 
                        (s, e) -> new Split(s.item1, e.item1 - s.item1))
               .filter(split -> !removeEmptyLines || split.length > 0)
               .zipWith(counter(), Tuple::new)
               .timeInterval(TimeUnit.MILLISECONDS)
               .map(x -> new SplitInfo(x.value().item1.start,
                                       x.value().item1.length, 
                                       x.value().item2,
                                       x.time(), x.unit()));
}
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);
}