Nifi组件脚本开发—ExecuteScript 使用指南(一)
Part 1 - 介绍 NiFi API 和 FlowFiles
ExecuteScript 是一个万能的处理器,允许用户使用编程语言定义自己的数据处理功能, 在每一次 ExecuteScript processor 触发时被调用。下面的变量绑定到脚本环境,以提供脚本中访问 NiFi 组件环境:
session: 是对processor的ProcessSession属性的引用。session允许在 flow files 执行下面的操作: create(), putAttribute(), transfer(), 像 read() 和 write()一样。
context: 是对 ProcessContext 的引用。可以用于检索 processor 的属性, 关系, Controller Services, 和 StateManager。
log: 是对ComponentLog的引用。用于 log 消息到 NiFi系统, 如 log.info('Hello world!')。
REL_SUCCESS: 这是对 "success" relationship 的引用。这是从父类 (ExecuteScript)的静态变量基础来的, 但是一些引擎(如 Lua)不允许引用静态成员, 只是一个为了方便的变量。
REL_FAILURE: 这是对 "failure" relationship 的引用。与 REL_SUCCESS 一样, 这是从父类 (ExecuteScript)的静态变量基础来的, 但是一些引擎(如 Lua)不允许引用静态成员, 只是一个为了方便的变量。
Dynamic Properties: 任何在ExecuteScript定义的动态属性都作为变量集合到 PropertyValue 对象,对应于dynamic property。允许获得property的 String 值 , 通过NiFi表达式进行求值,获得相应的类型 (如 Boolean, 等等)。 因为动态属性名称成为脚本里的变量名, 你需要了解所选的脚本引擎的变量命名属性。 例如, Groovy 不允许变量名中提供 (.) , 所以,如果 "my.property"作为动态属性将会报错。
与这些变量名的交互通过 NiFi Java API进行, 下面的每一个案例将讨论相应的API调用。 下面的案例执行不同的函数操作 flow files, 如 reading/writing 属性, 转换为 relationship, logging, 等等。需要注意,这里的例子只是一些片段。举例来说, 如果使用session.get()从队列中获取 flow file , 必须转换为 relationship 或者移除, 否则将会引发错误。代码片段应该是平面化的而且保持清晰,没有容易引起混乱的代码,仅用于演示概念,从而让工作简单。
从incoming queue得到flow file
从session中获得flow file
需求:从队列中获得输入flow file,执行 ExecuteScript 并进行处理。
方法:使用 session对象的get(). 该方法返回FlowFile,是下一个最高优先级的FlowFile用于处理. 如果没有 FlowFile 用于处理, 该方法将返回 null. 注意, 如果一个持续的FlowFiles流进入processor,也可能会返回null. 这在多个并发任务处理时会发生,此时其他任务已获得了 FlowFiles. 如果脚本要求有一个 FlowFile才能继续处理, 如果是session.get()得到null应该立即返回。
例子:
Groovy:
flowFile = session.get()
if(!flowFile) return
Jython:
flowFile = session.get()
if (flowFile != None):
Javascript:
var flowFile = session.get();
if (flowFile != null) {
// All processing code goes here
}
JRuby:
flowFile = session.get()
if flowFile != nil
# All processing code goes here
end
得到多个 flow files
需求:从queue(s)获得多个flow files用于ExecuteScript处理
方法:使用session对象的get(maxResults) 方法. 该方法从工作队列中返回最多 maxResults 个FlowFiles . 如果没有 FlowFiles 可用, 一个空的list 将被返回 (而不是返回 null).。
例子:
Groovy:
flowFileList = session.get(100)
if(!flowFileList.isEmpty()) {
flowFileList.each { flowFile ->
// Process each FlowFile here
}
}
Jython:
flowFileList = session.get(100)
if not flowFileList.isEmpty():
for flowFile in flowFileList:
# Process each FlowFile here
Javascript:
flowFileList = session.get(100)
if(!flowFileList.isEmpty()) {
for each (var flowFile in flowFileList) {
// Process each FlowFile here
}
}
JRuby:
flowFileList = session.get(100)
if !(flowFileList.isEmpty())
flowFileList.each { |flowFile|
# Process each FlowFile here}
end
创建一个新的 flow files
创建新Flow Files
需求:创建一个新的 FlowFile 发送到下一步的 processor
方法:使用session的 create() 方法. 该方法返回 FlowFile 对象, 以用于后续的处理操作。
例子:
Groovy:
flowFile = session.create()
// Additional processing here
Jython:
flowFile = session.create()
# Additional processing here
Javascript:
var flowFile = session.create();
// Additional processing here
JRuby:
flowFile = session.create()
# Additional processing here
从父级FlowFile创建新的 FlowFile
需求:从已有的 FlowFile 创建新的flow file 发送到下一步的 processor
方法:使用session的 create(parentFlowFile) 方法,该方法获得父级 FlowFile 的引用,然后返回新的派生 FlowFile 对象。新创建的 FlowFile 除UUID之外将继承父级的所有属性,同时该方法将自动创建一个 起源 FORK 事件或 起源 JOIN 事件,在 ProcessSession被提交的时候,取决于FlowFiles 是否从同一个parent创建,
例子:
Groovy:
flowFile = session.get()
if(!flowFile) return
newFlowFile = session.create(flowFile)
// Additional processing here
Jython:
flowFile = session.get()
if (flowFile != None):
newFlowFile = session.create(flowFile)
# Additional processing here
Javascript:
var flowFile = session.get();
if (flowFile != null) {
var newFlowFile = session.create(flowFile);
// Additional processing here
}
JRuby:
flowFile = session.get()
if flowFile != nil
newFlowFile = session.create(flowFile)
# Additional processing here
end
flow file 的attributes操作
从 flow file 得到属性
需求:获得flow file 的属性。
方法:使用FlowFile对象getAttribute(attributeKey) 。 该方法对于给定的attributeKey返回一个字符串值 , 如果没有找到相应的key就返回null. 下面的例子演示返回FlowFile的 "filename" 属性。
例子:
Groovy:
flowFile = session.get() if(!flowFile) return
myAttr = flowFile.getAttribute('filename')
Jython:
flowFile = session.get() if (flowFile != None):
myAttr = flowFile.getAttribute('filename')
# implicit return at the end
Javascript:
var flowFile = session.get()
if (flowFile != null) {
var myAttr = flowFile.getAttribute('filename')
}
JRuby:
flowFile = session.get() if flowFile != nil
myAttr = flowFile.getAttribute('filename')
end
从 flow file得到所有的属性
需求:从flow file得到所有的属性。
方法:使用FlowFile对象的getAttributes() 方法。 该方法返回 Map 数据结构,由字符串的 keys 和 values组成, 代表一个FlowFile的属性的 key/value 值对。 下面的显示如何递归显示FlowFile的所有属性的Map的值。
例子:
Groovy:
flowFile = session.get() if(!flowFile) return
flowFile.getAttributes().each { key,value ->
// Do something with the key/value pair
}
Jython:
flowFile = session.get() if (flowFile != None):
for key,value in flowFile.getAttributes().iteritems():
# Do something with key and/or value # implicit return at the end
Javascript:
var flowFile = session.get()
if (flowFile != null) {
var attrs = flowFile.getAttributes();
for each (var attrKey in attrs.keySet()) {
// Do something with attrKey (the key) and/or attrs[attrKey] (the value)
}
}
JRuby:
flowFile = session.get() if flowFile != nil
flowFile.getAttributes().each
{ |key,value|
# Do something with key and/or value
}
end
添加属性到 flow file
需求:在已有的 flow file 上添加自己的属性。
方法:使用session对象的 putAttribute(flowFile, attributeKey, attributeValue) 方法。 该方法更新给定的 FlowFile's 属性,使用给出的 key/value 对来进行。
注意:对象的 "uuid" 属性是固定的,并且不能修改; 如果key被命名为 "uuid", 将被忽略.
这里的FlowFile 对象是不可改变的; 这意味着,如果通过API更新了 FlowFile 的属性 (或其它的改变了) , 你将得到一个新版的FlowFile的新的引用。当转换FlowFiles到relationships时这是非常重要的。你必须保持对FlowFile的最新版本的引用, 你必须转换或者移除所有的FlowFiles的最后版本, 否则执行时将会得到错误信息。经常情况下, 该用于存储 FlowFile 引用变量将会被最后返回的版本覆盖 (中间的 FlowFile 应用将会被自动抛弃). 在这个例子中,你可以看到当添加属性时重用flowFile引用的技术。注意到当前的flowFile引用被传递给putAttribute() 方法. 这个结果FlowFile具有命名为 'myAttr'值为 'myValue'的属性。如果你有一个对象,可以序列化为String. 最终, 请注意如果你添加了多个属性, 最好创建一个Map,然后使用 putAllAttributes() 方法来进行赋值。
例子:
Groovy:
flowFile = session.get()
if(!flowFile) return
flowFile = session.putAttribute(flowFile, 'myAttr', 'myValue')
Jython:
flowFile = session.get()
if (flowFile != None):
flowFile = session.putAttribute(flowFile, 'myAttr', 'myValue')
# implicit return at the end
Javascript:
var flowFile = session.get();
if (flowFile != null) {
flowFile = session.putAttribute(flowFile, 'myAttr', 'myValue')
}
JRuby:
flowFile = session.get()
if flowFile != nil
flowFile = session.putAttribute(flowFile, 'myAttr', 'myValue')
end
添加多个属性到一个flow file
需求:向 flow file 添加多个自定义属性。。
方法:使用 session对象的putAllAttributes(flowFile, attributeMap) 方法。该方法更新给定的FlowFile's 属性,以 key/value 对的方式存储在Map中返回。
注意:对象的 "uuid" 属性是固定的,并且不能修改; 如果key被命名为 "uuid", 将被忽略.
该技术创建了一个 Map (aka dictionary in Jython, hash in JRuby) 用于更新,然后调用putAllAttributes() 。这比对putAttribute() 对每一个 key/value 遍历效率更高, 这将导致对每一个属性调用时 FlowFile 都需要创建一个副本 (查看上面 FlowFile 不变性的讨论)。下面例子中的Map包含两个条目: myAttr1 和 myAttr2, 设为 '1' 并且第二个为 String (附着到方法签名,对key和value都要求 String)。 注意到session.transfer() 在这里并未指定 (因此下面的代码片段并不能工作), 查看下面的方法。
例子:
Groovy:
attrMap = ['myAttr1': '1', 'myAttr2': Integer.toString(2)]
flowFile = session.get()
if(!flowFile) return
flowFile = session.putAllAttributes(flowFile, attrMap)
Jython:
attrMap = {'myAttr1':'1', 'myAttr2':str(2)}
flowFile = session.get()
if (flowFile != None):
flowFile = session.putAllAttributes(flowFile, attrMap)
# implicit return at the end
Javascript:
var number2 = 2;
var attrMap = {'myAttr1':'1', 'myAttr2': number2.toString()}
var flowFile = session.get() if (flowFile != null) {
flowFile = session.putAllAttributes(flowFile, attrMap)
}
JRuby:
attrMap = {'myAttr1' => '1', 'myAttr2' => 2.to_s}
flowFile = session.get()
if flowFile != nil
flowFile = session.putAllAttributes(flowFile, attrMap)
end
转换 flow file
转移一个flow file 到 relationship
需求:在处理完flow file (new or incoming)之后, 你希望将flow file转移到 relationship ("success" or "failure"). 在这个简单的例子中,让我们假定有一个变量叫做 "errorOccurred", 用于指示在哪种 relationship下 FlowFile 将被转移。
方法:使用session对象的transfer(flowFile, relationship) 方法。基于给定的relationship,该方法将给定的FlowFile发送到适合的目标处理器队列。如果relationship通向不止一个目标,FlowFile的状态将被复制 ,从而每一个目标都将收到一个 FlowFile的拷贝,因此也将具有唯一的标识符UUID。
注意:最后,ExecuteScript将执行session.commit() 以进行操作的提交。你不需要在脚本内部执行session.commit() 来执行提交操作。
例子:
Groovy:
flowFile = session.get() if(!flowFile) return // Processing occurs here
if(errorOccurred) {
session.transfer(flowFile, REL_FAILURE)
}
else {
session.transfer(flowFile, REL_SUCCESS)
}
Jython:
flowFile = session.get() if (flowFile != None):
# All processing code starts at this indent
if errorOccurred:
session.transfer(flowFile, REL_FAILURE)
else:
session.transfer(flowFile, REL_SUCCESS)
# implicit return at the end
Javascript:
var flowFile = session.get();
if (flowFile != null) {
// All processing code goes here
if(errorOccurred) {
session.transfer(flowFile, REL_FAILURE)
}
else {
session.transfer(flowFile, REL_SUCCESS)
}
}
JRuby:
flowFile = session.get()
if flowFile != nil
# All processing code goes here
if errorOccurred
session.transfer(flowFile, REL_FAILURE)
else
session.transfer(flowFile, REL_SUCCESS)
end
end
日志 Logging
发送消息到 log并制定日志级别
需求:希望报告一些事件、消息并通过日志框架写入。
方法: 使用 log 的方法(), trace(), debug(), info(), 或 error() 完成。这些方法可以是单个的字符串或者字符串数组对象, 或字符串后面跟着Throwable的对象数组。第一个用于简单消息. 第二个用于一些动态对象(值)的log。在消息字符串中使用 "{}" 进行引用。这些用于对对象数组进行求值,当消息读到 "Found these things: {} {} {}" 并且 Object array 是 ['Hello',1,true], 那么logged 消息将是 "Found these things: Hello 1 true",第三种logging方法带一个 Throwable 参数, 这在例外被捕捉到并且希望日志记录时使用。
例子:
Groovy:
log.info('Found these things: {} {} {}', ['Hello',1,true] as Object[])
Jython:
from java.lang import Object
from jarray import array objArray = ['Hello',1,True]
javaArray = array(objArray, Object)
log.info('Found these things: {} {} {}', javaArray)
Javascript:
var ObjectArrayType = Java.type("java.lang.Object[]");
var objArray = new ObjectArrayType(3);
objArray[0] = 'Hello';
objArray[1] = 1;
objArray[2] = true;
log.info('Found these things: {} {} {}', objArray)
JRuby:
log.info('Found these things: {} {} {}', ['Hello',1,true].to_java)
Nifi组件脚本开发—ExecuteScript 使用指南(一)的更多相关文章
- Nifi组件脚本开发—ExecuteScript 使用指南(三)
上一篇:Nifi组件脚本开发-ExecuteScript 使用指南(二) Part 3 - 高级特征 本系列的前两篇文章涵盖了 flow file 的基本操作, 如读写属性和内容, 以及使用" ...
- Nifi组件脚本开发—ExecuteScript 使用指南(二)
Part 2 - FlowFile I/O 和 Error Handling flow File的IO NiFi 的 Flow files 由两个主要部件组成:attributes 和 content ...
- Blazor 组件库开发指南
翻译自 Waqas Anwar 2021年5月21日的文章 <A Developer's Guide To Blazor Component Libraries> [1] Blazor 的 ...
- vue.js组件化开发实践
前言 公司目前制作一个H5活动,特别是有一定统一结构的活动,都要码一个重复的轮子.后来接到一个基于模板的活动设计系统的需求,便有了下面的内容.借油开车. 组件化 需求一到,接就是怎么实现,技术选型自然 ...
- Google Map和桌面组件 Android开发教程
本文节选于机械工业出版社推出的<Android应用开发揭秘>一 书,作者为杨丰盛.本书内容全面,详细讲解了Android框架.Android组件.用户界面开发.游戏开发.数据存储.多媒体开 ...
- 物联网操作系统HelloX开发人员入门指南
HelloX开发人员入门指南 HelloX是聚焦于物联网领域的操作系统开发项目,能够通过百度搜索"HelloX".获取具体信息. 当前开发团队正在进一步招募中,欢迎您的了解和添加. ...
- 基于TypeScript的FineUIMvc组件式开发(开头篇)
了解FineUIMvc的都知道,FineUIMvc中采用了大量的IFrame框架,对于IFrame的优缺点网上也有很多的讨论,这里我要说它的一个优点“有助于隔离代码逻辑”,这也是FineUIMvc官网 ...
- Jmeter+Ant+Jenkins接口自动化测试(二)_测试方案设计及jmeter脚本开发
前言 根据之前部署好的测试环境,进行接口自动化测试的方案设计及Jmeter脚本开发.测试方案设计过程中采用了数据分离和对象分离等思路,因此直接通过特定的测试用例文档来驱动整个自动化接口测试的执行,相关 ...
- AppBoxFuture(六): 前端组件化开发
前面几篇都是在介绍结构化与非结构化的数据存储,本篇换换口味介绍一下框架是如何实现前端组件化开发的.首先得感谢Vue.ElementUI等优秀的前端开源项目,这些项目帮助作者快速实现了框架的两个前端 ...
随机推荐
- js 点击input焦点不弹出键盘 PDA扫描枪
直接贴代码 1.利用input readonly属性 当input有readonly属性的时候,即使获取焦点,也不会吊起小键盘 扫码枪输入的间隔大概在15-60毫秒,然后手动输入的100-200毫秒之 ...
- Python基础(上篇)
本篇文章主要内容:变量.注释.运算符.关键字.数据类型. 在入手变量之前我们先来看看经典的编程语句 → hello world 在python3中输出到控制台的函数是print() print(&qu ...
- 解决Idea中没有SVN标识,不能提交、更新代码
使用idea也不久,今天从svn上down下来的项目导入idea,发现写的代码不能在idea里面更新,记录下解决方案. 步骤 1.点击VCS,然后Enable Version Control Inte ...
- [leetcode]TwoSum系列问题
1.普通数组找两个数,哈希表建立数值和下标的映射,遍历时一边判断一边添加 /* 哇,LeetCode的第一题...啧啧 */ public int [] twoSum(int[] nums, int ...
- web攻防环境--一句话木马
任务一.基于centos7搭建dvwa web服务靶机 1.在centos7安装LAMP并启动,访问phpinfo页面 也即安装httpd.php.mysql服务. 直接进行yum安装即可,完成后检查 ...
- Netty tcnative boringssl windows 32-bit 编译
1 问题 在使用Netty SSL时,我们往往会采用netty-tcnative-boringssl组件.但是netty-tcnative-boringssl在Windows上仅有64位版本的,没有3 ...
- Java并发编程实战(3)- 互斥锁
我们在这篇文章中主要讨论如何使用互斥锁来解决并发编程中的原子性问题. 目录 概述 互斥锁模型 互斥锁简易模型 互斥锁改进模型 Java世界中的互斥锁 synchronized中的锁和锁对象 synch ...
- Maven+Spring 框架,ModelAndView在页面取值不成功
如果创建的是maven project , maven生成的web.xml是这样的: 但是这样是不对的,应该修改成: 下面是代码: <?xml version="1.0" e ...
- 【MyBatis】MyBatis 动态 SQL
MyBatis 动态SQL if 可以根据实体类的不同取值,使用不同的 SQL 语句来进行查询. 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分. 持久层 DAO 接口: pub ...
- spring ioc踏出第一步
spring IOC(容器) AOP(面向切面编程) IOC容器:他的功能就是可以整合像 Structs2 .Hibernate.Mybatis: IOC:控制反转:所谓的控制就是控制资源的获取方法, ...