简介

本高级文档旨在深入探讨 Lynxus 的实现和设计决策。它假设读者已阅读足够的其他文档以了解 Lynxus 的基础知识。

FlowFile 是 Lynxus 及其基于流的设计的核心。FlowFile 是一条数据记录,由指向其内容(有效负载)的指针和支持内容的属性组成,与一个或多个溯源事件相关联。属性是键/值对,充当 FlowFile 的元数据,例如 FlowFile 文件名。内容是文件的实际数据或有效负载。溯源是对 FlowFile 发生事情的记录。这些部分中的每一个都有自己的存储库(repo)用于存储。

存储库的一个关键方面是不可变性。内容存储库中的内容和 FlowFile 存储库中的数据是不可变的。当 FlowFile 的属性发生更改时,会在内存中创建属性的新副本,然后持久化到磁盘上。当给定 FlowFile 的内容正在更改时,其原始内容被读取,通过转换流式传输,并写入新流。然后 FlowFile 的内容指针更新到磁盘上的新位置。因此,FlowFile 内容存储的默认方法可以说是一个不可变的版本化内容存储。这样做的好处很多,包括:大幅减少典型复杂处理图所需的存储空间、自然的重播能力、利用操作系统缓存、减少随机读/写性能影响,并且易于推理。以前的修订版根据 'lynxus.properties' 文件中设置的归档属性保留,并在 Lynxus 系统管理员指南 中概述。

存储库

Lynxus 使用了三个存储库。每个都存在于操作系统/主机的文件系统中,并提供特定功能。为了完全理解 FlowFile 以及底层系统如何使用它们,了解这些存储库非常重要。所有三个存储库都是 Lynxus 用于持久化数据的本地存储上的目录。

  • FlowFile 存储库包含流中所有当前 FlowFile 的元数据。

  • 内容存储库保存当前和过去 FlowFile 的内容。

  • 溯源存储库保存 FlowFile 的历史记录。

Lynxus 架构图

FlowFile 存储库

系统正在积极处理的 FlowFile 保存在 JVM 内存中的哈希映射中(更多内容见 深入视图:内存和磁盘中的 FlowFile)。这使得处理它们非常高效,但需要一种辅助机制来在进程重启时提供数据的持久性,原因包括断电、内核恐慌、系统升级和维护周期等。FlowFile 存储库是当前存在于系统中的每个 FlowFile 元数据的"预写日志"(或数据记录)。此 FlowFile 元数据包括与 FlowFile 关联的所有属性、指向 FlowFile 实际内容的指针(存在于内容存储库中)以及 FlowFile 的状态,例如 FlowFile 所属的连接/队列。此预写日志为 Lynxus 提供了处理重启和意外系统故障所需的弹性。

FlowFile 存储库充当 Lynxus 的预写日志,因此当 FlowFile 流经系统时,每个更改都在 FlowFile 存储库中作为事务性工作单元记录在发生之前。这允许系统确切地知道节点在处理数据时处于哪个步骤。如果节点在处理数据时宕机,它可以在重启时轻松从中断处恢复(更多内容见 系统故障对事务的影响)。日志中 FlowFile 的格式是一系列沿途发生的增量(或更改)。Lynxus 通过恢复 FlowFile 的"快照"(在存储库检查点时创建)然后重播每个增量来恢复 FlowFile。

系统定期自动拍摄快照,为每个 FlowFile 创建新快照。系统通过序列化哈希映射中的每个 FlowFile 并将其写入磁盘(文件名为".partial")来计算新的基础检查点。随着检查点的进行,新的 FlowFile 基线被写入".partial"文件。一旦检查点完成,旧的"snapshot"文件被删除,".partial"文件被重命名为"snapshot"。

系统检查点之间的周期可以在 'lynxus.properties' 文件中配置(在 Lynxus 系统管理员指南 中有记录)。默认是两分钟间隔。

系统故障对事务的影响

Lynxus 通过记录每个节点在其各自的 FlowFile 存储库中当时正在发生的事情来防止硬件和系统故障。如上所述,FlowFile 存储库是 Lynxus 的预写日志。当节点重新上线时,它首先检查"snapshot"和".partial"文件来恢复其状态。节点要么接受"snapshot"并删除".partial"(如果存在),或者如果"snapshot"文件不存在则将".partial"文件重命名为"snapshot"。

