Part 2 - FlowFile I/O 和 Error Handling

flow File的IO

NiFi 的 Flow files 由两个主要部件组成:attributes 和 content. Attributes 是关于 content / flow file的元数据, 我们在Nifi组件脚本开发—ExecuteScript 使用指南(一)看到了如何使用 ExecuteScript 来操纵这个属性. flow file 的内容, 核心是一个 bytes集合,没有继承的 structure, schema, format, 等等. 不同的 NiFi processors 假定输入的 flow files 具有特定的 schema/format (或者从 attributes确定如 "mime.type" 或者通过其他的方法). 这些 processors 然后按照假定的格式对内容进行处理 (将返回 "failure" 到relationship,如果不是的话). 经常 processors 将输出  flow files 以特定的格式, 这在 processors' 描述中有相应的说明.

flow files 的 Input 和 Output (I/O) 通过 ProcessSession API 提供,通过 ExecuteScript  的"session" 变量来访问。一个机制是传递一个 callback 对象到session.read() 或 session.write()的调用。对于FlowFile将创建一个 InputStream 和/或 OutputStream, 这个callback 对象将被激活,使用相应的 callback 接口, 然后这个InputStream 和/或 OutputStream 的引用被传递到 callback函数使用. 这里有三个 callback 接口, 每一个有自己的应用环境:

InputStreamCallback

这个 interface 用在 session.read( flowFile, inputStreamCallback) 方法中,提供一个 InputStream,用于读取 flow file的内容. 该 interface 有一个单一方法:

void process(InputStream in) throws IOException

该 interface 提供一个被管理的 input stream. 这个input stream自动打开和关闭,也可以手动关闭. 这是从 flow file读取的方法, 并且不能被写回去。

OutputStreamCallback

该 interface 被用于session.write( flowFile, outputStreamCallback) 方法,提供 OutputStream写入内容到 flow file. 该 interface 具有单一的方法:

void process(OutputStream out) throws IOException

该 interface 提供被管理的 output stream. 这个output stream 被自动打开和关闭,也可以手动关闭。 - 重要的一点是,如果任何 streams 包装了这个 streams,所有打开的资源应该被清理.

例如, 在ExecuteScript中被创建数据 , 来自于外部文件, 而不是一个 flow file. 然后你可以使用 session.create() 去创建一个新的FlowFile, 然后 session.write( flowFile, outputStreamCallback) 用于添加内容.

StreamCallback

该 interface 用于 session.write( flowFile, streamCallback) 方法,提供 InputStream 和 OutputStream,为 flow file提供内容的读取和写入. 该 interface 有一个单一的方法:

void process(InputStream in, OutputStream out) throws IOException

该 interface 提供被管理的 output stream. 这个output stream 被自动打开和关闭,也可以手动关闭。 - 重要的一点是,如果任何 streams 包装了这个 streams,所有打开的资源应该被清理.

因为这些  callbacks 是 Java objects, 脚本将创建一个并且传入 session 方法, 下面的方法将使用不同的脚本语言进行演示. 并且,这里还有其他的读写 flow files方法, 包括:

  • 使用 session.read(flowFile) 返回 InputStream. 取代 InputStreamCallback, 将返回 InputStream 用于读取. 你必须 (close, e.g.) 手动管理 InputStream.
  • 使用 session.importFrom(inputStreamflowFile) 从 InputStream 写入到 FlowFile. 这将替代 借助OutputStreamCallback的session.write() 的使用.

从 flow file 中读取数据

需求:传入连接执行 ExecuteScript ,并且从队列中得到 flow file 的内容进行处理.

方法:使用session的read(flowFileinputStreamCallback) 方法。一个 InputStreamCallback 对象需要被传入 read() 方法. 注意到,因为InputStreamCallback 是一个对象, 内容只在该对象中可见。 如果你需要在 read() 方法之外访问, 需要使用更为全局化的变量. 这里的例子讲来自flow file的全部内容存储到 String (使用 Apache Commons' IOUtils class)。

注意: 对于大的 flow files, 这并不是最好的技术方法; 应该只读取需要的数据,并按照适应的方法处理。比如 SplitText, 你应该一次读一行并且在 InputStreamCallback中处理, 或者 session.read(flowFile) 方法 得到 InputStream 的引用,从而在 callback之外处理.

例子

Groovy:

import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets flowFile = session.get() if(!flowFile)return def text = ''
// Cast a closure with an inputStream parameter to InputStreamCallback
session.read(flowFile, {inputStream ->
text = IOUtils.toString(inputStream, StandardCharsets.UTF_8) // Do something with text here
} as InputStreamCallback)

Jython:

from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from org.apache.nifi.processor.io import InputStreamCallback # Define a subclass of InputStreamCallback for use in session.read()
class PyInputStreamCallback(InputStreamCallback):
def __init__(self):
pass
def process(self, inputStream):
text = IOUtils.toString(inputStream, StandardCharsets.UTF_8) # Do something with text here
# end class flowFile = session.get()
if(flowFile != None):
session.read(flowFile, PyInputStreamCallback())
# implicit return at the end

Javascript:

var InputStreamCallback = Java.type("org.apache.nifi.processor.io.InputStreamCallback")
var IOUtils = Java.type("org.apache.commons.io.IOUtils")
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets") var flowFile = session.get(); if(flowFile != null) {
// Create a new InputStreamCallback, passing in a function to define the interface method
session.read(flowFile,new InputStreamCallback(function(inputStream) {
var text = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
// Do something with text here
}));
}

