Apache NiFi開發人員指南

 

介紹

NiFi組件

Processor API

支持API

AbstractProcessor API

組件生命週期

組件通知

受限

州經理

報告處理器活動

記錄組件

記錄屬性

記錄關係

記錄能力和關鍵詞

記錄FlowFile屬性交互

高級文檔

種源事件

通用處理器模式

數據入口

數據出口

基於內容的路由(一對一)

基於內容的路由(一對多)

基於內容的路由流(一對多)

基於屬性的路由

拆分內容(一對多)

根據內容更新屬性

豐富/修改內容

錯誤處理

處理器中的例外情況

回調中的異常:IOException,RuntimeException

懲罰與屈服

會話回滾

一般設計考慮因素

考慮用戶

凝聚力和可重用性

命名約定

處理器行爲註釋

數據緩衝

控制器服務

開發ControllerService

與ControllerService交互

報告任務

開發報告任務

UI擴展

自定義處理器UI

內容瀏覽者

命令行工具

TLS-工具包

測試

實例化TestRunner

添加ControllerServices

設置屬性值

排隊FlowFiles

運行處理器

驗證輸出

模擬外部資源

附加測試功能

NiFi檔案館(NAR)

每實例類加載

棄用組件

如何爲Apache NiFi做出貢獻

技術

從哪兒開始?

提供捐款

聯繫我們


介紹

本開發人員指南旨在爲讀者提供瞭解Apache NiFi擴展如何開發所需的信息,並幫助解釋開發組件背後的思維過程。它提供了用於開發擴展的API的介紹和說明。但是,它沒有詳細介紹API中的每個方法,因爲本指南旨在補充API的JavaDoc而不是替換它們。本指南還假定讀者熟悉Java 7和Apache Maven。

本指南由開發人員爲開發人員編寫。在閱讀本指南之前,您需要對NiFi和數據流概念有基本的瞭解。如果沒有,請參閱NiFi概述 和NiFi用戶指南,以熟悉NiFi的概念。

NiFi組件

NiFi提供了幾個擴展點,使開發人員能夠爲應用程序添加功能以滿足他們的需求。以下列表提供了最常見擴展點的高級描述:

  • 處理器(Processor)

    • 處理器接口是NiFi公開對FlowFile,其屬性及其內容的訪問的機制 。處理器是用於構成NiFi數據流的基本構建塊。此接口用於完成以下所有任務:

      • 創建FlowFiles

      • 閱讀FlowFile內容

      • 編寫FlowFile內容

      • 讀取FlowFile屬性

      • 更新FlowFile屬性

      • 攝取數據

      • 出口數據

      • 路線數據

      • 提取數據

      • 修改數據

  • ReportingTask

    • ReportingTask接口是NiFi公開的一種機制,允許將指標,監控信息和內部NiFi狀態發佈到外部端點,例如日誌文件,電子郵件和遠程Web服務。

  • ControllerService

    • ControllerService在單個JVM中跨處理器,其他ControllerServices和ReportingTasks提供共享狀態和功能。示例用例可以包括將非常大的數據集加載到存儲器中。通過在ControllerService中執行此工作,數據可以加載一次,並通過此服務公開給所有處理器,而不是要求許多不同的處理器自己加載數據集。

  • FlowFilePrioritizer

    • FlowFilePrioritizer接口提供了一種機制,通過該機制可以對隊列中的FlowFile進行優先級排序或排序,以便可以按照對特定用例最有效的順序處理FlowFiles。

  • AuthorityProvider

    • AuthorityProvide負責確定應授予給定用戶的特權和角色(如果有)。

Processor API

處理器是NiFi中使用最廣泛的組件。處理器是唯一可以訪問以創建,刪除,修改或檢查FlowFiles(數據和屬性)的組件。

使用Java的ServiceLoader機制加載和實例化所有處理器。這意味着所有處理器必須遵守以下規則:

  • 處理器必須具有默認構造函數。

  • Processor的JAR文件必須包含名爲META-INF / services目錄的條目 org.apache.nifi.processor.Processor。這是一個文本文件,其中每行包含Processor的完全限定類名。

雖然Processor是一個可以直接實現的接口,但這樣做非常罕見,因爲它org.apache.nifi.processor.AbstractProcessor是幾乎所有處理器實現的基類。AbstractProcessor類提供的功能的顯著,這使得開發的處理器更容易,更方便的任務。對於本文檔的範圍,我們將主要關注AbstractProcessor處理Processor API時的類。

併發注意事項

NiFi是一個高度併發的框架。這意味着所有擴展必須是線程安全的。如果不熟悉用Java編寫併發軟件,強烈建議您熟悉Java併發原理。

支持API

爲了理解處理器API,我們必須首先理解 - 至少在高層次 - 幾個支持類和接口,這將在下面討論。

FlowFile

FlowFile是一種邏輯概念,它將一段數據與一組關於該數據的屬性相關聯。這些屬性包括FlowFile的唯一標識符,以及其名稱,大小和任何數量的其他特定於流的值。雖然FlowFile的內容和屬性可以更改,但FlowFile對象是不可變的。ProcessSession可以對FlowFile進行修改。

FlowFiles的核心屬性在org.apache.nifi.flowfile.attributes.CoreAttributes枚舉中定義。您將看到的最常見屬性是filename,path和uuid。引號中的字符串是CoreAttributes枚舉中屬性的值。

  • Filename(“filename”):FlowFile的文件名。文件名不應包含任何目錄結構。

  • UUID(“uuid”):分配給此FlowFile的通用唯一標識符,用於區分FlowFile與系統中的其他FlowFiles。

  • Path(“path”):FlowFile的路徑指示FlowFile所屬的相對目錄,不包含文件名。

  • Absolute Path (“absolute.path”):FlowFile的絕對路徑表示FlowFile所屬的絕對目錄,不包含文件名。

  • Priority(“priority”):表示FlowFile優先級的數值。

  • MIME Type(“mime.type”):此FlowFile的MIME類型。

  • Discard Reason(“discard.reason”):指定丟棄FlowFile的原因。

  • Alternative Identifier(“alternate.identifier”):表示已知引用此FlowFile的FlowFile的UUID以外的標識符。

ProcessSession

ProcessSession通常簡稱爲“會話”,它提供了一種機制,通過該機制可以創建,銷燬,檢查,克隆FlowFiles並將其傳輸到其他處理器。此外,ProcessSession還提供了通過添加或刪除屬性或修改FlowFile內容來創建FlowFiles的修改版本的機制。ProcessSession還公開了一種用於發佈源代碼事件的機制,該機制提供了跟蹤FlowFile的沿襲和歷史的能力。在一個或多個FlowFiles上執行操作後,可以提交或回滾ProcessSession。

ProcessContext

ProcessContext提供了處理器和框架之間的橋樑。它提供有關處理器當前如何配置的信息,並允許處理器執行特定於Framework的任務,例如產生其資源,以便框架將安排其他處理器運行而不會不必要地消耗資源。

PropertyDescriptor

PropertyDescriptor定義將由Processor,ReportingTask或ControllerService使用的屬性。屬性的定義包括其名稱,屬性的描述,可選的默認值,驗證邏輯,以及關於處理器是否有效所需的屬性的指示符。PropertyDescriptors是通過實例化PropertyDescriptor.Builder 類的實例,調用適當的方法來填充有關屬性的詳細信息,最後調用該build方法來創建的。

驗證器(Validator)

PropertyDescriptor必須指定一個或多個Validator,可用於確保用戶輸入的屬性值有效。如果Validator指示屬性值無效,則在屬性生效之前,將無法運行或使用Component。如果未指定Validator,則假定Component無效,NiFi將報告該屬性不受支持。

ValidationContext

驗證屬性值時,ValidationContext可用於獲取ControllerServices,創建PropertyValue對象,以及使用表達式語言編譯和評估屬性值。

PropertyValue

返回到處理器的所有屬性值都以PropertyValue對象的形式返回。此對象具有便捷方法,用於將值從String轉換爲其他形式(如數字和時間段),以及提供用於評估表達式語言的API。

關聯(Relationship)

關係定義FlowFile可以從處理器傳輸到的路由。通過實例化Relationship.Builder 類的實例,調用適當的方法來填充關係的細節,最後調用 build方法來創建關係。

StateManager

StateManager爲處理器,報告任務和控制器服務提供了一種輕鬆存儲和檢索狀態的機制。API類似於ConcurrentHashMap,但每個操作都需要一個Scope。範圍指示是在本地檢索/存儲狀態還是以羣集範圍的方式存儲狀態。有關更多信息,請參閱“ 狀態管理器”部分。

ProcessorInitializationContext

創建處理器後,initialize將使用InitializationContext對象調用其方法。此對象向處理器公開配置,該配置在處理器的整個生命週期內不會更改,例如處理器的唯一標識符。

ComponentLog

鼓勵處理器通過ComponentLog接口執行日誌記錄 ,而不是獲取第三方記錄器的直接實例。這是因爲通過ComponentLog進行日誌記錄允許框架將超出可配置嚴重性級別的日誌消息呈現給用戶界面,從而允許在發生重要事件時通知監視數據流的人員。此外,它通過在DEBUG模式下記錄堆棧跟蹤並在日誌消息中提供處理器的唯一標識符,爲所有處理器提供一致的日誌記錄格式。

AbstractProcessor API

由於絕大多數處理器都是通過擴展AbstractProcessor來創建的,因此我們將在本節中討論它的抽象類。AbstractProcessor提供了處理器開發人員感興趣的幾種方法。

處理器初始化(Processor Initialization)

創建處理器時,在調用任何其他方法之前,init將調用AbstractProcessor 的 方法。該方法採用單個參數,即類型ProcessorInitializationContext。上下文對象爲Processor提供ComponentLog,Processor的唯一標識符和ControllerServiceLookup,可用於與配置的ControllerServices交互。每個這樣的對象是由AbstractProcessor存儲,並且可以由子類經由獲得getLoggergetIdentifier和 getControllerServiceLookup方法,分別。

揭露處理器的關係(Exposing Processor’s Relationships)

爲了使處理器將FlowFile傳輸到新目的地以進行後續處理,處理器必須首先能夠向框架公開它當前支持的所有關係。這允許應用程序的用戶通過在處理器之間創建連接並將適當的關係分配給這些連接來將處理器彼此連接。

處理器通過覆蓋該getRelationships方法來公開有效的關係集 。這個方法沒有參數,並返回SetRelationship 對象。對於大多數處理器,此Set將是靜態的,但其他處理器將根據用戶配置動態生成Set。對於Set爲靜態的那些處理器,建議在Processor的構造函數或init方法中創建一個不可變的Set並返回該值,而不是動態生成Set。這種模式有助於實現更清晰的代碼和更好的性能。

公開處理器屬性(Exposing Processor Properties)

大多數處理器在能夠使用之前需要一些用戶配置。處理器支持的屬性通過該getSupportedPropertyDescriptors方法向Framework公開 。這個方法沒有參數,並返回List的 PropertyDescriptor對象。List中對象的順序很重要,因爲它決定了在用戶界面中呈現屬性的順序。

PropertyDescriptor目的是通過創建一個新的實例構造PropertyDescriptor.Builder對象,調用構建器的適當的方法,並最終調用build方法。

雖然此方法涵蓋了大多數用例,但有時需要允許用戶配置名稱未知的其他屬性。這可以通過覆蓋該getSupportedDynamicPropertyDescriptor方法來實現 。此方法將 String唯一參數作爲參數,該參數指示屬性的名稱。該方法返回一個PropertyDescriptor對象,該 對象可用於驗證屬性的名稱以及值。應該構建從此方法返回的任何PropertyDescriptor,isDynamicPropertyDescriptor.Builder類中將值設置爲true 。AbstractProcessor的默認行爲是不允許任何動態創建的屬性。

驗證處理器屬性( Validating Processor Properties )

如果處理器的配置無效,則無法啓動處理器。可以通過在PropertyDescriptor上設置Validator或通過PropertyDescriptor.Builder的allowableValues方法或identifiesControllerService方法限制屬性的允許值來驗證Processor屬性。

但是,有時候單獨驗證處理器的屬性是不夠的。爲此,AbstractProcessor公開了一個customValidate方法。該方法採用單個參數類型ValidationContext。此方法的返回值是描述驗證期間發現的任何問題Collection的 ValidationResult對象。只應返回其isValid方法返回的ValidationResult對象 false。僅當所有屬性根據其關聯的Validators和Allowable Values有效時,纔會調用此方法。即,只有當所有屬性本身都有效時纔會調用此方法,並且此方法允許整體驗證處理器的配置。

響應配置更改(Responding to Changes in Configuration)

有時希望處理器在其屬性改變時急切地做出反應。該onPropertyModified 方法允許處理器執行此操作。當用戶更改Processor的屬性值時,onPropertyModified將爲每個已修改的屬性調用該 方法。該方法有三個參數:PropertyDescriptor,它指示修改了哪個屬性,舊值和新值。如果屬性沒有先前的值,則第二個參數將是null。如果刪除了屬性,則第三個參數將是null。重要的是要注意,無論值是否有效,都將調用此方法。只有在實際修改了值時纔會調用此方法,而不是在用戶更新處理器而不更改其值時調用此方法。在調用此方法時,保證調用此方法的線程是當前在Processor中執行代碼的唯一線程,除非Processor本身創建自己的線程。