如果节点在写入内容时宕机,由于写时复制(下面提到)和不可变性(上面提到)范式,什么都不会损坏。由于 FlowFile 事务从不修改原始内容(由内容指针指向),原始内容是安全的。当 Lynxus 宕机时,更改的写入声明被孤立,然后由后台垃圾回收清理。这提供了到最后一个已知稳定状态的"回滚"。

然后节点从 FlowFile 恢复其状态。有关该过程的更深入、逐步说明,请参阅 Lynxus 的预写日志实现。

这种设置,在事务性工作单元方面,允许 Lynxus 在面对逆境时非常有弹性,确保即使 Lynxus 突然被杀死,它也可以恢复而不会丢失任何数据。

深入视图:内存和磁盘中的 FlowFile

"FlowFile"这个术语有点用词不当。这会让人相信每个 FlowFile 对应磁盘上的一个文件,但事实并非如此。FlowFile 属性存在的两个主要位置是上面解释的预写日志和工作内存中的哈希映射。此哈希映射具有对流中正在使用的所有 FlowFile 的引用。此映射引用的对象与处理器使用和连接队列中持有的对象相同。由于 FlowFile 对象保存在内存中,处理器要获取 FlowFile 只需请求 ProcessSession 从队列中获取它。

当 FlowFile 发生更改时,增量被写入预写日志,内存中的对象也相应修改。这允许系统快速处理 FlowFile,同时跟踪发生了什么以及会话提交时会发生什么。这提供了一个非常强大和持久的系统。

还有"交换"FlowFile 的概念。当连接队列中的 FlowFile 数量超过"lynxus.queue.swap.threshold"属性中设置的值时,就会发生这种情况。连接队列中优先级最低的 FlowFile 以 10,000 个批次序列化并写入磁盘上的"交换文件"。然后从上面提到的哈希映射中删除这些 FlowFile,连接队列负责确定何时将文件交换回内存。当 FlowFile 被交换出去时,FlowFile 存储库会收到通知,并保留交换文件的列表。当系统检查点时,快照包括交换出去文件的部分。当交换文件被交换回来时,FlowFile 被添加回哈希映射。这种交换技术,与大多数操作系统执行的交换非常相似,允许 Lynxus 提供对正在积极处理的 FlowFile 的非常快速访问,同时仍然允许数百万 FlowFile 存在于流中而不会耗尽系统内存。

内容存储库

内容存储库只是本地存储中所有 FlowFile 内容存在的地方,通常是三个存储库中最大的。正如介绍部分所述,此存储库利用不可变性和写时复制范式来最大化速度和线程安全。影响内容存储库的核心设计决策是将 FlowFile 的内容保存在磁盘上,仅在需要时才将其读入 JVM 内存。这允许 Lynxus 处理微小和巨大的对象,而无需生产者和消费者处理器在内存中保存完整对象。因此,拆分、聚合和转换非常大的对象等操作很容易做到而不会损害内存。

与 JVM 堆具有垃圾回收过程以在需要空间时回收不可达对象的方式相同,Lynxus 中存在一个专用线程来分析内容存储库中未使用的内容(更多信息在"深入视图:检查点后删除"部分)。在 FlowFile 的内容被识别为不再使用后,它将被删除或归档。如果在 'lynxus.properties' 中启用了归档,则 FlowFile 的内容将存在于内容存储库中,直到它被淘汰(在一定时间后删除)或因内容存储库占用太多空间而删除。归档和/或删除的条件在 'lynxus.properties' 文件中配置("lynxus.content.repository.archive.max.retention.period"、"lynxus.content.repository.archive.max.usage.percentage"),并在 Lynxus 系统管理员指南 中概述。有关内容删除的更多信息,请参阅"数据退出"部分。

深入视图:内容声明