JRuby:

java_import org.apache.commons.io.IOUtils
java_import org.apache.nifi.processor.io.InputStreamCallback # Define a subclass of InputStreamCallback for use in session.read()
class JRubyInputStreamCallback
include InputStreamCallback def process(inputStream)
text = IOUtils.toString(inputStream)
# Do something with text here
end
end jrubyInputStreamCallback = JRubyInputStreamCallback.new
flowFile = session.get()
if flowFile != nil
session.read(flowFile, jrubyInputStreamCallback)
end

写入数据至 flow file

需求:为输出的 flow file创建内容.

方法:使用session的write(flowFileoutputStreamCallback) 方法。一个OutputStreamCallback 对象需要传递给 write() 方法. 注意,因为 OutputStreamCallback 是一个对象, 因此内容之灾对象内部可见. 如果你需要在 write() 方法之外访问, 使用更为全局化变量. 西面的例子写入 String 到 flowFile。

例子

Groovy:

import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets flowFile = session.get()
if(!flowFile) return def text = 'Hello world!'
// Cast a closure with an outputStream parameter to OutputStreamCallback
flowFile = session.write(flowFile, {outputStream ->
        outputStream.write(text.getBytes(StandardCharsets.UTF_8))
    } as OutputStreamCallback)

Jython:

from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from org.apache.nifi.processor.io import OutputStreamCallback # Define a subclass of OutputStreamCallback for use in session.write()
class PyOutputStreamCallback(OutputStreamCallback):
def __init__(self):
    pass def process(self, outputStream):
    outputStream.write(bytearray('Hello World!'.encode('utf-8')))
# end class flowFile = session.get() if(flowFile != None):
    flowFile = session.write(flowFile, PyOutputStreamCallback())
# implicit return at the end

Javascript:

var OutputStreamCallback = Java.type("org.apache.nifi.processor.io.OutputStreamCallback");
var IOUtils = Java.type("org.apache.commons.io.IOUtils");
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets");
var flowFile = session.get(); if(flowFile != null) {
// Create a new OutputStreamCallback, passing in a function to define the interface method
flowFile = session.write(flowFile,new OutputStreamCallback(function(outputStream) {
        outputStream.write("Hello World!".getBytes(StandardCharsets.UTF_8))
    }));
}

JRuby:

java_import org.apache.commons.io.IOUtils
java_import java.nio.charset.StandardCharsets
java_import org.apache.nifi.processor.io.OutputStreamCallback # Define a subclass of OutputStreamCallback for use in session.write()
class JRubyOutputStreamCallback
include OutputStreamCallback def process(outputStream)
    outputStream.write("Hello World!".to_java.getBytes(StandardCharsets::UTF_8))
end
end jrubyOutputStreamCallback = JRubyOutputStreamCallback.new
flowFile = session.get()
if flowFile != nil
    flowFile = session.write(flowFile, jrubyOutputStreamCallback)
end

覆盖 flow file内容

需求:重用输入 flow file但是希望修改内容并传递到输出的 flow file.

方法:使用session的write(flowFilestreamCallback) 方法。一个StreamCallback 对象需要传递给 write() 方法. StreamCallback 同时提供了InputStream (从输入的 flow file) 和 outputStream (下一版本的 flow file), 因此你可以使用InputStream去取得 flow file的当前内容, 然后修改他们并且写会到 flow file. 这将覆盖 flow file 的内容, 因此对于追加内容要采用读入内容添加的方式, 或者使用不同的方法 (使用 session.append() 而不是session.write() )。

注意,因为 StreamCallback 是一个对象, 因此内容之灾对象内部可见. 如果你需要在 write() 方法之外访问, 使用更为全局化变量.

以下这个例子将反转输入flowFile (假定为 String) 的内容,并将反转后的字符串写入到新版的 flowFile.

例子

Groovy:

import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets flowFile = session.get()
if(!flowFile) return def text = 'Hello world!'
// Cast a closure with an inputStream and outputStream parameter to StreamCallback flowFile = session.write(flowFile, {inputStream, outputStream ->
        text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
        outputStream.write(text.reverse().getBytes(StandardCharsets.UTF_8))
    } as StreamCallback) session.transfer(flowFile, REL_SUCCESS)

Jython:

from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from org.apache.nifi.processor.io import StreamCallback # Define a subclass of StreamCallback for use in session.write()
class PyStreamCallback(StreamCallback):
def __init__(self):
pass def process(self, inputStream, outputStream):
text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
outputStream.write(bytearray('Hello World!'[::-1].encode('utf-8')))
# end class flowFile = session.get() if(flowFile != None):
    flowFile = session.write(flowFile, PyStreamCallback()) # implicit return at the end

Javascript:

var StreamCallback = Java.type("org.apache.nifi.processor.io.StreamCallback");
var IOUtils = Java.type("org.apache.commons.io.IOUtils");
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets");
var flowFile = session.get(); if(flowFile != null) {
// Create a new StreamCallback, passing in a function to define the interface method
flowFile = session.write(flowFile,new StreamCallback(function(inputStream, outputStream) {
    var text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
    outputStream.write(text.split("").reverse().join("").getBytes(StandardCharsets.UTF_8))
    }));
}

JRuby:

java_import org.apache.commons.io.IOUtils
java_import java.nio.charset.StandardCharsets
java_import org.apache.nifi.processor.io.StreamCallback # Define a subclass of StreamCallback for use in session.write() class JRubyStreamCallback
include StreamCallback
def process(inputStream, outputStream)
    text = IOUtils.toString(inputStream)
    outputStream.write((text.reverse!).to_java.getBytes(StandardCharsets::UTF_8))
end
end jrubyStreamCallback = JRubyStreamCallback.new
flowFile = session.get()
if flowFile != nil
    flowFile = session.write(flowFile, jrubyStreamCallback)
end

处理错误

需求:在 script ( data validation 或者出现一个 exception)运行时出现错误, 处理和抛出错误。

方法:对于exceptions, 使用脚本语言的exception-handling 机制  (一般是try/catch 代码块). 对于 data validation, 可以使用类似的方法, 但是定义一个boolean 变量,如 "valid" 以及 if/else 语句,而不是try/catch 语句. ExecuteScript 定义了 "success" and "failure" relationships; 一般情况下,你的处理将转移 "good" flow files 到 success,而 "bad" flow files 到 failure (记录错误在后续的操作中)。

例子

Groovy:

flowFile = session.get()

if(!flowFile) return
try {
    // Something that might throw an exception here
    // Last operation is transfer to success (failures handled in the catch block)
    session.transfer(flowFile, REL_SUCCESS)
} catch(e) {
    log.error('Something went wrong', e)
    session.transfer(flowFile, REL_FAILURE)
}

Jython:

flowFile = session.get()

if(flowFile != None):
try:
    # Something that might throw an exception here
    # Last operation is transfer to success (failures handled in the catch block)
    session.transfer(flowFile, REL_SUCCESS)
except:
    log.error('Something went wrong', e)
    session.transfer(flowFile, REL_FAILURE) # implicit return at the end

Javascript:

var flowFile = session.get();
if(flowFile != null) {
try {
    // Something that might throw an exception here
    // Last operation is transfer to success (failures handled in the catch block)
    session.transfer(flowFile, REL_SUCCESS)
} catch(e) {
    log.error('Something went wrong', e)
    session.transfer(flowFile, REL_FAILURE)
}
}

JRuby:

flowFile = session.get()

if flowFile != nil
begin
    # Something that might raise an exception here
    # Last operation is transfer to success (failures handled in the rescue block)
    session.transfer(flowFile, REL_SUCCESS)
    rescue Exception => e
    log.error('Something went wrong', e)
    session.transfer(flowFile, REL_FAILURE)
end
end

上一篇:Nifi组件脚本开发—ExecuteScript 使用指南(一)
源:https://www.shangmayuan.com/a/0ba9c44310b04d1dad461790.html

参考:http://nifi.apache.org/developer-guide.html