執行工作(Performing the Work)

當處理器有工作要做時,它計劃onTrigger通過框架調用其方法來完成。該方法有兩個參數:a ProcessContext和aProcessSession。該onTrigger方法的第一步通常是通過調用getProcessSession上的一個方法來獲取要在其上執行工作的FlowFile 。對於從外部源將數據提取到NiFi的處理器,將跳過此步驟。然後,處理器可以自由檢查FlowFile屬性; 添加,刪除或修改屬性; 讀取或修改FlowFile內容; 並將FlowFiles傳輸到適當的關係。

處理器被觸發時(When Processors are Triggered)

onTrigger只有在計劃運行處理器並且處理器存在工作時,纔會調用處理器的方法。如果滿足以下任何條件,則稱處理器存在工作:

  • 目標爲Processor的Connection在其隊列中至少有一個FlowFile

  • 處理器沒有傳入的連接

  • 處理器使用@TriggerWhenEmpty批註進行批註

有幾個因素會導致onTrigger調用Processor的 方法。首先,除非用戶已將處理器配置爲運行,否則不會觸發處理器。如果計劃運行處理器,則週期性地(該週期由用戶界面中的用戶配置)檢查處理器是否有工作,如上所述。如果是這樣,框架將檢查處理器的下游目的地。如果處理器的任何出站連接已滿,則默認情況下,將不會安排處理器運行。

但是,@TriggerWhenAnyDestinationAvailable註釋可以添加到Processor的類中。在這種情況下,需求被更改,以便只有一個下游目標必須“可用”(如果連接的隊列未滿,則目標被視爲“可用”),而不是要求所有下游目標都可用。

與處理器調度有關的還有@TriggerSerially 註釋。使用此Annotation的處理器永遠不會有多個線程onTrigger同時運行該方法。但是,必須注意,執行代碼的線程可能會從調用更改爲調用。因此,仍然必須注意確保處理器是線程安全的!

組件生命週期(Component Lifecycle)

NiFi API通過使用Java Annotations提供生命週期支持。該org.apache.nifi.annotations.lifecycle軟件包包含幾個生命週期管理註釋。以下注釋可以應用於NiFi組件中的Java方法,以指示何時應該調用方法的框架。對於組件生命週期的討論,我們將NiFi組件定義爲Processor,ControllerServices或ReportingTask。

@OnAdded

@OnAdded註解導致要儘快一個組件被創建調用的方法。在構造組件之後,將調用組件的initialize方法(或init方法,如果是子類 AbstractProcessor),後跟註釋的方法@OnAdded。如果任何帶有@OnAdded拋出異常的方法拋出異常,則會向用戶返回錯誤,並且該組件將不會添加到流中。此外,不會調用具有此Annotation的其他方法。該方法僅在組件的生命週期內調用一次。使用此Annotation的方法必須採用零參數。

@OnEnabled

所述@OnEnabled註釋可以被用來指示每當使能控制器服務的方法應該被調用。每次用戶啓用服務時,都會調用具有此批註的任何方法。此外,每次重新啓動NiFi時,如果NiFi配置爲“自動恢復狀態”並且啓用了服務,則將調用該方法。

如果帶有此批註的方法拋出Throwable,則將爲該組件發出日誌消息和公告。在這種情況下,服務將保持“啓用”狀態,不可用。然後,在延遲之後將再次調用具有此註釋的所有方法。在具有此註釋的所有方法返回而不丟棄任何內容之前,該服務將不可用。

使用此批註的方法必須採用0參數或單個參數類型org.apache.nifi.controller.ConfigurationContext

請注意,如果應用於ReportingTask或Processor,則將忽略此批註。對於Controller Service,啓用和禁用被視爲生命週期事件,因爲該操作使其可用或不可用於其他組件。但是,對於處理器和報告任務,這些不是生命週期事件,而是允許在啓動或停止一組組件時排除組件的機制。

@OnRemoved

@OnRemoved註釋將使得前一組分從流中除去要被調用的方法。這樣可以在刪除組件之前清除資源。使用此批註的方法必須採用零參數。如果帶有此批註的方法拋出異常,則仍將刪除該組件。

@OnScheduled

此批註指示每次調度組件運行時都應調用方法。由於未調度ControllerServices,因此在ControllerService上使用此批註沒有意義,也不會受到尊重。它應僅用於處理器和報告任務。如果具有此批註的任何方法拋出異常,則不會調用具有此批註的其他方法,並且將向用戶顯示通知。在這種情況下, @OnUnscheduled然後觸發帶@OnStopped註釋的方法,然後是帶有註釋的方法 (在此狀態期間,如果這些方法中的任何一個拋出異常,則忽略這些異常)。然後該組件將在一段時間內執行,稱爲“管理產量持續時間”。nifi.properties文件。最後,該過程將再次啓動,直到所有註釋的方法 @OnScheduled都返回而不拋出任何異常。具有此批註的方法可以採用零參數或可以採用單個參數。如果使用單個參數變體,則ProcessContext如果組件是處理器或ConfigurationContext組件是ReportingTask ,則參數必須是類型。

@OnUnscheduled

只要不再計劃運行Processor或ReportingTask,就會調用帶有此批註的方法。那時,許多線程可能仍然在處理器的onTrigger方法中處於活動狀態。如果此類方法拋出異常,則將生成日誌消息,否則將忽略異常,並且仍將調用具有此批註的其他方法。具有此批註的方法可以採用零參數或可以採用單個參數。如果使用單個參數變體,則ProcessContext如果組件是處理器或ConfigurationContext組件是ReportingTask ,則參數必須是類型 。

@OnStopped

當不再計劃運行Processor或ReportingTask並且從onTrigger方法返回所有線程時,將調用具有此批註的方法。如果這樣的方法拋出異常,將生成一條日誌消息,否則將忽略異常; 仍將調用具有此批註的其他方法。允許使用此註釋的方法採用0或1參數。如果使用了參數,則如果組件是ReportingTask,則它必須是ConfigurationContext類型;如果組件是Processor,則它必須是ProcessContext類型。

@OnShutdown

@OnShutdown當NiFi成功關閉時,將調用任何使用註釋註釋的方法。如果此類方法拋出異常,則將生成日誌消息,否則將忽略異常,並且仍將調用具有此批註的其他方法。使用此批註的方法必須採用零參數。注意:雖然NiFi將嘗試在使用它的所有組件上調用帶有此註釋的方法,但這並不總是可行的。例如,進程可能會意外終止,在這種情況下,它沒有機會調用這些方法。因此,雖然使用此註釋的方法可用於清理資源,但是,不應依賴它們來處理關鍵業務邏輯。

組件通知( Component Notification )

NiFi API通過使用Java Annotations提供通知支持。該org.apache.nifi.annotations.notification軟件包包含幾個用於通知管理的註釋。以下注釋可以應用於NiFi組件中的Java方法,以向框架指示何時應該調用方法。對於組件通知的討論,我們將NiFi組件定義爲處理器,控制器服務或報告任務。

@OnPrimaryNodeStateChange

@OnPrimaryNodeStateChange一旦羣集中的主節點的狀態發生更改,註釋就會調用方法。帶有此註釋的方法應該不帶參數或類型的一個參數PrimaryNodeState。該PrimaryNodeState提供什麼改變,使得組件可以採取適當的行動內容。該PrimaryNodeState枚舉有兩個可能的值: ELECTED_PRIMARY_NODE(節點接收到這個狀態已經當選NiFi集羣的主節點),或PRIMARY_NODE_REVOKED(接收到這個狀態的節點是主節點,但現在已經有其主節點角色撤銷)。

受限( Restricted )

受限組件是可用於執行操作員通過NiFi REST API / UI提供的任意未經過抽樣的代碼的組件,或者可用於使用NiFi OS憑證獲取或更改NiFi主機系統上的數據。這些組件可由其他授權的NiFi用戶使用,超出應用程序的預期用途,升級特權,或者可能暴露有關NiFi進程或主機系統內部的數據。所有這些功能都應被視爲特權,管理員應瞭解這些功能,併爲可信用戶的子集明確啓用它們。

可以使用@Restricted註釋標記處理器,控制器服務或報告任務。這將導致組件被視爲受限制,並且需要將用戶顯式添加到可以訪問受限組件的用戶列表中。一旦允許用戶訪問受限制的組件,就可以允許他們創建和修改這些組件,前提是允許所有其他權限。如果不訪問受限制的組件,用戶仍然會知道存在這些類型的組件,但即使有足夠的權限,也無法創建或修改它們。

狀態管理(State Manager)

從ProcessContext,ReportingContext和ControllerServiceInitializationContext,組件能夠調用該getStateManager()方法。此狀態管理器負責提供用於存儲和檢索狀態的簡單API。此機制旨在使開發人員能夠非常輕鬆地存儲一組鍵/值對,檢索這些值並以原子方式更新它們。狀態可以存儲在節點的本地,也可以存儲在集羣中的所有節點上。然而,重要的是要注意,該機制僅用於提供存儲非常“簡單”狀態的機制。因此,API只允許aMap<String, String>存儲和檢索,以及原子地替換整個Map。此外,ZooKeeper支持當前支持存儲羣集範圍狀態的唯一實現。因此,序列化後,整個State Map的大小必須小於1 MB。嘗試存儲超過此數量將導致拋出異常。如果處理器管理狀態所需的交互比這更復雜(例如,必須存儲和檢索大量數據,或者必須單獨存儲和提取單個密鑰),則應使用不同的機制(例如,與之通信)外部數據庫)。

範圍(Scope)

與狀態管理器通信時,所有方法調用都要求提供範圍。本範圍將是Scope.LOCALScope.CLUSTER。如果NiFi在羣集中運行,則此Scope爲框架提供有關如何進行操作的重要信息。

如果使用存儲狀態Scope.CLUSTER,則集羣中的所有節點將與相同的狀態存儲機制進行通信。如果使用存儲和檢索狀態Scope.LOCAL,則每個節點將看到狀態的不同表示。

還值得注意的是,如果將NiFi配置爲作爲獨立實例運行,而不是在羣集中運行,Scope.LOCAL則始終使用範圍。這樣做是爲了允許NiFi組件的開發者以一致的方式編寫代碼,而不用擔心NiFi實例是否是羣集的。開發人員應該假設該實例是集羣的並相應地編寫代碼。

存儲和檢索狀態(Storing and Retrieving State)

國家正在使用StateManager的存儲getStatesetStatereplace,和clear方法。所有這些方法都要求提供範圍。應該注意,與Local作用域一起存儲的狀態與使用Cluster作用域存儲的狀態完全不同。如果處理器使用作用域使用My Key的鍵存儲值Scope.CLUSTER,然後嘗試使用Scope.LOCAL作用域檢索該值,則檢索的值將是null(除非使用Scope.CLUSTER作用域同時存儲了一個值)。每個處理器的狀態與其他處理器的狀態隔離存儲。

因此,兩個處理器不能共享相同的狀態。但是,在某些情況下,非常有必要在兩個不同類型的處理器或兩個相同類型的處理器之間共享狀態。這可以通過使用Controller服務來完成。通過從Controller Service存儲和檢索狀態,多個處理器可以使用相同的Controller Service,並且可以通過Controller Service的API公開狀態。

單元測試(Unit Tests)

NiFi的Mock Framework提供了大量工具來執行處理器的單元測試。處理器單元測試通常從TestRunner課程開始。因此,TestRunner該類包含getStateManager自己的方法。但是,返回的StateManager具有特定類型:MockStateManager。除了StateManager接口定義的方法之外,此實現還提供了幾種方法,可幫助開發人員更輕鬆地開發單元測試。

首先,MockStateManager實現StateManager接口,因此可以在單元測試中檢查所有狀態。此外,MockStateManager公開了一些assert*方法來執行狀態按預期設置的斷言。MockStateManager如果針對特定情況更新狀態,還提供指示單元測試應立即失敗的能力 Scope

報告處理器活動(Reporting Processor Activity)

處理器負責報告其活動,以便用戶能夠了解其數據發生了什麼。處理器應該通過ComponentLog記錄事件,ComponentLog可以通過InitializationContext或通過調用getLogger方法來訪問AbstractProcessor