一般来说,当谈论 FlowFile 时,对其内容的引用可以简单地称为内容的"指针"。不过,FlowFile 内容引用的底层实现具有多层复杂性。内容存储库由磁盘上的文件集合组成。这些文件被分入容器和部分。部分是容器的子目录。容器可以被认为是内容存储库的"根目录"。不过,内容存储库可以由许多容器组成。这样做是为了让 Lynxus 可以并行利用多个物理分区。"Lynxus 然后能够并行地从所有这些磁盘读取和写入,以便在单个节点上实现每秒数百兆字节甚至千兆字节的磁盘吞吐量。"资源声明"是 Java 对象,指向磁盘上的特定文件(这是通过跟踪文件 ID、文件所在的部分以及部分所属的容器来完成的)。

为了跟踪 FlowFile 的内容,FlowFile 有一个"内容声明"对象。此内容声明具有对包含内容的资源声明的引用、内容在文件中的偏移量以及内容的长度。要访问内容,内容存储库使用资源声明的属性向下钻取到磁盘上的特定文件,然后在从文件流式传输内容之前定位到资源声明指定的偏移量。

这种抽象层(资源声明)的完成是为了不为每个 FlowFile 的内容在磁盘上创建一个文件。不可变性的概念对此成为可能至关重要。由于内容一旦写入就永远不会更改(使用"写时复制"来进行更改),因此如果 FlowFile 的内容更改,不会有内存碎片或移动数据。通过在磁盘上使用单个文件来保存许多 FlowFile 的内容,Lynxus 能够提供更好的吞吐量,通常接近磁盘提供的最大数据速率。

溯源存储库

溯源存储库是存储每个 FlowFile 历史的地方。此历史用于提供每个数据的数据血统(也称为监管链)。每次 FlowFile 发生事件(FlowFile 被创建、分叉、克隆、修改等)时,都会创建一个新的溯源事件。此溯源事件是 FlowFile 在时间点看起来和适应流的快照。当创建溯源事件时,它会复制 FlowFile 的所有属性和指向 FlowFile 内容的指针,并将其与 FlowFile 的状态(例如它与其他溯源事件的关系)聚合到溯源存储库中的一个位置。此快照不会更改,除非数据过期。溯源存储库在完成后的一段时间内保存所有这些溯源事件,如 'lynxus.properties' 文件中指定的。

由于 FlowFile 的所有属性和指向内容的指针都保存在溯源存储库中,数据流管理器不仅能够查看该数据块的血统或处理历史,还能够稍后查看数据本身,甚至从流中的任何点重播数据。一个常见的用例是当下游系统声称未收到数据时。数据血统可以准确显示数据何时交付给下游系统、数据的样子、文件名以及数据发送到的 URL——或者可以确认数据确实从未发送。在任何一种情况下,都可以通过单击按钮(或通过访问适当的 HTTP API 端点)重播发送事件,以便仅将数据重新发送到该特定下游系统。或者,如果数据处理不当(也许应该先进行某些数据操作),可以修复流,然后将数据重播到新流中,以便正确处理数据。

请记住,由于溯源不复制内容存储库中的内容,而只是复制 FlowFile 指向内容的指针,内容可能在引用它的溯源事件被删除之前被删除。这意味着用户将无法再查看内容或稍后重播 FlowFile。但是,用户仍然能够查看 FlowFile 的血统并了解数据发生了什么。例如,即使数据本身不可访问,用户仍然能够查看数据的唯一标识符、其文件名(如果适用)、何时收到、从哪里收到、如何操作、发送到哪里等等。此外,由于 FlowFile 的属性可用,数据流管理器能够理解数据为何以这种方式处理,为理解和调试数据流提供了关键工具。

由于溯源事件是 FlowFile 的快照,因为它存在于当前流中,对流更改可能会影响稍后重播溯源事件的能力。例如,如果从流中删除连接,则无法从流中的该点重播数据,因为现在没有地方将数据入队进行处理。

有关溯源存储库背后的设计决策,请查看 持久溯源存储库设计。

深入视图:溯源日志文件

每个溯源事件有两个映射,一个用于事件之前的属性,一个用于更新的属性值。一般来说,溯源事件不存储事件发出时存在的属性的更新值,而是存储会话提交时的属性值。事件被缓存和保存,直到会话提交,一旦会话提交,事件就会与会话提交时与 FlowFile 关联的属性一起发出。此规则的例外是"SEND"事件,在这种情况下,事件包含事件发出时存在的属性。这样做是因为如果属性本身也被发送,重要的是准确记录发送了什么信息。