Nifi组件脚本开发—ExecuteScript 使用指南(二)的更多相关文章

  1. Nifi组件脚本开发—ExecuteScript 使用指南(三)

    上一篇:Nifi组件脚本开发-ExecuteScript 使用指南(二) Part 3 - 高级特征 本系列的前两篇文章涵盖了 flow file 的基本操作, 如读写属性和内容, 以及使用" ...

  2. Nifi组件脚本开发—ExecuteScript 使用指南(一)

    Part 1 - 介绍 NiFi API 和 FlowFiles ExecuteScript 是一个万能的处理器,允许用户使用编程语言定义自己的数据处理功能, 在每一次 ExecuteScript p ...

  3. Delphi 组件渐进开发浅谈(二)——双简合璧

    2.双简合璧2.1.带有T[x]Label的T[x]Edit组件 请允许我用[x]的书写方式来表示不同的对象.因为随后将大量提及TLabeledEdit与TTntLabeledEdit.TCustom ...

  4. 【NIFI】 Apache NiFI 之 ExecuteScript处理(二)

    本例介绍NiFI ExecuteScript处理器的使用,使用的脚本引擎ECMScript 接上一篇[NIFI] Apache NiFI 之 ExecuteScript处理(一) ExecuteScr ...

  5. JAVA+PHP+阿里云组件纯手工实现POP、SMTP、IMAP开发邮件服务器(二)

    java开发邮件服务器的接收模块 用java建立socket服务端,监听端口25,实现SMTP协议.即可完成邮件服务器的接收模块. 这里要注意的是,SMTP协议其实可以分为两种.一种是你用手机.PC等 ...

  6. Jmeter+Ant+Jenkins接口自动化测试(二)_测试方案设计及jmeter脚本开发

    前言 根据之前部署好的测试环境,进行接口自动化测试的方案设计及Jmeter脚本开发.测试方案设计过程中采用了数据分离和对象分离等思路,因此直接通过特定的测试用例文档来驱动整个自动化接口测试的执行,相关 ...

  7. App架构师实践指南二之App开发工具

    App架构师实践指南二之App开发工具     1.Android Studio 2.编译调试---条件断点.右键单击断点,在弹出的窗口中输入Condition条件.---日志断点.右键单击断点,在弹 ...

  8. 【JMeter4.0学习(二)】之搭建openLDAP在windows8.1上的安装配置以及JMeter对LDAP服务器的性能测试脚本开发

    目录: 概述 安装测试环境 安装过程 配置启动 配置搭建OpenLDAP 给数据库添加数据 测试查询刚刚插入的数据 客户端介绍 JMeter建立一个扩展LDAP服务器的性能测试脚本开发 附:LDAP学 ...

  9. Vue学习笔记-Vue.js-2.X 学习(二)===>组件化开发

    ===重点重点开始 ========================== (三) 组件化开发 1.创建组件构造器: Vue.extends() 2.注册组件: Vue.component() 3.使用 ...

随机推荐

  1. js将金额转成大写金额

    function Chinese(){ /* var num= $(dialogStruct.iframe.contentDocument.getElementById("contractA ...

  2. Cisco动态路由(rip)

    接Cisco静态路由,讨论一下Cisco动态路由. 实验环境布置 命令布置动态路由 Router0: Router>enable Router#configure terminal Router ...

  3. docker第一日学习总结

    查看当前所有的镜像 docker images 查看当前运行的容器 docker ps 一般容器分为后台驻留和闪退(ubuntu\busybox等)两种,对于后台驻留的,我们如果想进入这个容器(前提是 ...

  4. Codeforces Global Round 8 C. Even Picture(构造)

    题目链接:https://codeforces.com/contest/1368/problem/C 题意 构造一个只含有灰.白块的网格,要求: 所有灰块为一个连通块 每个灰块与偶数个灰块相邻 恰有 ...

  5. Python:装饰器是如何调用的

    应用举例:(1)装饰器 # 装饰器的调用: # 一旦用上装饰器会: # 第一步:调outer函数 # 第二步:被装饰的函数play_game会被当作参数fn给outer # 第三步:最后调用play_ ...

  6. 【转】Kubernetes scheduler学习笔记

    简介 Kubernetes是一个强大的编排工具,可以用来很方便的管理许多台机器,为了使机器的资源利用率提高,同时也尽可能的把压力分摊到各个机器上,这个职责就是由scheduler来完成的. Kuber ...

  7. 计算机网络基础篇-ppp协议

    所谓的PPP协议是点对点协议,是目前使用最广泛的数据链路层的协议.大部分用户使用电话线拨号入网的,从用户计算机到ISP的链路所使用的数据链路层协议就是PPP协议. 首先介绍下拨号入网的过程.因特网服务 ...

  8. 云原生系列3 pod核心字段

    pod是容器化的基础,好比大楼的地基. Pod跟容器的关系 类比一下: POD: 物理机容器: 物理机上的一个进程: 容器只是Pod的一个普通字段. Pod的作用范围 跟容器的linux namesp ...

  9. 探索 .NET Core 依赖注入的 IServiceCollection

    如果您使用了.NET Core,则很可能已使用Microsoft.Extensions.DependencyInjection中的内置依赖项注入容器,在本文中,我想更深入地了解Microsoft De ...

  10. 免费在线 Linux Desktop 环境

    免费在线 Linux Desktop 环境 Run Linux OS Distributions online https://www.onworks.net/os-distributions 免费测 ...