8

我们正在考虑在 Java EE 中开发任务关键型应用程序,让我印象深刻的一件事是平台中缺少会话隔离。让我解释一下这个场景。

我们有一个本机 Windows 应用程序(一个完整的 ERP 解决方案),每月从稀疏的贡献者那里接收大约 2k 个 LoC 和 50 个错误修复。它还支持脚本,因此客户可以添加自己的逻辑,我们不知道这些逻辑的作用。每个服务器节点都没有使用线程池,而是有一个代理和一个进程池。代理接收客户端请求,将其排入队列直到池中的实例空闲,向该实例发送请求,向客户端传递响应,然后将实例释放回进程池。

这种架构是健壮的,因为有这么多的稀疏贡献和自定义脚本,部署的版本有一些严重的错误并不罕见,例如无限循环、长期等待的悲观锁、内存损坏或内存泄漏。我们实现了内存限制、请求超时和简单的看门狗。每当某个进程未能按时正确响应时,代理就会简单地将其杀死,因此看门狗会检测并启动另一个实例。如果一个进程在开始响应请求之前崩溃,代理将相同的请求发送到另一个池实例,并且用户不知道服务器端的任何故障(管理日志除外)。这很好,因为有些实例在处理请求时会被虚假代码慢慢破坏。

现在考虑迁移到 Java EE,我在规范或流行的应用程序服务器(如 Glassfish 和 JBoss)上找不到任何类似的东西。是的,我知道大多数集群实现都使用会话复制进行透明故障转移,但是我们有一些小公司在简单的 2 节点集群上使用我们的系统(我们也有冒险者在 1 节点服务器上使用系统) . 使用线程池,我知道一个有问题的线程可能会导致整个节点停机,因为服务器无法检测到并安全地杀死它。关闭整个节点比杀死单个进程要糟糕得多——我们的部署中每个节点都有大约 100 个池化进程实例。

我知道 IBM 和 SAP 都意识到了这个问题,基于

, 分别。但是根据最近的 JSR、论坛和开源工具,社区上的活动并不多。

现在问题来了!

  1. 如果你有类似的场景,使用 Java EE,你是怎么解决的?

  2. 您是否知道即将推出的开源产品或 Java EE 规范的变化可以解决这个问题?

  3. .NET 有同样的问题吗?你能解释或引用参考资料吗?

  4. 你知道一些可以解决这个问题并且值得做 ERP 业务逻辑的现代开放平台吗?

拜托,我不得不问你不要告诉你做更多的测试或任何类型的质量保证投资,因为我们不能强迫我们的客户在他们自己的脚本上做这个。我们也有紧急错误修复必须绕过 QA 的情况,虽然我们强迫客户接受这一点,但我们不能让他接受有错误的软件部分会影响一系列不相关的功能。这是关于健壮架构的问题,而不是开发过程。

感谢您的关注!

4

3 回答 3

6

您偶然发现的是有关使用 Java 和“敌对”应用程序的基本问题。

这不仅是 Java EE 级别的基本问题,也是核心 JVM 级别的基本问题。可用的典型 JVM 在加载“不安全代码”方面存在各种问题。从内存泄漏、类加载器泄漏、资源耗尽和不干净的线程终止,典型的 JVM 根本不够健壮,无法在共享环境中很好地处理行为不良的代码。

一个简单的例子是 Java 堆的内存耗尽。作为一个基本规则,NOBODY(没有人,我特别指的是核心 java 库和几乎所有其他第三方库)捕获 OutOfMemory 异常。很少有人这样做,但即使他们也无能为力。典型代码处理他们“期望”处理的异常,但让其他代码失败。运行时异常(其中 OOM 就是其中之一)将愉快地通过调用堆栈一直冒泡到顶部,留下未经检查的关键路径代码的残骸,使各种事物处于未知状态。

诸如构造函数或静态初始化程序之类的“不会失败”的东西会留下“永不为空”的未初始化的类成员。这些损坏的类根本不知道它们已损坏。没有人知道它们被损坏了,也没有办法清理它们。命中 OOM 的堆是不安全的映像,几乎需要重新启动(当然,除非您自己编写或审核了所有代码,当然,您不会——谁会?)。

现在,很可能会有特定于供应商的 JVM,它们的行为更好,可以更好地控制。基于 Sun/Oracle JVM 的那些(即大多数)没有。

因此,这不一定是 Java EE 问题,而是 JVM 问题。

在 JVM 中托管恶意代码是一个坏主意。唯一可行的方法是托管脚本语言,并且该脚本语言实现某种资源控制。这可以做到,并且您可以调整现有的作为开始(JavaScript、Groovy、JPython、JRuby)。这些语言允许用户直接访问 Java 库这一事实使它们具有潜在的危险性,因此您可能还必须将其限制为仅由脚本处理程序包装的方面。但是,此时,“为什么要使用 Java”的问题浮出水面。

您会注意到 Google App Engine 不执行这些操作。它为每个正在运行的应用程序建立一个单独的 JVM,但即便如此,它也极大地限制了在这些 JVM 中可以完成的操作,特别是通过现有的 Java 安全模型。这里的区别在于这些实例往往是“长期存在的”,以免承受启动和关闭的处理成本。我应该说,他们应该长寿,而那些不长寿的人确实会产生这些成本。

您可以自己创建多个 JVM 实例,为它们提供一些基础架构来处理逻辑请求,为它们提供自定义类加载器逻辑以尝试防止类加载器泄漏,并尽可能地让您杀死实例(它们只是一个过程)如果你想。这可以工作,并且可能工作“正常”,具体取决于调用的粒度和逻辑的“启动”时间。启动时间将至少是从运行到运行的逻辑类的加载,仅此一项就可能使这成为一个坏主意。它肯定不会是“Java EE”。Java EE 不适合做这种事情。但是您也不清楚您正在查看哪些 Java EE 功能。

实际上,这就是 Apache 和“mod_php”所做的。几个实例,作为进程,单独处理请求,一旦有必要被杀死,表现不佳。这就是为什么 PHP 在共享主机业务中很常见的原因。在这种结构中,它基本上是“安全的”。

于 2011-03-09T20:56:10.673 回答
0

我相信您的场景非常不典型,因此不可能有现成的框架/平台来满足这一需求。Java EE 假设请求处理代码与应用程序的其余部分是由同一个团队编写的,因此不需要经常隔离、监视和重置它,并且错误修复将在所有部分以相同的方式处理系统。这个假设极大地简化了大多数项目的开发、部署、测试等,而不是强迫他们为不需要的东西付费,是的,它并不适合所有人。如果你想要一些根本不同的东西,你可能需要自己实现相当多的故障转移逻辑。Java EE 确实为此提供了基本的构建块。

我相信(尽管没有具体的经验来证明).NET 或其他平台基本上是建立在类似的假设之上的。

于 2011-03-09T20:23:07.043 回答
0

我们将一个非常庞大的 Perl 站点移植到 Java 中,虽然不是那么严重,但我们也有类似的情况。在接收到 HTTP 请求时,我们实例化一个类并调用它的 processRequest 方法。被try-catch和时间测量包围。添加计时器和线程就足以杀死线程。这在现实生活中可能就足够了。

像 glassfish 这样的 Java EE 服务器是一个 OSGi 容器,您可能有更多的隔离方式。

您还可以运行一系列(Web 或本地)应用程序,通过中央 Web 应用程序在这些应用程序上发送您的请求。然后这些应用程序被隔离。

更孤立的是序列化会话和启动新 JVM 的操作系统进程。

于 2011-11-23T14:39:42.463 回答