另外,處理器應該使用ProvenanceReporter 通過ProcessSession獲得的接口 getProvenanceReporter方法。ProvenanceReporter應用於指示從外部源接收內容或發送到外部位置的任何時間。ProvenanceReporter還具有報告何時克隆,分叉或修改FlowFile,以及將多個FlowFile合併到單個FlowFile以及將FlowFile與其他標識符相關聯時的報告方法。但是,這些功能對於報告來說不那麼重要,因爲框架能夠檢測這些功能並代表處理器發出適當的事件。然而,處理器開發人員發佈這些事件是一種最佳實踐,因爲它在代碼中顯式地發出了這些事件,並且開發人員能夠爲事件提供其他詳細信息,例如該行動採取了有關所採取行動的相關信息。如果處理器發出事件,則框架不會發出重複事件。相反,它總是假設處理器開發人員比框架更好地瞭解處理器上下文中發生的事情。但是,該框架可能會發出不同的事件。例如,如果處理器修改FlowFile的內容及其屬性,然後僅發出ATTRIBUTES_MODIFIED事件,則框架將發出CONTENT_MODIFIED事件。如果爲該FlowFile(由處理器或框架)發出任何其他事件,框架將不會發出ATTRIBUTES_MODIFIED事件。這是因爲所有的事實 它總是假設處理器開發人員比框架更好地瞭解處理器上下文中發生的事情。但是,該框架可能會發出不同的事件。例如,如果處理器修改FlowFile的內容及其屬性,然後僅發出ATTRIBUTES_MODIFIED事件,則框架將發出CONTENT_MODIFIED事件。如果爲該FlowFile(由處理器或框架)發出任何其他事件,框架將不會發出ATTRIBUTES_MODIFIED事件。這是因爲所有的事實 它總是假設處理器開發人員比框架更好地瞭解處理器上下文中發生的事情。但是,該框架可能會發出不同的事件。例如,如果處理器修改FlowFile的內容及其屬性,然後僅發出ATTRIBUTES_MODIFIED事件,則框架將發出CONTENT_MODIFIED事件。如果爲該FlowFile(由處理器或框架)發出任何其他事件,框架將不會發出ATTRIBUTES_MODIFIED事件。這是因爲所有的事實 如果爲該FlowFile(由處理器或框架)發出任何其他事件,框架將不會發出ATTRIBUTES_MODIFIED事件。這是因爲所有的事實 如果爲該FlowFile(由處理器或框架)發出任何其他事件,框架將不會發出ATTRIBUTES_MODIFIED事件。這是因爲所有的事實 Provenance Events瞭解事件發生之前FlowFile的屬性以及由於FlowFile處理而發生的屬性,因此ATTRIBUTES_MODIFIED通常被認爲是冗餘的,並且會導致FlowFile譜系的呈現非常冗長。但是,如果從處理器的角度來看事件被認爲是相關的,則處理器可以與其他事件一起發出此事件。

記錄組件

NiFi通過用戶界面從NiFi應用程序本身向用戶提供大量文檔,試圖使用戶體驗儘可能簡單方便。當然,爲了實現這一點,處理器開發人員必須向框架提供該文檔。NiFi提供了一些不同的機制來爲框架提供文檔。

記錄屬性

可以通過調用description PropertyDescriptor構建器的方法來記錄單個屬性:

  1.  
    public static final PropertyDescriptor MY_PROPERTY = new PropertyDescriptor.Builder()
  2.  
    .name("My Property")
  3.  
    .description("Description of the Property")
  4.  
    ...
  5.  
    .build();

如果屬性要提供一組允許值,那麼這些值將在UI的下拉字段中顯示給用戶。這些值中的每一個也可以給出描述:

  1.  
    public static final AllowableValue EXTENSIVE = new AllowableValue("Extensive", "Extensive",
  2.  
    "Everything will be logged - use with caution!");
  3.  
    public static final AllowableValue VERBOSE = new AllowableValue("Verbose", "Verbose",
  4.  
    "Quite a bit of logging will occur");
  5.  
    public static final AllowableValue REGULAR = new AllowableValue("Regular", "Regular",
  6.  
    "Typical logging will occur");
  7.  
     
  8.  
    public static final PropertyDescriptor LOG_LEVEL = new PropertyDescriptor.Builder()
  9.  
    .name("Amount to Log")
  10.  
    .description("How much the Processor should log")
  11.  
    .allowableValues(REGULAR, VERBOSE, EXTENSIVE)
  12.  
    .defaultValue(REGULAR.getValue())
  13.  
    ...
  14.  
    .build();

記錄關係(Documenting Relationships)

處理器關係的記錄方式與屬性大致相同 - 通過調用descriptionRelationship的構建器的方法:

  1.  
    public static final Relationship MY_RELATIONSHIP = new Relationship.Builder()
  2.  
    .name("My Relationship")
  3.  
    .description("This relationship is used only if the Processor fails to process the data.")
  4.  
    .build();

記錄能力和關鍵詞(Documenting Capability and Keywords)

org.apache.nifi.annotations.documentation包提供了可用於記錄組件的Java註釋。CapabilityDescription註釋可以添加到處理器,報告任務或控制器服務中,旨在提供組件提供的功能的簡要說明。標籤註釋有一個value被定義爲字符串數組的變量。因此,它通過提供多個值作爲逗號分隔的帶有花括號的字符串列表來使用。然後,通過允許用戶基於標籤(即關鍵字)過濾組件,將這些值合併到UI中。此外,UI提供了一個標籤雲,允許用戶選擇他們想要過濾的標籤。雲中最大的標籤是那些在NiFi實例中的組件上最多的標籤。下面提供了使用這些註釋的示例:

  1.  
    @Tags({"example", "documentation", "developer guide", "processor", "tags"})
  2.  
    @CapabilityDescription("Example Processor that provides no real functionality but is provided" +
  3.  
    " for an example in the Developer Guide")
  4.  
    public static final ExampleProcessor extends Processor {
  5.  
    ...
  6.  
    }

記錄FlowFile屬性交互

很多時候,處理器會期望在入站的FlowFiles中設置某些FlowFile屬性,以使處理器正常運行。在其他情況下,處理器可以在出站的FlowFile上更新或創建FlowFile屬性。處理器開發人員可以使用ReadsAttributeWritesAttribute文檔註釋記錄這兩種行爲。這些屬性用於生成文檔,使用戶可以更好地瞭解處理器如何與流進行交互。

