12

我们有一个用 Java 制作的 Web 应用程序,它使用 struts2、spring 和 JasperReport。此应用程序在 glassfish 4.0 上运行。

该应用程序的库位于 WEB-INF/lib 文件夹中,并且在 glassfish 中安装了 4 个以上使用相同的库。

Glassfish 配置为使用 1024mb 的堆空间和 512m 的 permgen,当我使用每个应用程序的库时,大部分内存消耗都在 struts 操作和 spring aop 类中(使用 netbeans 分析器)。

我们遇到的问题是每个应用程序的类加载器中的库消耗的内存量很高,并且会产生 PermGen 错误,而且我们还注意到应用程序在用户越多时运行速度越慢。

因此我们尝试使用共享库,将其放在 domain1/lib 文件夹中,发现使用单个部署的应用程序的加载时间和内存消耗要低得多,并且应用程序通常运行得更快。但是当我们在服务器上部署其余的应用程序时,只有第一个加载的应用程序运行良好,其余的在我们调用 struts2 操作时会出错。我们认为这是因为每个应用程序在 struts2 和 log4j 上的设置略有不同。

我们还尝试仅将某些库放在 glassfish 上并仅将 struts2 留在应用程序中,但它显示 InvocationTargetException 错误,因为所有库都依赖于 apache-common 的库,并且我们将这些库放在一个地方还是另一个地方都没有关系。此外,如果我们把它放在两个地方,应用程序也不会启动。

  1. 使用共享库有什么特殊设置或最佳实践吗?
  2. 有没有办法使用共享库但为每个应用程序加载设置?或者我们必须更改设置以使它们都相同?
4

4 回答 4

3

使用共享库是否有任何特殊设置或最佳实践?有没有办法使用共享库但为每个应用程序加载设置?或者我们必须更改设置以使它们都相同?

这些实际上是有趣的问题......我不使用 GlassFish,但根据文档

特定于应用程序的类加载

[...] 您可以指定模块或应用程序特定的库类 [...] 使用带有--libraries选项的 asadmin deploy 命令并指定逗号分隔的路径 [...]

规避类加载器隔离

由于每个应用程序或单独部署的模块类加载器世界都是隔离的,因此应用程序或模块无法从另一个应用程序或模块加载类。这可以防止不同应用程序或模块中的两个类似命名的类相互干扰。

要规避由多个应用程序访问的库、实用程序类或单独部署的模块的这一限制,您可以通过以下方式之一包含所需类的相关路径:

  • 使用通用类加载器
  • 跨集群共享库
  • 将一个应用程序的客户端 JAR 打包到另一个应用程序中

使用通用类加载器

要使用通用类加载器,请将 JAR 文件复制到domain-dir/liboras-install/lib目录或将 .class 文件(和其他需要的文件,例如 .properties 文件)复制到domain-dir/lib/classes目录中,然后重新启动服务器。

使用通用类加载器可以让部署在共享相同配置的服务器上的所有应用程序或模块访问应用程序或模块。但是,这种可访问性并未扩展到应用程序客户端。有关更多信息,请参阅将库与应用程序客户端一起使用。[...]

然后我会尝试:

解决方案 1

  • 将除 Struts2 jar 之外的所有库放在domain1/lib,
  • 仅将 Struts2 jar 放在下面domain1/lib/applibs

然后运行

$ asadmin deploy --libraries struts2-core-2.3.15.2.jar FooApp1.war
$ asadmin deploy --libraries struts2-core-2.3.15.2.jar FooApp2.war

隔离 Struts2 库的类加载,同时将其余部分置于 Common Classloader 的控制之下。

解决方案 2

  • 将除 Struts2 jar 之外的所有库放在domain1/lib,
  • 仅将 Struts2 jar 放在domain1/lib/applibs具有不同名称的不同副本中,例如在 jar 名称处附加 _appname

然后运行

$ asadmin deploy --libraries struts2-core-2.3.15.2_FooApp1.jar FooApp1.war
$ asadmin deploy --libraries struts2-core-2.3.15.2_FooApp2.jar FooApp2.war

通过实例化(模拟)它们的不同版本来防止共享库。

希望对您有所帮助,如果上述某些方法有效,请告诉我。

于 2013-11-13T15:26:05.443 回答
2

您可以尝试创建所谓的瘦 WAR。将所有 WAR 打包到 EAR 中,并将所有常见的 JAR 从EARWEB-INF/liblib/文件夹中移出(不要忘记在 EAR 中进行设置<library-directory>application.xml

于 2013-11-11T14:54:39.383 回答
0

我敢打赌,将库放在 lib/ 或 lib/ext 下不会解决您的性能问题。你没有写任何关于应用程序或服务器设置的东西,比如应用程序的大小、可用的堆和 PermGen 空间,但我仍然建议每个应用程序使用单独的库。

如果您将库放在服务器目录中,它们将在所有应用程序之间共享。您将失去仅将其中一个应用程序升级到新框架或摆脱其中任何一个的选项。您的部署将绑定到特定的服务器架构。

而你写的它并没有解决你的问题,它甚至可能引发新的问题。

我建议花一些时间来调整服务器。如果它以默认值运行,则分配更多的 PermGen 和 HeapSpace。

如果这没有帮助,您应该深入分析出了什么问题。共享库可能是一个解决方案,但您还不知道问题所在。IBM 提供了一些很酷的免费工具来分析堆转储,这可能是一个很好的起点。

于 2013-11-10T18:53:24.383 回答
0

我来这里是为了寻找有关安装在多个应用程序或项目之间共享的库的指导。我深感失望的是,公认的做法倾向于将每个共享库的副本安装到每个项目中。因此,如果您有十个 Web 应用程序,所有这些应用程序都使用,例如 httpcomponents-client、mysql-connector-java 等,那么您的安装包含每个的十个副本。

这种行为让我痛苦地想起了促使我放弃大型机而转向 PC 的思维方式。想法似乎是“我不在乎我的应用程序消耗了多少资源。事实上,我希望能够吹嘘它是多么的资源消耗。” 对不起,请在我投掷时。

  • 库公开的接口是一个不可变的合同,不会随开发人员的心血来潮而改变

  • 有一个称为向后兼容性的概念。如果你打破它,你就会创建一个新的界面。

我知道至少有两种类型的接口符合这些规则的文字和精神。

  1. 迄今为止最古老的是IBM System/370 系统库。您可能拥有Fooand Foo2,后者Foo以某种方式扩展和/或破坏接口所做的合同,使其不兼容。

  2. 从贝尔实验室的 Unix 项目开始,标准 C 运行时库就遵循上述规则。

  3. 尽管它更新很多,但Microsoft COM 接口规范强制执行相同的规则。

值得称赞的是,Microsoft 通常也遵守Win32 API中的这些规则,尽管该 API 中有少数例外。在某种程度上,他们使用 .NET Framework 倒退了,这似乎是在追随它急切寻求取代的 Java 环境的脚步。

自 1978 年以来我一直在使用库,我的理解是,将代码放入库的目标是使其可重用。虽然在每个应用程序中维护库代码的副本消除了为每个新项目再次实现它的需要,但它使升级变得非常复杂,因为您现在有十个(或更多)库副本,每个副本都必须更新。

如果库遵守接口是不可变契约的规则,为什么它们不应该存在于共享库目录中,就像存在于其/lib目录中的 Unix 系统库一样,主机上运行的所有内容都共享一个副本标准 C 运行时库、Zlib 等。

让我非常失望。

于 2017-09-20T05:34:12.077 回答