随着 Lynxus 运行,有一组滚动的 16 个溯源日志文件。随着溯源事件的发出,它们被写入 16 个文件之一(有多个文件以增加吞吐量)。日志文件定期轮换(默认时间间隔是每 30 秒)。这意味着新创建的溯源事件开始写入一组新的 16 个日志文件,原始文件被处理用于长期存储。首先,轮换的日志被合并为一个文件。然后文件被可选地压缩(由"lynxus.provenance.repository.compress.on.rollover"属性决定)。最后,事件使用 Lucene 索引并可供查询。这种批处理索引方法意味着溯源事件不能立即用于查询,但作为回报,这大大提高了性能,因为提交事务和索引是非常昂贵的任务。

一个单独的线程处理溯源日志的删除。管理员可以设置控制溯源日志删除的两个条件是它可以占用的最大磁盘空间和日志的最大保留期限。线程按最后修改日期对存储库排序,并在超过其中一个条件时删除最旧的文件。

溯源存储库是一个 Lucene 索引,被分成多个分片。这样做有多个原因。首先,Lucene 使用 32 位整数作为文档标识符,因此 Lucene 在不分片的情况下支持的最大文档数量有限。其次,如果我们知道每个分片的时间范围,使用多线程搜索就很容易。此外,这种分片还允许更有效的删除。Lynxus 等待分片中的所有事件都被计划删除,然后才从磁盘删除整个分片。这使得我们在删除时不必更新 Lucene 索引。

一般存储库说明

多个物理存储点

对于溯源和内容存储库,可以选择将信息跨多个物理分区条带化。如果管理员希望跨多个磁盘联合读和写,他们会这样做。存储库(内容或溯源)仍然是一个逻辑存储,但写入将由系统自动跨多个卷/分区条带化。目录在 'lynxus.properties' 文件中指定。

最佳实践

分析 FlowFile 的内容尽可能少,而是从内容中提取关键信息到 FlowFile 的属性中;然后从 FlowFile 属性中读/写信息,这被认为是最佳实践。一个例子是 ExtractText 处理器,它从 FlowFile 内容中提取文本并将其作为属性,以便其他处理器可以使用它。这比不断处理 FlowFile 的整个内容提供了更好的性能,因为属性保存在内存中,更新 FlowFile 存储库比更新内容存储库快得多,考虑到每个存储的数据量。

FlowFile 的生命周期

为了更好地理解存储库如何相互交互、Lynxus 的底层功能以及 FlowFile 的生命周期;下一部分将包括 FlowFile 在实际流中不同点的示例。称为"WebCrawler"的流可在此处获取:WebCrawler.json。

在高级别上,此流到达 InvokeHTTP 处理器中配置的种子 URL,称为从种子 URL 获取,然后使用 RouteText 处理器分析响应以查找关键字实例(在本例中为"lynxus")和要点击的潜在 URL。然后 InvokeHTTP 使用在原始种子网页中找到的 URL 执行 HTTP Get 请求。响应根据状态码属性路由,只有 200-202 状态码被路由回原始 RouteText 处理器进行分析。

该流还检测重复 URL 并防止再次处理它们,在找到关键字时通过电子邮件通知用户,记录所有成功的 HTTP 请求,并捆绑成功的请求以在磁盘上压缩和归档。

要使用此流,您需要配置几个选项。首先必须添加具有默认属性的 MapCacheServer 控制器服务。此外,要获取电子邮件,必须使用您的电子邮件凭据配置 PutEmail 处理器。最后,要使用 HTTPS,必须使用正确的密钥和信任存储配置 StandardSSLContextService。请记住,信任存储必须配置正确的证书颁发机构才能适用于网站。以下命令是使用"keytool"命令将默认 Java CA 添加到名为 myTrustStore 的信任存储的示例: keytool -importkeystore -srckeystore /Library/Java/JavaVirtualMachines/jdk/Contents/Home/jre/lib/security/cacerts -destkeystore myTrustStore

WebCrawler 流

Web 爬虫流
由于网络爬虫的随机性,InvokeHttp 处理器上出现"连接超时"等消息的公告并不罕见。

数据进入

