1

语境

我正在绘制一个将庞大的 PL/SQL 系统迁移到 Java 的解决方案。第一步是迁移一些 ETL 作业:

  1. 从多个 ftp / sftp 源读取 CSV、XML、(XLS,这是一个新要求)和位置文件
  2. 根据存储在数据库中的规则处理文件并将结果写入数据库表。

目前这是由几个存储过程和作业完成的。

我的公司愿意接受建议(如果它可以在 GlassFish 4 中运行并共享它的日志记录和连接池机制,以及管理控制台,那就太好了)。

我做了一些研究,以下选项引起了我的注意:

  1. Java EE 7 批处理,听起来很简单,特别适合 GlassFish 4。
  2. Spring Batch更加成熟,并且与 Java EE 7 标准(可能基于它)非常相似。
  3. Apache Camel听起来很强大,可以让我们免于大量摆弄诸如 Apache POI 之类的库,但它看起来也有些复杂。此外,我不确定它是否最适合这项工作(ETL 处理大文件)。
  4. 什么都自己煮。我可以创建一个应用程序客户端来运行 Quartz / Spring Scheduler 甚至 EJB Timers

虽然我仍然对建议持开放态度(建议会很好),但迄今为止最合适的似乎是 Java EE 7 批处理。

还有一件事,基础设施团队有一个解决方案,可以将文件从每个 ftp 源移动到本地目录,所以 FTP 真的不是问题。

问题

我已经阅读了一些关于 Java EE 批处理的教程,并且在所有这些教程中,某种ServletEJBTimer 负责启动作业:

JobOperator jobOperator = BatchRuntime.getJobOperator(); 
jobOperator.start("job", properties);

我可以很容易地上传一个 web / ejb 项目并保持汇集变化。但我在考虑一个推送模型:

  1. 应用程序客户端控制台应用程序
  2. 主类监视新文件的目录
  3. 当有一个新文件时,它将开始一个新工作。

我的疑问是:

  1. 这种策略可行/可取吗?
  2. 我是否需要一个 JMS 队列或中间的某种生产者/消费者策略,还是应该只调用jobOperator.start每个文件并信任批处理层来管理应用程序资源?换句话说,如果一千个文件一次传送到我的文件夹并且我调用jobOperator.start了一千次,GlassFish 4 是否会进行某种智能排队,或者我应该创建某种门以便n同时运行多个作业?
4

2 回答 2

3

我已经在 Wildfly (Jboss AS) 中使用批处理实现了一个项目。我不熟悉 Glassfish 的配置细节(不再使用它,因为它已经放弃了企业支持),但是我可以根据我的经验给你一些见解和指导。另外,请注意 Spring 和 Batch 规范。在 EE 7 上非常相似,您决定使用任何一种技术必须取决于除了批处理之外您还想通过应用程序实现的“其他”。您想要一个易于维护的 Web 界面吗?你想开发一个 REST api 吗?等。

您描述的 ETL 作业与 EE 7 规范中的步骤和块模型非常吻合,因此如果您已经尝试开发一些测试,您可能已经注意到您仍然需要为每个文件读取器和映射器编写代码文件规范。您的阅读资源非常标准,您可以轻松找到一个库来读取/流式传输它们并处理它们的数据。

我实施的项目非常简单。客户上传需要处理以提供数据仓库的文件。该服务在“云”上。文件具有已定义的规范,并且必须为 CSV 格式。大多数处理结果是维度的“Upserts”和事实“擦除之前的插入”。用户有一个 Web 界面,必须在该界面上显示文件和批处理元数据(处理状态、日期、拒绝项目等)。因为它是一种云服务,所以文件不能驻留在每台服务器的本地(使用 S3)。

所以首先要设计的是块步骤。我不想为每个文件规范都有一个实现。所以我所做的是设计一个“适合所有情况”的实现,根据文件中包含的元数据以及作业配置本身来处理文件。这是简单的部分。第二件要考虑的是处理和元数​​据管理。在这里,我开发了一个 REST api 和一个使用它的 Web 界面。毕竟,它会扩展吗?Wilfly 具有批处理的线程配置参数,您可以增加或减少 JobOperator 的线程可用性。如果没有足够的可用线程,则不会提交作业。那么这些请求发生了什么?好吧,它们可以驻留在内存中,可以开发一个备份的有状态会话,您绝对可以实现排队处理请求的 MQ 侦听器。我所做的要简单得多。该公司没有资源来维护集群,所以做了一个弹性配置,将根据 cpu 消耗和请求量进行扩展。到目前为止,该应用程序已经处理了来自 15 个客户的 10 TB 数据,并且在最大请求/处理峰值时,启动了 3 个弹性实例。

文件监听器是一个有趣的想法。您可以收听目录并将处理请求放入队列或立即放入 BatchRuntime。这将取决于您希望如何扩展它、您需要的响应时间、可用资源等。

有什么事情尽管问我。

问候。

