如何让RxJava在NiFi中工作?或者我如何让NiFi和RxJava玩得很好?它们似乎是彼此完美的补充。
我遇到了一个我无法弄清楚如何解决的问题。 NiFi一直在抱怨IllegalStateException
或FlowFileHandlingException
,这取决于我从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彼此玩得很好的任何帮助或指示。
答案 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。