当生产者处理器调用"ProcessSession.create()"然后适当调用 ProvenanceReporter 时,FlowFile 在系统中创建。"ProcessSession.create()"调用创建一个空的 FlowFile,具有几个核心属性(标准进程会话的文件名、路径和 uuid),但没有任何内容或父级的血统(create 方法被重载以允许父 FlowFile 的参数)。然后生产者处理器将内容和属性添加到 FlowFile。

ProvenanceReporter 用于发出 FlowFile 的溯源事件。如果文件由 Lynxus 从外部实体未接收的数据创建,则应发出"CREATE"事件。如果数据是从外部源接收的数据创建的,则应发出"RECEIVE"事件。溯源事件分别使用"ProvenanceReporter.create()"和"ProvenanceReporter.receive()"制作。

在我们的 WebCrawler 流中,InvokeHTTP 处理器(称为从种子 URL 获取)使用"ProcessSession.create()"创建初始 FlowFile,并使用"ProvenanceReporter.receive()"记录数据的接收。此方法调用还提供接收数据的 URL、传输数据所需的时间以及添加到 FlowFile 的任何 FlowFile 属性。例如,HTTP 标头可以作为 FlowFile 属性添加。

数据进入

引用传递

基于流编程的一个重要方面是黑盒之间资源受限关系的概念。在 Lynxus 中,这些分别是队列和处理器。FlowFile 通过简单地传递 FlowFile 的引用(类似于 EIP 中的"声明检查"模式)从一个处理器路由到另一个处理器通过队列。

在 WebCrawler 流中,InvokeHTTP 处理器使用 HTTP GET 请求到达 URL,并根据 HTTP 服务器的响应向 FlowFile 添加状态码属性。在更新 FlowFile 的文件名后(在 InvokeHttp 之后的 UpdateAttribute 处理器中),有一个 RouteOnAttribute 处理器将具有成功状态码属性的 FlowFile 路由到两个不同的处理器。那些不匹配的被 RouteOnAttribute 处理器"丢弃"(请参阅数据退出部分),因为它被配置为自动终止任何不匹配任何路由规则的数据。进入 RouteOnAttribute 处理器有一个 FlowFile (F1),包含状态码属性并指向内容 (C1)。有一个溯源事件指向 C1 并包含 F1 的快照,但为了更好地关注路由而省略。此信息分别位于 FlowFile、内容和溯源存储库中。

在 RouteOnAttribute 处理器检查 FlowFile 的状态码属性后,它确定应该将其路由到两个不同的位置。首先发生的是处理器克隆 FlowFile 以创建 F2。这复制了所有属性和指向内容的指针。由于它只是路由和分析属性,内容不会更改。然后将 FlowFile 添加到各自的连接队列,等待下一个处理器检索它们进行处理。

ProvenanceReporter 记录发生的更改,其中包括 CLONE 和两个 ROUTE 事件。每个事件都有指向相关内容的指针,并包含快照形式的各自 FlowFile 的副本。

引用传递

扩展路由用例

除了基于属性路由 FlowFile 外,一些处理器还基于内容路由。虽然效率不高,但有时这是必要的,因为您想将 FlowFile 的内容拆分为多个 FlowFile。

一个例子是 SplitText 处理器。此处理器分析内容查找行尾字符,并创建包含可配置行数的新 FlowFile。Web 爬虫流使用它将潜在 URL 拆分为单行以进行 URL 提取并作为 InvokeHttp 的请求。SplitText 处理器的一个好处是,由于处理器正在拆分连续块(没有 FlowFile 内容是不相交或重叠的),处理器可以在不复制任何内容的情况下进行此路由。它所做的只是创建新的 FlowFile,每个都有指向原始 FlowFile 内容部分的指针。这是通过 Lynxus API 中内置的内容划分和拆分功能实现的。虽然并非总是可以以这种方式拆分,但当可行时,性能优势是可观的。