编辑:忘了提。我真的不推荐使用应用程序客户端,除非你已经在你的组织上部署了一些东西。最近的安全限制和 java SE 更新机制给维护这些部署带来了真正的麻烦。想想网络。

于 2014-06-09T21:41:52.970 回答
2

我会这样处理它。

对于这个用例,我的重点是Java Watch Service、Servlet、JMS 队列和 Batch 服务。

首先,Watch Service 是 Java 7 用来处理文件系统监控的地方。

我会编写一个 Watch Service 实现,然后在线程上运行它。

你问线程在哪里运行?

正式地,您可能应该为此使用 JCA。但是,JCA 使用起来很痛苦,没有得到充分利用,因此没有得到充分的记录。有可靠的示例,但它根本不是 Java EE 堆栈中的常见技术。

另一个地方是异步会话 Bean 调用。没有任何迹象表明这些调用不能长期存在。您可以使用@Startup 建立一个@Singleton 会话Bean,从@PostConstruct 方法调用异步方法,然后放手。然后,在@PreDestroy 中通知长时间运行的方法停止,这样它就可以干净地关闭。根据 Hoyle 的说法,这一切都应该符合规范、便携。

第三个位置是 ServletContextListener,它是 Java EE 6 之前用于将代码绑定到应用程序生命周期的地方。在这里,您将在 contextInitialized 方法中自己创建线程,然后在 contextDestroyed 方法中将其拆除。

在这里创建线程“定义较少”,但我已经这样做了多年,从来没有遇到过问题。

现在您的服务正在运行,服务(恕我直言)将做两件事。

1)当一个新文件到达目录时它会感知,当它到达时,它会将文件移动(mv,重命名)到并行的“处理”目录。原因是这告诉您文件已从传入移动到处理中,该文件正在处理中。无论后端认为它在做什么,从目录列表中都很明显。请记住,系统可能会在文件中途关闭。

2) 移动后,将文件名和任何其他元数据发布到 JMS 队列,并让 MDB 执行批处理作业的工具。

为什么要添加 JMS 队列?它为聚会带来了一些功能。首先,这是从 EJB 喜欢的快乐事务上下文“外部”获取内容到内部的好方法。其次,它是事务性的。根据您的 ETL 用例,您可以让 MDB 直接处理该作业。通过这样做,您只需在处理完成之前不确认来自队列的消息(并且文件被删除或从“处理”目录中移动)。在理想情况下,消息队列具有与处理目录中的文件匹配的消息。处理完成后,方法返回,消息获取“提交”,您就完成了。如果系统崩溃,这将自动从头开始(因为消息仍在队列中并且从未被删除)。

MDB 通过配置它的实例,也可以控制同时作业的数量。配置10个实例,只能同时处理10个文件。但这可能有点太简单、太粗糙了。例如,没有优先级(先到先得)。但它可能对你有用。

但无论哪种方式,MDB 都是进入系统的一个很好的入口,因为每个都从它自己的一点事务上下文开始。与长时间运行的 servlet 线程或长时间运行的异步线程不同。servlet 线程具有可疑的(如果有的话)事务状态,长时间运行的线程从 @Startup 方法继承它的状态,并在其生命周期内保留它。MDB 每次都会得到一个新的。其中大部分都可以通过新事务调用方法来欺骗。

但我喜欢MDB的划分。即使整个任务是为文件名创建批处理条目,MDB 也是一个很好的看门人。

差不多就是这样。

关键部分是成为一个好公民,拆除与应用程序生命周期正确绑定的线程,了解各个组件的事务状态,并了解所有活动部分如何组合在一起。

如果使用@Startup 技术,请确保通过注入会话 bean 的另一个实例来调用异步方法。否则调用将是本地调用,而不是异步调用。你会盯着它想知道为什么你的服务器挂起而不启动。所有 EJB 注释仅在通过注入或查找代理调用时才起作用。

玩得开心,分享和享受。

问题的补充:

让外部进程管理监视服务确实没有任何价值。与服务器生命周期相关的一种更易于维护。我想到了两件事。如果服务器关闭,文件将简单地堆积在文件系统中,直到服务器再次启动,所以您不会丢失数据。如果您有一个外部服务,那么您要么让它向死服务器发送消息,要么您必须将 JMS 服务器与应用程序服务器分开进行暂存和管理。在这种情况下,您现在需要管理 3 个进程:Watch 服务、JMS 服务器和应用服务器,而不仅仅是应用服务器。

我同意另一张海报,如果您决定使用外部服务,一个简单的 Java SE 应用程序将简单的消息发布到服务器上的 JAX-RS REST 服务,甚至是一个微不足道的 Servlet,维护起来要容易得多,暂存和部署而不是应用程序客户端。如果你这样做,你可以用完全不同的方式编写监视服务。

但是由于服务器(表面上)可以通过文件直接访问文件系统,因此实际上没有动机在容器之外破坏该服务。将整个套件放入 EAR 并使用它。只是扁平化管理更容易。

于 2014-06-10T00:28:55.470 回答