注意:由於Java 7不支持對類型重複註釋,因此您可能需要使用ReadsAttributesWritesAttributes指示處理器讀取或寫入多個FlowFile屬性。此批註只能應用於處理器。下面列出了一個例子:

  1.  
    @WritesAttributes({ @WritesAttribute(attribute = "invokehttp.status.code", description = "The status code that is returned"),
  2.  
    @WritesAttribute(attribute = "invokehttp.status.message", description = "The status message that is returned"),
  3.  
    @WritesAttribute(attribute = "invokehttp.response.body", description = "The response body"),
  4.  
    @WritesAttribute(attribute = "invokehttp.request.url", description = "The request URL"),
  5.  
    @WritesAttribute(attribute = "invokehttp.tx.id", description = "The transaction ID that is returned after reading the response"),
  6.  
    @WritesAttribute(attribute = "invokehttp.remote.dn", description = "The DN of the remote server") })
  7.  
    public final class InvokeHTTP extends AbstractProcessor {

通常,處理器和控制器服務彼此相關。有時它是PutFile和/中的put / get關係GetFile。有時,處理器使用類似InvokeHTTP和的ControllerService StandardSSLContextService。有時一個ControllerService使用另一個DistributedMapCacheClientServiceDistributedMapCacheServer。這些擴展點的開發者可以使用SeeAlso標籤來關聯這些不同的組件。此註釋將文檔中的這些組件鏈接起來。 SeeAlso可以應用於處理器,ControllerServices和ReportingTasks。下面列出瞭如何執行此操作的示例:

  1.  
    @SeeAlso(GetFile.class)
  2.  
    public class PutFile extends AbstractProcessor {

高級文檔

當上述文檔方法不足時,NiFi可以通過“使用”文檔向用戶公開更高級的文檔。當用戶右鍵單擊處理器時,NiFi在上下文菜單中提供“使用”菜單項。此外,UI在右上角顯示“幫助”鏈接,從中可以找到相同的使用信息。

處理器的高級文檔以名爲的HTML文件的形式提供additionalDetails.html。此文件應存在於名稱爲Processor的完全限定名稱的目錄中,並且應該命名此目錄的父級 docs並存在於Processor的jar的根目錄中。此文件將從生成的HTML文件鏈接,該文件將包含所有Capability,Keyword,PropertyDescription和Relationship信息,因此沒有必要複製該文件。這個地方可以提供有關此處理器正在執行的操作,預期和生成的數據類型以及預期和生成的FlowFile屬性的豐富說明。由於此文檔採用HTML格式,因此您可以包含圖像和表格以最好地描述此組件。可以使用相同的方法爲Processors,ControllerServices和ReportingTasks提供高級文檔。

 

源事件

源報告的不同事件類型是:

源事件描述

ADDINFO

表示用於添加其他信息(例如新鏈接到新URI或UUID)的起源事件

ATTRIBUTES_MODIFIED

表示以某種方式修改了FlowFile的屬性。當同時報告另一個事件時,不需要此事件,因爲另一個事件已包含所有FlowFile屬性

CLONE

表示FlowFile與其父FlowFile完全相同

CONTENT_MODIFIED

表示以某種方式修改了FlowFile的內容。使用此事件類型時,建議您提供有關如何修改內容的詳細信息

CREATE

表示FlowFile是從未從遠程系統或外部進程接收的數據生成的

DOWNLOAD

表示用戶或外部實體下載了FlowFile的內容

DROP

表示由於對象到期之外的某些原因導致對象生命結束的起源事件

EXPIRE

表示由於未及時處理對象而導致對象生命結束的起源事件

FETCH  
FORK

表示一個或多個FlowFiles是從父FlowFile派生的

JOIN

表示單個FlowFile是通過將多個父FlowFiles連接在一起而派生的

RECEIVE

表示從外部進程接收數據的來源事件。此事件類型應該是FlowFile的第一個事件。因此,從外部源接收數據並使用該數據替換現有FlowFile內容的處理器應使用FETCH事件類型,而不是RECEIVE事件類型

REPLAY

表示重放FlowFile的originance事件。事件的UUID指示正在重播的原始FlowFile的UUID。該事件包含一個父UUID,它也是正在重放的FlowFile的UUID,以及一個子UUID,它是新創建的FlowFile的UUID,它將被重新排隊等待處理

ROUTE

表示FlowFile已路由到指定的關係,並提供有關FlowFile路由到此關係的原因的信息

SEND

表示將數據發送到外部進程的originance事件

UNKNOWN

表示原產地事件的類型未知,因爲嘗試訪問該事件的用戶無權知道該類型

通用處理器模式( Common Processor Patterns )

雖然NiFi用戶可以使用許多不同的處理器,但絕大多數處理器屬於幾種常見的設計模式之一。下面,我們討論這些模式,模式是否合適,我們遵循這些模式的原因,以及應用此類模式時需要注意的事項。請注意,下面討論的模式和建議是一般指導原則,而不是強化規則。

數據入口( Data Ingress )

將數據提取到NiFi中的處理器具有一個名爲的關係success。此處理器通過ProcessSession create方法生成新的FlowFiles,並且不從傳入的Connections中提取FlowFiles。處理器名稱以“Get”或“Listen”開頭,具體取決於它是輪詢外部源還是公開某些外部源可以連接的接口。名稱以用於通信的協議結束。遵循這種模式的處理器包括GetFileGetSFTP, ListenHTTP,和GetHTTP

此處理器可以在使用@OnScheduled註釋的方法中創建或初始化連接池 。但是,由於通信問題可能會阻止建立連接或導致連接終止,因此此時不會創建連接本身。而是在onTrigger方法中從池創建或租用連接。

onTrigger此處理器的方法首先從連接池租用連接(如果可能),或以其他方式創建與外部服務的連接。當沒有來自外部源的數據時,yieldProcessContext 的方法由處理器調用,並且該方法返回,以便該處理器避免持續運行和耗盡資源而沒有任何好處。否則,此處理器然後通過ProcessSession的create 方法創建FlowFile,併爲FlowFile 分配適當的文件名和路徑(通過添加filenamepath 屬性),以及可能適當的任何其他屬性。通過ProcessSession獲取FlowFile內容的OutputStreamwrite方法,傳遞一個新的OutputStreamCallback(通常是一個匿名的內部類)。在此回調中,處理器能夠寫入FlowFile並將內容從外部資源流式傳輸到FlowFile的OutputStream。如果希望將InputStream的整個內容寫入FlowFile,則importFromProcessSession 的方法可能比write方法更方便 。

當此處理器希望接收許多小文件時,建議在提交會話之前從單個會話創建多個FlowFiles。通常,這允許框架更有效地處理新創建的FlowFiles的內容。

此處理器生成一個Provenance事件,指示它已接收數據並指定數據來自何處。此處理器應記錄FlowFile的創建,以便可以通過分析日誌來確定FlowFile的來源(如有必要)。

此處理器確認收到數據和/或從外部源中刪除數據,以防止接收重複文件。只有在創建了FlowFile的ProcessSession提交後才能執行此操作!如果不遵守此原則可能會導致數據丟失,因爲在提交會話之前重新啓動NiFi將導致臨時文件被刪除。但請注意,使用此方法可以接收重複數據,因爲應用程序可以在提交會話之後以及在確認或從外部源中刪除數據之前重新啓動。但是,一般而言,潛在的數據重複優於潛在的數據丟失。最終返回連接或將連接添加到連接池,具體取決於連接是從連接池租用還是在onTrigger方法中創建。

如果存在通信問題,則通常會終止連接,並且不會將連接返回(或添加)到連接池。在使用註釋註釋的方法中拆除與遠程系統的連接並關閉連接池,@OnStopped以便可以回收資源。

數據出口(Data Egress)

將數據發佈到外部源的處理器有兩個關係:successfailure。處理器名稱以“Put”開頭,後跟用於數據傳輸的協議。遵循這種模式的處理器包括PutEmail,, PutSFTP和 PostHTTP(請注意,該名稱不以“Put”開頭,因爲這會導致混淆,因爲PUT和POST在處理HTTP時具有特殊含義)。

此處理器可以在使用@OnScheduled註釋的方法中創建或初始化連接池 。但是,由於通信問題可能會阻止建立連接或導致連接終止,因此此時不會創建連接本身。而是在onTrigger方法中從池創建或租用連接。

onTrigger方法首先通過該get方法從ProcessSession獲取FlowFile 。如果沒有可用的FlowFile,則該方法返回而不獲取與遠程資源的連接。

如果至少有一個FlowFile可用,則處理器會從連接池獲取連接(如果可能),或以其他方式創建新連接。如果處理器既不能從連接池租用連接也不能創建新連接,則FlowFile將路由到failure,記錄事件,並返回方法。

如果獲得了連接,則處理器通過調用readProcessSession上的方法並傳遞InputStreamCallback(通常是匿名內部類)來獲取FlowFile內容 的InputStream,並從該回調中將FlowFile的內容傳輸到目標。記錄事件以及傳輸文件所花費的時間和傳輸文件的數據速率。通過getProvenanceReporter方法從ProcessSession獲取報告器並在報告器上調用send方法,向ProvenanceReporter報告SEND事件 。連接將返回或添加到連接池,具體取決於連接是從池租用還是由onTrigger方法新創建 。

如果存在通信問題,則通常會終止連接,並且不會將連接返回(或添加)到連接池。如果將數據發送到遠程資源時出現問題,則處理錯誤的所需方法取決於一些注意事項。如果問題與網絡狀況有關,則FlowFile通常會路由到failure。FlowFile不會受到懲罰,因爲數據沒有必要存在問題。與數據入口處理器的情況不同,我們通常不會調用yieldProcessContext。這是因爲在攝取的情況下,在處理器能夠執行其功能之前,FlowFile不存在。但是,對於Put Processor,DataFlow Manager可以選擇路由failure到不同的處理器。這可以允許在一個系統出現問題的情況下使用“備份”系統,或者可以用於跨多個系統的負載分配。

如果出現與數據相關的問題,則應採取兩種方法之一。首先,如果問題可能會解決,FlowFile會受到懲罰,然後路由到 failure。例如,當使用PutFTP時,由於文件命名衝突而無法傳輸FlowFile。假設最終將從目錄中刪除該文件,以便可以傳輸新文件。因此,我們會懲罰FlowFile並路由到 failure以後我們可以再試一次。在另一種情況下,如果數據存在實際問題(例如數據不符合某些要求的規範),則可以採用不同的方法。在這種情況下,將failure關係分解爲a failure和a 可能是有利的 communications failure關係。這允許DataFlow Manager確定如何單獨處理這些情況。在這些情況下,通過在創建關係時在“描述”中對其進行澄清,很好地記錄兩個關係之間的差異。

與遠程系統的連接被拆除,並且連接池在註釋的方法中關閉,@OnStopped以便可以回收資源。

基於內容的路由(一對一) Route Based on Content (One-to-One)

根據內容路由數據的處理器將採用以下兩種形式之一:將傳入的FlowFile路由到恰好一個目標,或將傳入數據路由到0個或更多目標。在這裏,我們將討論第一個案例。

此處理器有兩種關係:matchedunmatched。如果需要特定的數據格式,則處理器還將具有failure在輸入不是預期格式時使用的關係。處理器公開一個指示路由標準的屬性。

如果指定路由條件的屬性需要處理(例如編譯正則表達式),則此處理將在註釋的方法中完成@OnScheduled,如果可能的話。然後將結果存儲在標記爲的成員變量中volatile

onTrigger方法獲得單個FlowFile。該方法通過ProcessSession的方法讀取FlowFile的內容,read 在數據流傳輸時評估匹配條件。然後,處理器確定FlowFile是應該路由到matched還是unmatched基於條件是否匹配,並將FlowFile路由到適當的關係。

然後,處理器發出一個Provenance ROUTE事件,指示處理器將FlowFile路由到哪個關係。

此處理器使用 包中的註釋@SideEffectFree和 @SupportsBatching註釋進行註釋org.apache.nifi.annotations.behavior

基於內容的路由(一對多)  Route Based on Content (One-to-Many)

如果處理器將單個FlowFile路由到可能的許多關係,則此處理器將與上述基於內容的路由數據處理器略有不同。此處理器通常具有由用戶動態定義的unmatched關係以及關係。

爲了使用戶能夠另外定義Properties,getSupportedDynamicPropertyDescriptor必須重寫該方法。此方法返回帶有提供的名稱和適用的Validator的PropertyDescriptor,以確保用戶指定的匹配條件有效。

在此處理器中,getRelationships方法返回的關係集 是標記的成員變量volatile。此Set最初構造爲一個名爲的Relationship unmatchedonPropertyModified重寫該方法,以便在添加或刪除屬性時,創建具有相同名稱的新關係。如果處理器具有非用戶定義的屬性,則檢查指定的屬性是否是用戶定義的非常重要。這可以通過調用來實現isDynamic傳遞給此方法的PropertyDescriptor的方法。如果此屬性是動態的,則會創建一組新的關係,並將先前的一組關係複製到其中。這個新Set要麼添加了新創建的關係,要麼從中刪除了,具體取決於是否將新屬性添加到處理器或刪除了屬性(通過檢查是否有此函數的第三個參數來檢測屬性刪除null)。然後更新包含“關係集”的成員變量以指向此新集。

如果指定路由條件的屬性需要處理(例如編譯正則表達式),則此處理將在使用註釋的方法中完成@OnScheduled(如果可能)。然後將結果存儲在標記爲的成員變量中volatile。此成員變量通常是Map鍵類型的類型Relationship,值的類型由處理屬性值的結果定義。

onTrigger方法通過getProcessSession 的方法獲得FlowFile 。如果沒有FlowFile可用,則立即返回。否則,將創建一組類型關係。該方法通過ProcessSession的方法讀取FlowFile的內容,read在數據流傳輸時評估每個匹配條件。對於匹配的任何條件,與該匹配條件關聯的關係將添加到“關係集”中。

在讀取FlowFile的內容後,該方法檢查“關係集”是否爲空。如果是這樣,原始FlowFile會添加一個屬性,以指示它被路由到的關係並被路由到unmatched。記錄此信息,發出一個Provenance ROUTE事件,該方法返回。如果Set的大小等於1,則原始FlowFile會添加一個屬性,以指示它所路由的關係,並路由到Set中條目指定的Relationship。記錄此信息,爲FlowFile發出一個Provenance ROUTE事件,該方法返回。

如果Set包含多個Relationship,則Processor會爲每個Relationship創建一個FlowFile的副本,第一個除外。這是通過cloneProcessSession 的方法完成的。無需報告CLONE Provenance事件,因爲框架將爲您處理此事。原始FlowFile和每個克隆被路由到其適當的關係,其中屬性指示關係的名稱。爲每個FlowFile發出一個Provenance ROUTE事件。記錄此信息,方法返回。

此處理器使用 包中的註釋@SideEffectFree和 @SupportsBatching註釋進行註釋org.apache.nifi.annotations.behavior

基於內容的路由流(一對多)

先前對基於內容的路由(一對多)的描述提供了用於創建非常強大的處理器的抽象。但是,它假定每個FlowFile將完整路由到零個或多個關係。如果傳入的數據格式是許多不同信息的“流”,我們希望將此流的不同部分發送到不同的關係,該怎麼辦?例如,假設我們想要一個RouteCSV處理器,以便配置多個正則表達式。如果CSV文件中的一行與正則表達式匹配,則該行應包含在出站FlowFile中與關聯關係中。如果正則表達式與關係“has-apples”相關聯且正則表達式與FlowFile中的1,000行匹配,則應該有一個出站FlowFile用於“has-apples”關係,其中包含1,000行。如果不同的正則表達式與關係“has-oranges”相關聯並且正則表達式與FlowFile中的50行匹配,則應該有一個出站FlowFile用於“has-oranges”關係,其中包含50行。即,一個FlowFile進來,兩個FlowFiles問世。兩個FlowFiles可能包含原始FlowFile中的一些相同的文本行,或者它們可能完全不同。這是我們將在本節中討論的處理器類型。

此處理器的名稱以“Route”開頭,並以其路由的數據類型的名稱結束。在我們的示例中,我們正在路由CSV數據,因此處理器名爲RouteCSV。此處理器支持動態屬性。每個用戶定義的屬性都有一個映射到關係名稱的名稱。Property的值採用“Match Criteria”所需的格式。在我們的示例中,屬性的值必須是有效的正則表達式。

此處理器維護一個內部ConcurrentMap,其中鍵是a Relationship,值的類型取決於匹配條件的格式。在我們的例子中,我們將保持一個 ConcurrentMap<Relationship, Pattern>。此處理器會覆蓋該 onPropertyModified方法。如果提供給此方法的新值(第三個參數)爲null,則將從ConcurrentMap中刪除名稱由屬性名稱(第一個參數)定義的Relationship。否則,處理新值(在我們的示例中,通過調用Pattern.compile(newValue)),並將此值添加到ConcurrentMap,其中鍵再次爲其名稱由屬性名稱指定的Relationship。

此處理器將覆蓋該customValidate方法。在此方法中,它將從中檢索所有屬性ValidationContext並計算動態PropertyDescriptors的數量(通過調用isDynamic() PropertyDescriptor)。如果動態PropertyDescriptors的數量爲0,則表示用戶尚未添加任何關係,因此處理器返回 ValidationResult指示處理器無效的因爲它沒有添加關係。

處理器在getRelationships調用其方法時返回用戶指定的所有關係,並且還將返回unmatched關係。因爲此處理器必須讀取和寫入內容存儲庫(這可能相對昂貴),如果預期此處理器將用於非常高的數據量,則添加允許用戶指定是否有用的屬性可能是有利的。他們是否關心與任何匹配標準不匹配的數據。

onTrigger調用該方法時,處理器獲取FlowFile via ProcessSession.get。如果沒有可用數據,則處理器返回。否則,處理器創建一個Map<Relationship, FlowFile>。我們將此地圖稱爲flowFileMap。處理器通過調用讀取傳入的FlowFile ProcessSession.read 並提供InputStreamCallback。在回調中,處理器從FlowFile讀取第一個數據。然後,處理器根據此數據評估每個匹配條件。如果特定條件(在我們的示例中,正則表達式)匹配,則處理器從中獲取flowFileMap屬於適當關係的FlowFile 。如果此關係的Map中尚不存在FlowFile,則處理器通過調用創建新的FlowFile session.create(incomingFlowFile),然後將新的FlowFile添加到flowFileMap。然後,處理器通過調用寫入這一塊數據到FlowFile的session.append 用OutputStreamCallback。從這個OutputStreamCallback中,我們可以訪問新的FlowFile的OutputStream,因此我們可以將數據寫入新的FlowFile。然後我們從OutputStreamCallback返回。在遍歷每個匹配條件之後,如果它們都不匹配,我們對unmatched關係執行與上述相同的例程 (除非用戶將我們配置爲不寫出不匹配的數據)。現在我們已經調用了session.append,我們有了一個新版本的FlowFile。因此,我們需要更新我們flowFileMap以將關係與新的FlowFile相關聯。

如果在任何時候拋出異常,我們將需要將傳入的FlowFile路由到failure。我們還需要刪除每個新創建的FlowFiles,因爲我們不會將它們轉移到任何地方。我們可以通過調用做到這一點session.remove(flowFileMap.values())。此時,我們將記錄錯誤並返回。

否則,如果一切都成功,我們現在可以迭代 flowFileMap並將每個FlowFile傳輸到相應的關係。然後刪除原始FlowFile或將其路由到original關係。對於每個新創建的FlowFiles,我們還會發出一個Provenance ROUTE事件,指示FlowFile前往哪個關係。在ROUTE事件的詳細信息中包含此FlowFile中包含多少條信息也很有幫助。這使得DataFlow Manager可以在查看Provenance Lineage視圖時輕鬆查看給定輸入FlowFile的每個關係有多少信息。

此外,某些處理器可能需要“分組”發送到每個關係的數據,以便發送到關係的每個FlowFile具有相同的值。在我們的示例中,我們可能希望允許正則表達式具有捕獲組,並且如果CSV中的兩個不同行與正則表達式匹配但捕獲組具有不同的值,我們希望將它們添加到兩個不同的FlowFiles。然後可以將匹配值作爲屬性添加到每個FlowFile。這可以通過修改來實現flowFileMap,使得其被定義爲Map<Relationship, Map<T, FlowFile>>其中T是分組功能的類型(在我們的例子中,該集團將是一個String,因爲它是評價一個正則表達式的捕獲組的結果)。

基本路由屬性 (Route Based on Attributes)

該處理器幾乎與上述基於內容處理器的路由數據相同。它採用兩種不同的形式:一對一和一對多,基於內容的路由處理器也是如此。但是,此處理器不會調用ProcessSession的read 方法,因爲它不會讀取FlowFile內容。此處理器通常非常快,因此@SupportsBatching在這種情況下注釋非常重要。

拆分內容(一對多)

此處理器通常不需要用戶配置,除了要創建的每個Split的大小。該onTrigger方法從其輸入隊列獲取FlowFile。創建FlowFile類型的列表。通過ProcessSession的read方法讀取原始FlowFile ,並使用InputStreamCallback。在InputStreamCallback中,讀取內容直到達到FlowFile應該被拆分的點。如果不需要拆分,則返回Callback,並將原始FlowFile路由到success。在這種情況下,會發出一個Provenance ROUTE事件。通常,在將FlowFile路由到時不會發出ROUTE事件success因爲這會產生一個非常冗長的血統,變得難以導航。但是,在這種情況下,事件是有用的,因爲否則我們會期望FORK事件,並且沒有任何事件可能會引起混淆。將記錄FlowFile未拆分但轉移到的事實success,並返回該方法。

如果達到需要拆分FlowFile的點,則通過ProcessSession的create(FlowFile)方法或 clone(FlowFile, long, long)方法創建新的FlowFile 。下一部分代碼取決於create是使用該clone方法還是使用該方法。兩種方法如下所述。必須根據具體情況確定哪種解決方案是合適的。

當數據不能直接從原始FlowFile複製到新的FlowFile時,Create Method最合適。例如,如果僅複製某些數據,或者在複製到新FlowFile之前將以某種方式修改數據,則此方法是必需的。但是,如果新FlowFile的內容將是原始FlowFile的一部分的精確副本,則克隆方法是更優選的。

創建方法 如果使用該create方法,則使用原始FlowFile作爲參數調用該方法,以便新創建的FlowFile將繼承原始FlowFile的屬性,並且框架將創建Provenance FORK事件。

然後代碼進入一個try/finally塊。在finally 塊中,新創建的FlowFile將添加到已創建的FlowFile列表中。這是在一個finally塊內完成的,這樣如果拋出異常,將適當地清理新創建的FlowFile。在try塊中,回調通過write使用OutputStreamCallback 調用ProcessSession的方法來啓動新的回調。然後將適當的數據從原始FlowFile的InputStream複製到新FlowFile的OutputStream。

克隆方法 如果新創建的FlowFile的內容只是原始FlowFile的字節的連續子集,則最好使用該clone(FlowFile, long, long)方法而不是create(FlowFile)ProcessSession 的 方法。在這種情況下,新FlowFile內容應該開始的原始FlwoFile的偏移量將作爲方法的第二個參數傳遞clone。新FlowFile的長度作爲方法的第三個參數傳遞clone 。例如,如果原始FlowFile是10,000字節並且我們調用clone(flowFile, 500, 100)了,那麼返回給我們的FlowFile將與flowFile關於它的屬性。但是,新創建的FlowFile的內容長度爲100個字節,並且將從原始FlowFile的偏移量500開始。也就是說,新創建的FlowFile的內容與您複製原始FlowFile的字節500到599的內容相同。

創建克隆後,它將添加到FlowFiles列表中。

在適用的情況下,此方法比Create方法更受歡迎,因爲不需要磁盤I / O. 該框架能夠簡單地創建一個新的FlowFile,它引用原始FlowFile內容的一個子集,而不是實際複製數據。但是,這並不總是可行的。例如,如果必須從原始FlowFile的開頭複製標題信息並將其添加到每個Split的開頭,則無法使用此方法。

兩種方法 無論使用克隆方法還是創建方法,以下內容均適用:

如果在InputStreamCallback中的任何點處,達到了無法繼續處理的條件(例如,輸入格式錯誤),ProcessException則應拋出a。對ProcessSession read方法的調用包含在一個被捕獲的try/catch塊中ProcessException。如果捕獲到異常,則會生成一條說明錯誤的日誌消息。通過ProcessSession remove 方法刪除新創建的FlowFiles列表。原始FlowFile被路由到failure

如果沒有問題,則將原始FlowFile路由到,original 並更新所有新創建的FlowFiles以包含以下屬性:

屬性名稱描述

split.parent.uuid

原始FlowFile的UUID

split.index

一個單一的數字,表示列表中的哪個FlowFile(創建的第一個FlowFile將具有值0,第二個將具有值1,等等)

split.count

已創建的拆分FlowFiles的總數

新創建的FlowFiles被路由到success; 記錄此事件; 並且該方法返回。

根據內容更新屬性

此處理器與上面討論的基於內容處理器的路由非常相似。而不是將FlowFile路由到matchedunmatched,FlowFile通常路由到success或者failure 將屬性添加到FlowFile中。要添加的屬性的配置方式類似於基於內容的路由(一對多),用戶可以定義自己的屬性。屬性的名稱表示要添加的屬性的名稱。屬性的值指示要應用於數據的一些匹配條件。如果匹配條件與數據匹配,則添加一個名稱與Property屬性相同的屬性。屬性的值是匹配內容的條件。

例如,評估XPath表達式的處理器可能允許輸入用戶定義的XPath。如果XPath與FlowFile的內容匹配,則該FlowFile將添加一個屬性,該屬性的名稱等於Property name的名稱,並且該值等於與XPath匹配的XML Element或Attribute的文本內容。failure如果在此示例中傳入的FlowFile不是有效的XML,則將使用該關係。success無論是否找到任何匹配,都將使用該關係。然後,可以在適當時使用它來路由FlowFile。

此處理器發出類型爲ATTRIBUTES_MODIFIED的源事件。

豐富/修改內容

Enrich / Modify Content模式非常常見且非常通用。此模式負責任何一般內容修改。對於大多數情況,此處理器標有@SideEffectFree@SupportsBatching註釋。處理器具有任意數量的必需屬性和可選屬性,具體取決於處理器的功能。處理器通常具有successfailure關係。failure當輸入文件不是預期格式時,通常使用該關係。

此處理器獲取FlowFile並使用ProcessSession的write(StreamCallback)方法對其進行更新,以便它能夠從FlowFile的內容中讀取並寫入FlowFile內容的下一個版本。如果在回調期間遇到錯誤,則回調將拋出一個ProcessException。對ProcessSession write方法的調用包含在一個try/catch塊中,該 塊捕獲ProcessException 並將FlowFile路由到失敗。

如果回調成功,則會發出CONTENT_MODIFIED Provenance事件。

錯誤處理

編寫處理器時,可能會出現幾種不同的意外情況。如果處理器本身不處理錯誤,處理器開發人員必須瞭解NiFi框架如何運行的機制,並且瞭解處理器預期的錯誤處理非常重要。在這裏,我們將討論處理器如何在工作過程中處理意外錯誤。

處理器中的例外情況

在執行onTrigger處理器的方法期間,許多事情可能會出錯。常見的故障情況包括:

  • 傳入的數據不是預期的格式。

  • 與外部服務的網絡連接失敗。

  • 讀取或寫入磁盤數據失敗。

  • 處理器或從屬庫中存在錯誤。

任何這些條件都可能導致從處理器拋出異常。從框架的角度來看,有兩種類型的異常可以逃脫處理器:ProcessException以及所有其他類型的異常。

如果從處理器拋出ProcessException,框架將假定這是一個已知結果的失敗。此外,嘗試稍後再次處理數據的條件可能是成功的。因此,框架將回滾正在處理的會話並懲罰正在處理的FlowFiles。

但是,如果任何其他Exception轉義處理器,則框架將假定它是開發人員未考慮的故障。在這種情況下,框架還將回滾會話並懲罰FlowFiles。但是,在這種情況下,我們可以進入一些非常有問題的案例。例如,處理器可能處於不良狀態並且可能持續運行,耗盡系統資源,而不提供任何有用的工作。這是相當常見的,例如,當連續拋出NullPointerException時。爲了避免這種情況,如果ProcessException以外的Exception能夠轉義處理器 onTrigger方法,框架也將“管理性地產生”處理器。這意味着處理器不會被觸發再運行一段時間。時間量在nifi.properties文件中配置,但默認爲10秒。

回調中的異常:IOException,RuntimeException

更通常情況下,當一個異常在處理器中發生時,它從一個回調內發生(即, InputStreamCallbackOutputStreamCallback,或StreamCallback)。也就是說,在處理FlowFile的內容時。回調被允許拋出RuntimeExceptionIOException。在RuntimeException的情況下,此異常將傳播回該onTrigger方法。在an的情況下 IOException,Exception將被包裝在ProcessException中,然後將從Framework拋出此ProcessException。

出於這個原因,建議使用回調的處理器在一個try/catch塊內執行,並捕獲與其期望回調拋出的ProcessException任何其他處理器RuntimeException。但是,不建議處理器捕獲一般ExceptionThrowable案例。由於兩個原因,這是不鼓勵的。

首先,如果拋出了意外的RuntimeException,它可能是一個錯誤並允許框架回滾會話將確保沒有數據丟失並確保DataFlow Manager能夠通過保持排隊的數據來處理他們認爲合適的數據地點。

其次,當從回調中拋出IOException時,實際上有兩種類型的IOExceptions:從處理器代碼拋出的那些(例如,數據不是預期的格式或網絡連接失敗),以及從那裏拋出的那些內容存儲庫(存儲FlowFile內容的位置)。如果是後一種情況,框架將捕獲此IOException並將其包裝到一個FlowFileAccessException擴展中RuntimeException。這是明確完成的,因此Exception將轉義onTrigger方法,框架可以適當地處理這個條件。捕獲常規異常可防止這種情況發生。

Penalization vs. Yielding

當在處理過程中出現問題時,框架公開了兩種方法,允許處理器開發人員避免執行不必要的工作:“懲罰”和“屈服”。對於剛接觸NiFi API的開發人員來說,這兩個概念可能會讓人感到困惑。開發人員可以通過調用來懲罰FlowFilepenalize(FlowFile)ProcessSession的方法。這導致FlowFile本身在一段時間內無法訪問下游處理器。FlowFile不可訪問的時間由DataFlow Manager通過在Processor Configuration對話框中設置“Penalty Duration”設置來確定。默認值爲30秒。通常,這是在處理器確定由於預期將自己排序的環境原因而無法處理數據時完成的。一個很好的例子是PutSFTP處理器,如果SFTP服務器上已存在具有相同文件名的文件,它將懲罰FlowFile。在這種情況下,處理器會懲罰FlowFile並將其路由到失敗。然後,DataFlow Manager可以將故障路由回相同的PutSFTP處理器。這樣,如果文件存在相同的文件名,處理器不會再次嘗試發送文件30秒(或DFM配置處理器使用的任何時間段)。與此同時,它能夠繼續處理其他FlowFiles。

另一方面,讓步允許處理器開發人員向框架指示它將在一段時間內不能執行任何有用的功能。這通常發生在與遠程資源通信的處理器上。如果處理器無法連接到遠程資源,或者如果遠程資源需要提供數據但報告它沒有,則處理器應調用yieldProcessContext對象然後返回。通過這樣做,處理器告訴框架它不應該浪費資源來觸發此處理器運行,因爲它無法做任何事情 - 最好使用這些資源來允許其他處理器運行。

會話回滾 ( Session Rollback )

到目前爲止,當我們討論ProcessSession過時,我們通常將其簡稱爲訪問FlowFiles的機制。但是,它提供了另一個非常重要的功能,即事務性。在ProcessSession上調用的所有方法都作爲事務發生。當我們決定結束交易時,我們可以通過電話 commit()或通過電話來完成rollback()。通常,這由AbstractProcessor類處理:如果onTrigger方法拋出異常,AbstractProcessor將捕獲異常,調用session.rollback(),然後重新拋出異常。否則,AbstractProcessor將調用commit()ProcessSession。

但是,有時候開發人員會希望顯式回滾會話。這可以通過調用rollback()rollback(boolean)方法隨時完成。如果使用後者,則布爾值表示從隊列中提取的那些FlowFiles(通過ProcessSession get方法)是否應該在被添加回隊列之前受到懲罰。

rollback被調用時,對發生在該屆會議上FlowFiles任何修改被丟棄,以既包括內容的修改和屬性修改。另外,所有的種源的事件被回滾(與通過使值發射的任何SEND事件之外trueforce參數)。從輸入隊列中提取的FlowFile然後被傳輸回輸入隊列(並且可選地被處罰),以便可以再次處理它們。

另一方面,當commit調用該方法時,FlowFile的新狀態將保留在FlowFile存儲庫中,並且發生的任何Provenance事件都會保留在Provenance存儲庫中。先前的內容被銷燬(除非另一個FlowFile引用相同的內容),並且FlowFiles被傳輸到出站隊列,以便下一個處理器可以對數據進行操作。

注意使用org.apache.nifi.annotations.behavior.SupportsBatching 註釋如何影響此行爲也很重要。如果處理器使用此註釋,則呼叫ProcessSession.commit可能不會立即生效。相反,這些提交可以一起批處理以提供更高的吞吐量。但是,如果處理器在任何時候回滾ProcessSession,則自上次調用以來的所有更改commit都將被丟棄,所有“批處理”提交都將生效。這些“批量”提交不會回滾。

一般設計考慮因素 ( General Design Considerations  )

在設計處理器時,需要考慮一些重要的設計。“開發人員指南”的這一部分將開發人員在創建處理器時應考慮的一些想法放在首位。

考慮用戶 (Consider the User)

開發處理器(或任何其他組件)時要記住的最重要的概念之一是您正在創建的用戶體驗。重要的是要記住,作爲這樣一個組件的開發者,您可能對其他人沒有的上下文有重要的瞭解。應始終提供文檔,以便不熟悉該過程的人員能夠輕鬆使用它。

在考慮用戶體驗時,同樣重要的是要注意一致性非常重要。最好堅持使用標準命名約定。這適用於處理器名稱,屬性名稱和值,關係名稱以及用戶將體驗的任何其他方面。

簡單至關重要!避免添加您不希望用戶理解或更改的屬性。作爲開發人員,我們被告知硬編碼值很糟糕。但這有時會導致開發人員暴露屬性,當要求澄清時,告訴用戶只保留默認值。這導致混亂和複雜性。

凝聚力和可重用性 (Cohesion and Reusability)

爲了製作單個,有凝聚力的單元,開發人員有時會試圖將多個功能組合到一個處理器中。對於處理器期望輸入數據採用格式X以便處理器可以將數據轉換爲格式Y並將新格式化的數據發送到某些外部服務的情況,情況確實如此。

採用這種方法格式化特定端點的數據,然後將數據發送到同一處理器內的該端點有幾個缺點:

  • 處理器變得非常複雜,因爲它必須執行數據轉換任務以及將數據發送到遠程服務的任務。

  • 如果處理器無法與遠程服務通信,它將把數據路由到failure關係。在這種情況下,處理器將負責再次執行數據轉換。如果它再次失敗,則翻譯再次完成。

  • 如果我們有五個不同的處理器在傳輸數據之前將傳入數據轉換爲這種新格式,那麼我們就會有大量重複的代碼。例如,如果架構發生更改,則必須更新許多處理器。

  • 當處理器完成發送到遠程服務時,將丟棄此中間數據​​。中間數據格式可能對其他處理器有用。

爲了避免這些問題,並使處理器更具可重用性,處理器應始終堅持“做一件事,做得好”的原則。這樣的處理器應該分成兩個獨立的處理器:一個用於將數據從格式X轉換爲格式Y,另一個處理器用於將數據發送到遠程資源。

命名約定 (Naming Conventions)

爲了向用戶提供一致的外觀和感覺,建議處理器遵守標準命名約定。以下是使用的標準約定列表:

  • 從遠程系統提取數據的處理器名爲Get <Service>或Get <Protocol>,具體取決於它們是否通過已知協議(例如GetHTTP或GetFTP)從任意源輪詢數據,或者它們是否從已知服務提取數據(比如GetKafka)

  • 將數據推送到遠程系統的處理器名爲Put <Service>或Put <Protocol>。

  • 關係名稱是低級的,並使用空格來描述單詞。

  • 屬性名稱大寫重要單詞,就像書名一樣。

處理器行爲註釋 (Processor Behavior Annotations)

在創建處理器時,開發人員能夠向框架提供有關如何最有效地利用處理器的提示。這是通過將註釋應用於Processor的類來完成的。可以應用於處理器的註釋存在於三個子包中org.apache.nifi.annotations。那些在documentation子包被用於提供文檔給用戶。lifecycle子包中的那些指示框架應該在處理器上調用哪些方法以響應適當的生命週期事件。behavior包中的那些幫助框架理解如何在調度和一般行爲方面與處理器交互。

org.apache.nifi.annotations.behavior包中的以下注釋可用於修改框架處理處理器的方式:

  • EventDriven:指示可以使用事件驅動的調度策略調度處理器的框架。此策略目前仍處於試驗階段,但可能導致數據流的資源利用率降低,而這些數據流無法處理極高的數據速率。

  • SideEffectFree:表示處理器在NiFi外部沒有任何副作用。因此,框架可以使用相同的輸入多次調用處理器,而不會導致任何意外結果。這意味着冪等行爲。框架可以使用它來通過執行諸如將ProcessSession從一個處理器轉移到另一個處理器之類的操作來提高效率,這樣如果出現問題,許多處理器的操作可以回滾並再次執行。

  • SupportsBatching:此註釋表明框架可以將多個ProcessSession提交批處理到單個提交中。如果存在此註釋,則用戶將能夠在“處理器的調度”選項卡中選擇是否更喜歡高吞吐量或更低延遲。此註釋應該應用於大多數處理器,但它有一個警告:如果處理器調用ProcessSession.commit,則無法保證數據已安全地存儲在NiFi的Content,FlowFile和Provenance Repositories中。因此,不適合那些從外部源接收數據,提交會話,然後刪除遠程數據或使用遠程資源確認事務的處理器。

  • TriggerSerially:當存在此註釋時,框架將不允許用戶一次調度多個併發線程來執行該onTrigger方法。相反,線程數(“併發任務”)將始終設置爲1。這並沒有,但是,意味着該處理器不必是線程安全的,因爲這是執行的線程onTrigger調用之間可能發生改變。

  • PrimaryNodeOnly:Apache NiFi在集羣時爲處理器提供兩種執行模式:“主節點”和“所有節點”。雖然在所有節點中運行可提供更好的並行性,但已知某些處理器在多個節點中運行時會導致意外行爲。例如,某些處理器列出或讀取遠程文件系統中的文件。如果計劃在“所有節點”上運行此類處理器,則會導致不必要的重複甚至錯誤。此類處理器應使用此註釋。應用此批註將限制處理器僅在“主節點”上運行。

  • TriggerWhenAnyDestinationAvailable:默認情況下,如果任何出站隊列已滿,NiFi將不會安排處理器運行。這允許背壓一直應用於處理器鏈。但是,即使其中一個出站隊列已滿,某些處理器也可能需要運行。此註釋表示如果任何關係“可用”,則應運行處理器。如果沒有使用該關係的連接已滿,則稱關係“可用”。例如,DistributeLoad Processor使用此批註。如果使用“循環”調度策略,則如果任何出站隊列已滿,則處理器將不會運行。但是,如果使用“下一個可用”調度策略,

  • TriggerWhenEmpty:默認行爲是僅在其輸入隊列至少有一個FlowFile或者處理器沒有輸入隊列(這是典型的“源”處理器)時觸發處理器才能運行。應用此批註將導致框架忽略輸入隊列的大小並觸發處理器,無論輸入隊列中是否有任何數據。例如,如果需要觸發處理器定期運行以超時網絡連接,這很有用。

  • InputRequirement:默認情況下,所有處理器將允許用戶爲處理器創建傳入連接,但如果用戶未創建傳入連接,則處理器仍然有效並可以安排運行。但是,對於預期用作“源處理器”的處理器,這可能會使用戶感到困惑,並且用戶可能會嘗試將FlowFiles發送到該處理器,僅用於FlowFiles排隊而不進行處理。相反,如果處理器期望傳入的FlowFiles但沒有輸入隊列,則處理器將被安排運行但不會執行任何工作,因爲它將不會收到FlowFile,這也會導致混淆。因此,我們可以使用@InputRequirement註解,併爲其提供一個值INPUT_REQUIREDINPUT_ALLOWEDINPUT_FORBIDDEN。這爲框架提供了有關何時使處理器無效,或者用戶是否應該甚至能夠將連接繪製到處理器的信息。例如,如果使用註釋處理器InputRequirement(Requirement.INPUT_FORBIDDEN),則用戶甚至無法創建與該處理器作爲目標的連接。

數據緩衝(Data Buffering)

需要記住的一點是,NiFi提供了通用的數據處理功能。數據可以是任何格式。處理器通常安排有多個線程。開發人員對NiFi做出的一個常見錯誤是將FlowFile的所有內容緩衝到內存中。雖然有些情況需要這樣做,但應儘可能避免,除非衆所周知數據的格式是什麼。例如,負責對XML文檔執行XPath的處理器需要加載整個數據內容到內存中。這通常是可以接受的,因爲預計XML不會非常大。但是,搜索特定字節序列的處理器可用於搜索數百千兆字節或更多的文件。

不是將這些數據緩衝到內存中,而是建議在從內容存儲庫流式傳輸時評估數據(即,掃描InputStream提供給回調的內容ProcessSession.read)。當然,在這種情況下,我們不希望從Content Repository讀取每個字節,因此我們將使用BufferedInputStream或以某種方式緩衝一些少量數據,視情況而定。

控制器服務(Controller Services)

ControllerService接口允許開發人員以乾淨,一致的方式在JVM上共享功能和狀態。該接口類似於Processor 接口的接口,但沒有onTrigger方法,因爲Controller Services未安排定期運行,並且Controller Services沒有關係,因爲它們沒有直接集成到流中。相反,它們由處理器,報告任務和其他控制器服務使用。

開發ControllerService

與處理器接口一樣,ControllerService接口公開了配置,驗證和初始化的方法。這些方法都與Processor接口的initialize方法相同,只是方法是傳遞a ControllerServiceInitializationContext而不是a ProcessorInitializationContext

控制器服務附帶了一個處理器沒有的附加約束。控制器服務必須包含擴展的接口ControllerService。然後,實現只能通過其界面進行交互。例如,處理器永遠不會被賦予ControllerService的具體實現,因此必須僅通過擴展的接口引用服務ControllerService

這種約束主要是因爲處理器可以存在於一個NiFi存檔(NAR)中,而處理器所在的控制器服務的實現可以存在於不同的NAR中。這是通過框架動態實現公開的接口來實現的,框架可以切換到適當的ClassLoader並在具體實現上調用所需的方法。但是,爲了使其工作,處理器和控制器服務實現必須共享Controller Service接口的相同定義。因此,這兩個NAR必須依賴於包含Controller Service接口的NAR。有關更多信息,請參閱NiFi Archives(NAR)

與ControllerService交互

ControllerServices可以通過ControllerServiceLookup或使用identifiesControllerServicePropertyDescriptor的Builder類的方法由Processor,另一個ControllerService或ReportingTask獲取 。ControllerServiceLookup可以由處理器從傳遞給該initialize 方法的ProcessorInitializationContext獲得。同樣,它是由ControllerService從ControllerServiceInitializationContext獲得的,也是由ReportingTask通過傳遞給該initialize方法的ReportingConfiguration對象獲得的。

但是,對於大多數用例,使用identifiesControllerService PropertyDescriptor Builder 的方法是首選,並且是最不復雜的方法。爲了使用這個方法,我們創建了一個PropertyDescriptor,它引用了一個Controller服務:

  1.  
    public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
  2.  
    .name("SSL Context Service")
  3.  
    .description("Specified the SSL Context Service that can be used to create secure connections")
  4.  
    .required(true)
  5.  
    .identifiesControllerService(SSLContextService.class)
  6.  
    .build();

使用此方法,將提示用戶提供應使用的SSL上下文服務。這是通過向用戶提供下拉菜單來完成的,他們可以從中選擇已配置的任何SSLContextService配置,而不管實現如何。

爲了使用此服務,處理器可以使用以下代碼:

  1.  
    final SSLContextService sslContextService = context.getProperty(SSL_CONTEXT_SERVICE)
  2.  
    .asControllerService(SSLContextService.class);

請注意,這SSLContextService是一個擴展ControllerService的接口。目前唯一的實施是StandardSSLContextService。但是,處理器開發人員無需擔心此細節。

報告任務

到目前爲止,我們幾乎沒有提到如何向外界傳達NiFi及其組件的表現。系統是否能夠跟上傳入的數據速率?系統還能處理多少?在一天的高峯時段與一天中最不忙的時間處理多少數據?

爲了回答這些問題以及更多問題,NiFi提供了通過ReportingTask 界面向外部服務報告狀態,統計,指標和監控信息的功能。ReportingTasks可以訪問大量信息,以確定系統的運行方式。

開發報告任務

與Processor和ControllerService接口一樣,ReportingTask接口公開了配置,驗證和初始化的方法。這些方法都與Processor和ControllerService接口的initialize方法相同,除了 方法是傳遞ReportingConfiguration 對象,而不是其他組件接收的初始化對象。ReportingTask還有一個onTrigger框架調用的方法,用於觸發任務執行其任務。

在該onTrigger方法中,ReportingTask被授權訪問ReportingContext,從中可以獲得有關NiFi實例的配置和信息。BulletinRepository允許查詢公告,並允許ReportingTask提交自己的公告,以便將信息呈現給用戶。可通過Context訪問的ControllerServiceLookup提供對已配置的ControllerServices的訪問。但是,這種獲取Controller Services的方法不是首選方法。相反,獲取Controller服務的首選方法是在PropertyDescriptor中引用Controller服務,如與ControllerService交互部分中所述。

EventAccess通過ReportingContext公開的對象提供對該對象的訪問ProcessGroupStatus,該對象公開有關過程組,處理器,連接和其他組件在過去五分鐘內處理的數據量的統計信息。此外,EventAccess對象提供對存儲在其中的ProvenanceEventRecords的訪問ProvenanceEventRepository。當從外部源接收數據,發送到外部服務,從系統中刪除,修改或根據所做出的決定路由時,處理器發出這些源事件事件。

每個ProvenanceEvent都具有FlowFile的ID,事件的類型,事件的創建時間,以及組件訪問FlowFile時與FlowFile關聯的所有FlowFile屬性以及與之關聯的FlowFile屬性。 FlowFile是事件描述的處理結果。這爲ReportingTasks提供了大量信息,允許以多種不同方式生成報告,以公開任何操作問題所需的指標和監控功能。

UI擴展

NiFi中有兩個UI擴展點:

  • 自定義處理器UI

  • 內容瀏覽者

可以創建自定義UI以提供超出大多數處理器設置中可用的標準屬性/值表的配置選項。具有自定義UI的處理器的示例是UpdateAttributeJoltTransformJSON

可以創建內容查看器以擴展可在NiFi中查看的數據類型。NiFi在lib目錄中附帶NAR,其中包含數據類型的內容查看器,如csv,xml,avro,json(standard-nar)和圖像類型,如png,jpeg和gif(media-nar)。

自定義處理器UI

要將自定義UI添加到處理器:

  1. 創建您的UI。

  2. 在處理器NAR中構建並捆綁WAR。

  3. WAR需要nifi-processor-configuration在META-INF目錄中包含一個文件,該文件將Custom UI與該處理器相關聯。

  4. 將NAR放在lib目錄中,它將在NiFi啓動時被發現。

  5. 在處理器的“配置處理器”窗口中,“屬性”選項卡現在應該有一個Advanced按鈕,該按鈕將訪問自定義UI。

例如,以下是UpdateAttribute的NAR佈局:

更新屬性NAR佈局

 
  1.  
    nifi-update-attribute-bundle
  2.  
  3.  
    ├── nifi-update-attribute-model
  4.  
  5.  
    ├── nifi-update-attribute-nar
  6.  
  7.  
    ├── nifi-update-attribute-processor
  8.  
  9.  
    ├── nifi-update-attribute-ui
  10.  
    │ ├── pom.xml
  11.  
    │ └── src
  12.  
    │ └── main
  13.  
    │ ├── java
  14.  
    │ ├── resources
  15.  
    │ └── webapp
  16.  
    │ └── css
  17.  
    │ └── images
  18.  
    │ └── js
  19.  
    │ └── META-INF
  20.  
    │ │ └── nifi-processor-configuration
  21.  
    │ └── WEB-INF
  22.  
  23.  
    └── pom.xml

 

 

內容nifi-processor-configuration如下:

 

org.apache.nifi.processors.attributes.UpdateAttribute:${project.groupId}:nifi-update-attribute-nar:${project.version}

 

 

  還可以爲Controller Services和Reporting Tasks實現自定義UI。

內容瀏覽者

要添加內容查看器:

  1. 在處理器NAR中構建並捆綁WAR。

  2. WAR需要nifi-content-viewer在META-INF目錄中包含一個文件,該目錄列出了支持的內容類型。

  3. 將NAR放在lib目錄中,它將在NiFi啓動時被發現。

  4. 遇到匹配的內容類型時,內容查看器將生成適當的視圖。

一個很好的例子是標準內容查看器的NAR佈局:

標準內容查看器NAR佈局

 
  1.  
    nifi-standard-bundle
  2.  
  3.  
    ├── nifi-jolt-transform-json-ui
  4.  
  5.  
    ├── nifi-standard-content-viewer
  6.  
    │ ├── pom.xml
  7.  
    │ └── src
  8.  
    │ └── main
  9.  
    │ ├── java
  10.  
    │ ├── resources
  11.  
    │ └── webapp
  12.  
    │ └── css
  13.  
    │ └── META-INF
  14.  
    │ │ └── nifi-content-viewer
  15.  
    │ └── WEB-INF
  16.  
  17.  
    ├── nifi-standard-nar
  18.  
  19.  
    ├── nifi-standard-prioritizers
  20.  
  21.  
    ├── nifi-standard-processors
  22.  
  23.  
    ├── nifi-standard-reporting-tasks
  24.  
  25.  
    ├── nifi-standard-utils
  26.  
  27.  
    └── pom.xml

 

內容nifi-content-viewer如下:

  1.  
    application/xml
  2.  
    application/json
  3.  
    text/plain
  4.  
    text/csv
  5.  
    avro/binary
  6.  
    application/avro-binary
  7.  
    application/avro+binary

命令行工具

TLS-工具包

客戶端/服務器操作模式來自於希望自動生成所需的TLS配置工件而無需在集中位置執行該生成。這簡化了羣集環境中的配置。由於我們不一定擁有運行生成邏輯或可信證書頒發機構的中心位置,因此使用共享密鑰來相互驗證客戶端和服務器。

tls-toolkit使用HMAC驗證CA服務器的公鑰和客戶端發送的CSR來防止中間人攻擊。共享密鑰(令牌)用作HMAC密鑰。

基本流程如下:

  1. 客戶端生成KeyPair。

  2. 客戶端生成包含CSR和HMAC的請求json有效負載,其中令牌作爲密鑰,CSR的公鑰指紋作爲數據。

  3. 客戶端連接到指定的https端口上的CA主機名,並驗證CA證書的CN與主機名匹配(注意:因爲此時我們不信任CA,這增加了無安全性,這只是一種方式如果可能的話,提早錯誤)。

  4. 服務器使用令牌作爲密鑰並使用CSR的公鑰指紋作爲數據,從客戶端有效負載驗證HMAC。這證明客戶端知道共享密鑰,並且希望對具有該公鑰的CSR進行簽名。(注意:中間的人可以轉發此功能,但無法在不使HMAC無效的情況下更改CSR,從而無法實現目的)。

  5. 服務器對CSR進行簽名,併發回包含證書的響應json有效負載和以令牌作爲密鑰的HMAC以及其公鑰的指紋作爲數據。

  6. 客戶端使用令牌作爲密鑰以及由TLS會話提供的證書公鑰的指紋來驗證響應HMAC。這驗證了知道共享密鑰的CA是我們通過TLS與之交談的CA.

  7. 客戶端驗證來自TLS會話的CA證書是否在有效負載中籤署了證書。

  8. 客戶端將生成的KeyPair添加到具有證書鏈的密鑰庫,並將CA證書從TLS連接添加到其信任庫。

  9. 客戶端寫出包含密鑰庫,信任庫密碼和有關交換的其他詳細信息的配置json。

測試

測試將在更大的框架內使用的組件通常非常麻煩和棘手。通過NiFi,我們努力使測試組件儘可能簡單。爲此,我們創建了一個nifi-mock可與JUnit結合使用的模塊,以提供對組件的廣泛測試。

模擬框架主要用於測試處理器,因爲它們是迄今爲止最常開發的擴展點。但是,該框架確實提供了測試Controller服務的能力。

通常通過創建功能測試來驗證組件行爲,從而對組件進行測試。這樣做是因爲處理器通常由少數輔助方法組成,但邏輯將主要包含在onTrigger方法中。該TestRunner接口允許我們通過將更多的“原始”的對象,如文件和字節數組到FlowFiles來測試處理器和控制器服務,並處理創建ProcessSessions和所需的處理器ProcessContexts做它的工作,以及調用在必要的生命週期方法爲了確保處理器在單元測試中的行爲與在生產中的行爲相同。

實例化TestRunner

處理器或控制器服務的大多數單元測試都是通過創建TestRunner 類的實例來開始的。爲了向處理器添加必要的類,您可以使用Maven依賴項:

  1.  
    <dependency>
  2.  
    <groupId>org.apache.nifi</groupId>
  3.  
    <artifactId>nifi-mock</artifactId>
  4.  
    <version>${nifi version}</version>
  5.  
    </dependency>

我們TestRunner通過調用類的一個靜態newTestRunner方法TestRunners(位於org.apache.nifi.util包中)來創建一個新的。這些方法爲被測處理器提供了一個參數(可以是要測試的處理器的類,也可以是處理器的實例),並允許設置處理器名稱。

添加ControllerServices

在創建新的測試運行器之後,我們可以將任何控制器服務添加到我們的處理器將需要的測試運行器以執行其工作。我們通過調用該addControllerService方法並提供Controller Service的標識符和Controller Service的實例來完成此操作。

如果控制器服務需要進行配置,它的性質可以通過調用被設置setProperty(ControllerService, PropertyDescriptor, String)setProperty(ControllerService, String, String)setProperty(ControllerService, PropertyDescriptor, AllowableValue)方法。每種方法都返回一個 ValidationResult。然後可以檢查此對象以確保通過調用該屬性有效isValid。可以通過調用setAnnotationData(ControllerService, String)方法來設置註釋數據。

我們現在可以通過調用來確保Controller Service有效assertValid(ControllerService)- 或者如果通過調用測試Controller Service本身,則確保配置的值無效 assertNotValid(ControllerService)

將Controller Service添加到Test Runner並進行配置後,現在可以通過調用該enableControllerService(ControllerService)方法啓用它 。如果Controller Service無效,則此方法將拋出IllegalStateException。否則,該服務現在可以使用了。

設置屬性值

配置任何必要的Controller服務後,我們需要配置我們的處理器。我們可以通過調用與Controller Services相同的方法來完成此操作,而無需指定任何Controller Service。即,我們可以打電話setProperty(PropertyDescriptor, String),等等。每個setProperty方法再次返回一個ValidationResult屬性,可用於確保屬性值有效。

同樣,根據我們的期望,我們也可以調用assertValid()assertNotValid()確保處理器的配置是否有效。

排隊FlowFiles

在觸發處理器運行之前,通常需要將FlowFiles排入隊列以供處理器處理。這可以通過使用該類的enqueue方法來實現TestRunner。該enqueue方法具有幾個不同的覆蓋,並允許在的形式被添加的數據byte[]InputStreamPath。這些方法中的每一種還支持允許Map<String, String>添加以支持FlowFile屬性的變體。

此外,還有一種enqueue方法可以獲取FlowFile對象的var-args。例如,這可用於獲取處理器的輸出,然後將其提供給處理器的輸入。

運行處理器

配置Controller Services並將必要的FlowFile排入隊列後,可以通過調用run方法來觸發處理器運行TestRunner。如果在沒有任何參數的情況下調用此方法,它將使用@OnScheduled註釋調用Processor中的任何方法,調用Processor的onTrigger方法一次,然後運行@OnUnscheduledfinally @OnStopped方法。

如果希望在觸發onTrigger其他事件@OnUnscheduled和 @OnStopped生命週期事件之前運行該方法的多次迭代,則該run(int)方法可用於指定現在onTrigger應該調用的許多迭代。

還有,當我們想要觸發處理器上運行,但不會觸發時間@OnUnscheduled@OnStopped 生命週期事件。例如,這有助於在這些事件發生之前檢查處理器的狀態。這可以使用run(int, boolean)和傳遞false作爲第二個參數來實現。但是,在執行此操作後,調用@OnScheduled生命週期方法可能會導致問題。因此,我們現在可以onTrigger再次運行,而不會通過使用方法的run(int,boolean,boolean)版本runfalse作爲第三個參數傳遞來發生這些事件。

如果測試多個線程發生的行爲很有用,這也可以通過調用setThreadCount方法來實現 TestRunner。默認值爲1個線程。如果使用多個線程,請務必記住,run調用TestRunner指定應觸發處理器的次數,而不是每個線程應觸發處理器的次數。因此,如果線程計數設置爲2但 run(1)被調用,則只使用一個線程。

驗證輸出

處理器運行完畢後,單元測試通常需要驗證FlowFiles是否符合預期要求。這可以使用TestRunnersassertAllFlowFilesTransferred和 assertTransferCount方法來實現。前一種方法將關係和整數作爲參數,以指示應該將多少FlowFiles傳輸到該關係。除非將此數量的FlowFiles轉移到給定的關係或者任何FlowFile被轉移到任何其他關係,否則該方法將無法通過單元測試。該assertTransferCount方法僅驗證FlowFile計數是給定關係的預期數量。

驗證計數後,我們可以通過該getFlowFilesForRelationship方法獲得實際的輸出FlowFiles 。這個方法返回一個List<MockFlowFile>。重要的是要注意List的類型MockFlowFile,而不是FlowFile接口。這樣做是因爲MockFlowFile有許多方法可以驗證內容。

例如,MockFlowFile有斷言FlowFile屬性exists(assertAttributeExists)的方法,聲明其他屬性不存在(assertAttributeNotExists),或者Attributes具有正確的值(assertAttributeEqualsassertAttributeNotEquals)。存在用於驗證FlowFile的內容的類似方法。可以將FlowFile的內容與a byte[],和InputStream文件或String進行比較。如果預期數據是文本的,則首選String版本,因爲如果輸出不是預期的,它會提供更直觀的錯誤消息。

模擬外部資源

測試連接到遠程資源的NiFi處理器時最大的問題之一是我們不希望從單元測試中實際連接到某些遠程資源。我們可以在單元測試中自己站起來一個簡單的服務器並配置處理器與之通信,但是我們必須理解並實現特定於服務器的規範,並且可能無法正確發送錯誤消息等我們想要測試。

通常,這裏採用的方法是在處理器中具有負責獲得遠程資源的連接或客戶端的方法。我們通常將此方法標記爲受保護。在單元測試中,我們不是TestRunner通過調用TestRunners.newTestRunner(Class)和提供Processor類來創建,而是在單元測試中創建處理器的子類並使用它:

  1.  
    @Test
  2.  
    public void testConnectionFailure() {
  3.  
    final TestRunner runner = TestRunners.newTestRunner(new MyProcessor() {
  4.  
    protected Client getClient() {
  5.  
    // Return a mocked out client here.
  6.  
    return new Client() {
  7.  
    public void connect() throws IOException {
  8.  
    throw new IOException();
  9.  
    }
  10.  
     
  11.  
    // ...
  12.  
    // other client methods
  13.  
    // ...
  14.  
    };
  15.  
    }
  16.  
    });
  17.  
     
  18.  
    // rest of unit test.
  19.  
    }

這允許我們實現一個客戶端,該客戶端模擬所有網絡通信並返回我們想要測試的不同錯誤結果,並確保我們的邏輯對於處理成功調用客戶端是正確的。

附加測試功能

除了測試框架提供的上述功能外,TestRunner還提供了幾種方便的方法來驗證處理器的行爲。提供了用於確保已清空處理器的輸入隊列的方法。單元測試能夠獲取ProcessContext,ProcessSessionFactory,ProvenanceReporter以及TestRunner將使用的其他特定於框架的實體。該shutdown方法提供了測試註釋爲僅在關閉NiFi時運行的處理器方法的能力。可以爲使用自定義用戶界面的處理器設置註釋數據。最後,可以通過該setThreadCount(int)方法設置應該用於運行處理器的線程數。

NiFi檔案館(NAR)

當來自許多不同組織的軟件都託管在同一環境中時,Java ClassLoaders很快成爲一個問題。如果多個組件依賴於同一個庫但每個組件依賴於不同的版本,則會出現許多問題,通常會導致出現意外行爲或NoClassDefFoundError錯誤。爲了防止這些問題成爲問題,NiFi引入了NiFi存檔或NAR的概念。

NAR允許將多個組件及其依賴項一起打包到單個包中。然後,NAR包與其他NAR包提供ClassLoader隔離。開發人員應始終將其NiFi組件部署爲NAR包。

爲此,開發人員創建了一個新的Maven工件,我們將其稱爲NAR工件。包裝設定爲nardependencies然後創建POM 的部分,以便NAR依賴於要包含在NAR中的所有NiFi組件。

爲了使用包裝nar,我們必須使用該nifi-nar-maven-plugin模塊。通過將以下代碼段添加到NAR的pom.xml中來實現此目的:

  1.  
    <build>
  2.  
    <plugins>
  3.  
    <plugin>
  4.  
    <groupId>org.apache.nifi</groupId>
  5.  
    <artifactId>nifi-nar-maven-plugin</artifactId>
  6.  
    <version>1.1.0</version>
  7.  
    <extensions>true</extensions>
  8.  
    </plugin>
  9.  
    </plugins>
  10.  
    </build>

在Apache NiFi代碼庫中,這存在於NiFi根POM中,其中所有其他NiFi工件(除了nifi-nar-maven-plugin本身)都繼承,因此我們不需要將其包含在我們的任何其他工具中POM文件。

NAR能夠具有一個類型的依賴項nar。如果指定了多個類型的依賴項 nar,那麼nifi-nar-maven-plugin將會出錯。如果NAR A在NAR B上添加依賴關係,這將不會導致NAR B打包NAR A的所有組件。相反,這將向 NAR A Nar-Dependency-IdMANIFEST.MF文件添加一個元素。這將導致設置NAR B的ClassLoader作爲NAR A的Parent ClassLoader。在這種情況下,我們將NAR B稱爲NAR A 的Parent

Parent ClassLoaders的這種鏈接是NiFi使用的機制,以便在所有NAR之間共享Controller Services。如“ 開發 控制器服務”部分所述,必須將控制器服務分爲擴展的接口ControllerService和實現該接口的實現。只要Controller Service Implementation和Processor共享Controller Service接口的相同定義,Controller Services就可以從任何處理器引用,無論它在哪個NAR中。

爲了共享相同的定義,處理器的NAR和Controller服務實現的NAR必須作爲父服務器服務定義的NAR。示例層次結構可能如下所示:

控制器服務NAR佈局

 
  1.  
    root
  2.  
    ├── my-controller-service-api
  3.  
    │ ├── pom.xml
  4.  
    │ └── src
  5.  
    │ └── main
  6.  
    │ └── java
  7.  
    │ └── org
  8.  
    │ └── my
  9.  
    │ └── services
  10.  
    │ └── MyService.java
  11.  
  12.  
    ├── my-controller-service-api-nar
  13.  
    │ └── pom.xml //此POM文件的類型爲nar。它依賴於 nifi-standard-services-api-nar。
  14.  
  15.  
  16.  
  17.  
    ├── my-controller-service-impl
  18.  
    │ ├── pom.xml //此POM文件是類型jar。它依賴於 my-controller-service-api。它不具有任何依賴 nar的工件
  19.  
    │ └── src
  20.  
    │ ├── main
  21.  
    │ │ ├── java
  22.  
    │ │ │ └── org
  23.  
    │ │ │ └── my
  24.  
    │ │ │ └── services
  25.  
    │ │ │ └── MyServiceImpl.java
  26.  
    │ │ └── resources
  27.  
    │ │ └── META-INF
  28.  
    │ │ └── services
  29.  
    │ │ └── org.apache.nifi.controller.ControllerService
  30.  
    │ └── test
  31.  
    │ └── java
  32.  
    │ └── org
  33.  
    │ └── my
  34.  
    │ └── services
  35.  
    │ └── TestMyServiceImpl.java
  36.  
  37.  
  38.  
    ├── my-controller-service-nar
  39.  
    │ └── pom.xml // 此POM文件的類型爲nar。它依賴於 my-controller-service-api-nar。
  40.  
  41.  
  42.  
    └── other-processor-nar
  43.  
    └── pom.xml //此POM文件的類型爲nar。它依賴於 my-controller-service-api-nar。

 

雖然這些看起來可能看起來非常複雜,但在創建一次或兩次這樣的層次結構之後,它變得複雜得多。請注意,這 my-controller-service-api-nar依賴於 nifi-standard-services-api-nar。這樣做是爲了使任何具有依賴性的NAR my-controller-service-api-nar也能夠訪問由nifi-standard-services-api-narSSLContextService 提供的所有Controller服務 。在同一個葉片中,沒有必要爲每個服務創建不同的“service-api”NAR。相反,通常有一個“service-api”NAR封裝了許多不同的Controller服務的API,這通常是有意義的。nifi-standard-services-api-nar。通常,API不會包含廣泛的依賴關係,因此,ClassLoader隔離可能不那麼重要,因此將許多API工件集中到同一個NAR中通常是可以接受的。

Per-Instance ClassLoading

組件開發人員可能希望在運行時向組件的類路徑添加其他資源。例如,您可能希望將JDBC驅動程序的位置提供給與關係數據庫交互的處理器,從而允許處理器使用任何驅動程序,而不是嘗試將驅動程序捆綁到NAR中。

這可以通過將一個或多個PropertyDescriptor實例聲明 dynamicallyModifiesClasspath爲true來實現。例如:

  1.  
    PropertyDescriptor EXTRA_RESOURCE = new PropertyDescriptor.Builder()
  2.  
    .name("Extra Resources")
  3.  
    .description("The path to one or more resources to add to the classpath.")
  4.  
    .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
  5.  
    .expressionLanguageSupported(true)
  6.  
    .dynamicallyModifiesClasspath(true)
  7.  
    .build();

在組件上設置這些屬性後,框架將標識所有dynamicallyModifiesClasspath設置爲true的屬性 。對於每個屬性,框架都嘗試從屬性的值中解析文件系統資源。該值可以是一個或多個目錄或文件的逗號分隔列表,其中跳過任何不存在的路徑。如果資源表示目錄,則列出該目錄,並且該目錄中的所有文件都將單獨添加到類路徑中。

每個屬性可以通過驗證器對值的格式施加進一步的限制。例如,使用StandardValidators.FILE_EXISTS_VALIDATOR將屬性限制爲接受單個文件。使用StandardValidators.NON_EMPTY_VALIDATOR允許逗號分隔文件或目錄的任意組合。

通過將資源添加到始終首先檢查的內部ClassLoader,將資源添加到實例ClassLoader。只要這些屬性的值發生更改,內部ClassLoader就會關閉並使用新資源重新創建。

NiFi提供了@RequiresInstanceClassLoading註釋,以進一步擴展和隔離組件類路徑上可用的庫。您可以註釋組件,@RequiresInstanceClassLoading 以指示組件的實例ClassLoader需要組件的NAR ClassLoader中所有資源的副本。如果@RequiresInstanceClassLoading不存在,實例ClassLoader只是將其父ClassLoader設置爲NAR ClassLoader,而不是複製資源。

@RequiresInstanceClassLoading註釋還提供了一個可選的標誌`cloneAncestorResources'。如果設置爲true,則實例ClassLoader將包含祖先資源,直到包含組件引用的控制器服務API的第一個ClassLoader,或者直到Jetty NAR。如果設置爲false或未指定,則僅包含組件NAR中的資源。

因爲@RequiresInstanceClassLoading爲組件的每個實例從NAR ClassLoader複製資源,所以明智地使用此功能。如果創建了一個組件的十個實例,則組件的NAR ClassLoader中的所有類都將加載到內存中十次。當創建足夠的組件實例時,這最終會顯着增加內存佔用。

此外,在使用Controller Services時使用@RequiresInstanceClassLoading時存在一些限制。處理器,報告任務和控制器服務可以在其屬性描述符之一中引用Controller Service API。當Controller Service API與引用它的組件或Controller Service實現捆綁在同一NAR中時,可能會出現問題。如果遇到這些情況之一併且擴展需要實例類加載,則將跳過擴展並記錄適當的ERROR。要解決此問題,Controller Service API應捆綁在父NAR中。引用該服務的服務實現和擴展應取決於Controller Service API NAR。請參閱中的Controller Service NAR佈局NiFi檔案館(NARs)部分。只要Controller Service API與需要它的擴展捆綁在一起,即使未使用@RequiresInstanceClassLoading,也會記錄一條警告以幫助避免這種不良做法。

棄用組件

有時可能需要棄用組件。每當發生這種情況時,開發人員可以使用@DeprecationNotice註釋來指示組件已被棄用,允許開發人員描述棄用的原因並建議替代組件。下面是一個如何執行此操作的示例:

  1.  
    @DeprecationNotice(alternatives = {ListenSyslog.class}, classNames = {"org.apache.nifi.processors.standard.ListenRELP"}, reason = "Technology has been superseded", )
  2.  
    public class ListenOldProtocol extends AbstractProcessor {

如您所見,替代方案可用於定義替代組件和數組,而classNames可用於通過字符串數組表示類似內容。

如何爲Apache NiFi做出貢獻

我們總是很高興能夠從社區中獲得貢獻 - 尤其是來自新貢獻者的貢獻!我們有興趣接受代碼的貢獻,以及文檔甚至可以作爲圖標或樣式應用於應用程序的藝術作品。

技術

Apache NiFi的後端是用Java編寫的。Web層利用JAX-RS和JavaScript廣泛用於提供用戶界面。我們依賴於幾個第三方JavaScript庫,包括D3和JQuery等。我們爲我們的構建使用Apache Maven,爲我們的版本控制系統使用Git。

文檔在AsciiDoc中創建。

從哪兒開始?

NiFi的JIRA頁面可用於查找標記爲“初學者”的票證,或者您可以深入瞭解創建處理器的任何票證。處理器應該是獨立的,不依賴於其他外部組件(Controller Services除外),因此它們爲新的NiFi開發人員提供了良好的起點。這使開發人員暴露於NiFi API,並且是數據流系統中最具擴展性的部分。

系統級和概述文檔位於“<code checkout location> / nifi / nifi-docs / src / main / asciidoc”中。可以使用實時預覽編輯AsciiDoc,以便於生成文檔。

提供捐款

可以通過創建補丁來提供貢獻:

git format-patch

並將該補丁附加到故障單,或生成Pull請求。

有關貢獻的更多詳細信息,請參閱貢獻者指南的相關部分。

聯繫我們

開發人員郵件列表([email protected])受到密切監控,我們傾向於快速響應。如果您有任何疑問,請隨時向我們發送電子郵件 - 我們隨時爲您提供幫助!不幸的是,電子郵件可能會在隨機播放中丟失,所以如果您發送電子郵件並且在一兩天內沒有收到回覆,那就是我們的錯 - 不要擔心打擾我們。只需再次ping郵件列表。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章