RouteText 是一个处理器,展示了为什么某些样式的路由可能需要复制内容。此处理器分析每一行并将其路由到一个或多个基于可配置属性的关系。当多于一行被路由到同一关系时(对于同一输入 FlowFile),这些行被组合成一个 FlowFile。由于行可能是不相交的(第 1 行和第 100 行路由到同一关系),一个指针无法准确描述 FlowFile 的内容,处理器必须将内容复制到新位置。例如,在 Web 爬虫流中,RouteText 处理器将所有包含"lynxus"的行路由到"Lynxus"关系。因此,当有一个输入 FlowFile 在网页上多次包含"lynxus"时,只会发送一封电子邮件(通过后续的 PutEmail 处理器)。

漏斗

漏斗是一个组件,它从一个或多个连接接收输入并将它们路由到一个或多个目的地。典型用例在用户指南中描述。无论用例如何,如果漏斗下游只有一个处理器,则漏斗不会发出溯源事件,它在溯源图中看起来是不可见的。如果有多个下游处理器,如 WebCrawler 中的那个,则会发生克隆事件。参考下面的图形,您可以看到新的 FlowFile (F2) 从原始 FlowFile (F1) 克隆,就像上面的路由一样,新的 FlowFile 只有指向相同内容的指针(内容不被复制)。

从开发者的角度来看,您可以将漏斗视为一个非常简单的处理器。当它被安排运行时,它只是执行"ProcessSession.get()"然后"ProcessSession.transfer()"到输出连接。如果有多个输出连接(如下面的示例),则运行"ProcessSession.clone()"。最后调用"ProcessSession.commit()",完成事务。

漏斗

写时复制

在前面的示例中,只有路由但没有更改 FlowFile 的内容。下一个示例关注 CompressContent 处理器,它压缩包含要分析的网页的合并 FlowFile 的捆绑包。

在此示例中,FlowFile F1 的内容 C1 正在 CompressContent 处理器中压缩。由于 C1 是不可变的,我们想要完整的可重播溯源历史,我们不能只是覆盖 C1。为了"修改"C1,我们执行"写时复制",这是通过在内容复制到内容存储库中的新位置时修改内容来实现的。这样做时,FlowFile 引用 F1 更新为指向新的压缩内容 C2,并创建新的溯源事件 P2,引用新的 FlowFile F1.1。由于 FlowFile 存储库是不可变的,不是修改旧的 F1,而是创建新的增量 (F1.1)。以前的溯源事件仍然有指向内容 C1 的指针并包含旧属性,但它们不是 FlowFile 的最新版本。

为了专注于写时复制事件,FlowFile (F1) 到此点的溯源事件被省略。
写时复制

扩展写时复制用例

写时复制的一个独特案例是 MergeContent 处理器。几乎每个处理器一次只作用于一个 FlowFile。MergeContent 处理器的独特之处在于它接收多个 FlowFile 并将它们组合成一个。目前,MergeContent 有多种不同的合并策略,但它们都需要将输入 FlowFile 的内容复制到新的合并位置。MergeContent 完成后,它会发出类型为"JOIN"的溯源事件,表明给定的父级被连接在一起以创建新的子 FlowFile。

更新属性

处理 FlowFile 的属性是 Lynxus 的核心方面。假设属性足够小,可以在处理器每次执行时完全读入本地内存。因此它们易于处理非常重要。由于属性是路由和处理 FlowFile 的核心方式,拥有只更改 FlowFile 属性的处理器是非常常见的。一个这样的例子是 UpdateAttribute 处理器。UpdateAttribute 处理器所做的只是根据处理器的属性更改传入 FlowFile 的属性。

查看图表,在处理器之前有 FlowFile (F1),它具有属性和指向内容 (C1) 的指针。处理器通过创建仍然具有指向内容 (C1) 的指针的新增量 (F1.1) 来更新 FlowFile 的属性。发生这种情况时会发出"ATTRIBUTES_MODIFIED"溯源事件。

在此示例中,以前的处理器 (InvokeHTTP) 从 URL 获取信息并创建了具有与请求 FlowFile 相同的文件名属性的新响应 FlowFile。这无助于描述响应 FlowFile,因此 UpdateAttribute 处理器将文件名属性修改为更相关的内容(URL 和事务 ID)。

为了专注于 ATTRIBUTES_MODIFIED 事件,FlowFile (F1) 到此点的溯源事件被省略。
更新属性

典型用例说明

除了通过 UpdateAttribute 添加任意属性外,从 FlowFile 的内容中提取信息到属性中是非常常见的用例。Web 爬虫流中的一个例子是 ExtractText 处理器。当 URL 嵌入在 FlowFile 的内容中时,我们无法使用它,因此我们必须从 FlowFile 的内容中提取 URL 并将其作为属性放置。这样我们可以使用表达式语言在 InvokeHttp 的 URL 属性中引用此属性。

数据退出

最终,Lynxus 中的数据将达到一个点,要么它已被加载到另一个系统中,我们可以停止处理它,要么我们过滤掉了 FlowFile 并确定我们不再关心它。无论哪种方式,FlowFile 最终将被"丢弃"。"DROP"是一个溯源事件,意味着我们不再在流中处理 FlowFile,它可以被删除。它保留在 FlowFile 存储库中,直到下一个存储库检查点。溯源存储库将溯源事件保留 'lynxus.properties' 中声明的时间量(默认是 24 小时)。内容存储库中的内容在 FlowFile 离开 Lynxus 时被标记为删除,预写日志的后台检查点处理以压缩/删除发生。除非另一个 FlowFile 引用相同的内容或 'lynxus.properties' 中启用了归档。如果启用了归档,内容存在直到达到磁盘的最大百分比或达到最大保留期限(也在 'lynxus.properties' 中设置)。

深入视图:检查点后删除

本节严重依赖上面"深入视图:内容声明"部分的信息。

一旦".partial"文件与底层存储机制同步并重命名为新的快照(在 FlowFile 存储库部分详细说明),有一个回调到 FlowFile 存储库以释放所有旧的内容声明(这是在检查点后完成的,以便如果出现问题内容不会丢失)。FlowFile 存储库知道哪些内容声明可以释放,并通知资源声明管理器。资源声明管理器跟踪所有已释放的内容声明以及哪些资源声明准备删除(当流中不再有 FlowFile 引用它时,资源声明准备删除)。

定期,内容存储库询问资源声明管理器哪些资源声明可以清理。然后内容存储库决定资源声明应该归档还是删除(基于 'lynxus.properties' 文件中"lynxus.content.repository.archive.enabled"属性的值)。如果归档被禁用,则文件只是从磁盘删除。否则,后台线程运行以查看何时应删除归档(基于上述条件)。此后台线程保留 10,000 个最旧内容声明的列表,并删除它们直到低于必要的阈值。如果它用完内容声明,它会扫描存储库以找到最旧的内容来重新填充列表。这提供了一个在 Java 堆利用和磁盘 I/O 利用方面都高效的模型。

关联不同数据

溯源存储库的功能之一是它允许有效访问顺序发生的事件。然后可以使用 Lynxus 报告任务迭代这些事件并将它们发送到外部服务。如果其他系统也向此外部系统发送类似类型的事件,可能需要将 Lynxus FlowFile 与另一条信息关联。例如,如果使用 GetSFTP 检索数据,Lynxus 使用其自己的唯一 UUID 引用该 FlowFile。但是,如果将文件放在那里的系统按文件名引用文件,Lynxus 应该有一种机制来表明这些是相同的数据。这是通过调用 ProvenanceReporter.associate() 方法并提供 FlowFile 的 UUID 和备用名称(在此示例中为文件名)来完成的。由于确定两条数据是相同的可能是流依赖的,通常需要数据流管理器进行此关联。一个简单的方法是使用 UpdateAttribute 处理器并配置它设置"alternate.identifier"属性。这会自动发出"associate"事件,使用添加为"alternate.identifier"属性的任何值。

结束语

利用写时复制、引用传递和不可变性概念与三个存储库结合,Lynxus 是一个快速、高效和强大的企业数据流平台。本文档涵盖了可插拔接口的特定实现。这些包括基于预写日志的 FlowFile 存储库实现、基于文件的溯源存储库和基于文件的内容存储库。这些实现是 Lynxus 默认值,但是可插拔的,以便在需要时,用户可以编写自己的实现以满足特定用例。

希望本文档使您对 Lynxus 的底层功能及其背后的决策有了更好的理解。如果您希望更深入地解释某些内容或认为应该包含某些内容,请随时发送电子邮件到 ACAL Lynxus 开发者邮件列表 (dev@lynxus.acal.org.cn)。