gpt4 book ai didi

java - WAR 共享的容器级版本化库

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:25:52 26 4
gpt4 key购买 nike

在 Java servlet 容器中(最好是 Tomcat,但如果这可以在不同的容器中完成,那么请这样说)我想要一些理论上可行的东西。我的问题是是否存在支持它的工具,如果有的话是什么工具(或者我应该进一步研究哪些名称)。

这是我的问题:在一个 servlet 容器中,我想运行大量不同的 WAR 文件。它们共享一些大型公共(public)库(例如 Spring)。乍一看,我有两个无法接受的选择:

  1. 在每个 WAR 文件中包含大型库(例如 Spring)。这是 Not Acceptable ,因为它会加载大量的 Spring 副本,耗尽服务器上的内存。

  2. 将大型库放在容器类路径中。现在所有的 WAR 文件共享库的一个实例(很好)。但这是 Not Acceptable ,因为我无法在不立即升级所有 WAR 文件的情况下升级 Spring 版本,而且如此大的更改几乎不可能。

不过,从理论上讲,还有一个可行的替代方案:

  1. 将大型库的每个版本 放入容器级别的类路径中。做一些容器级别的魔法,以便每个 WAR 文件都声明它希望使用哪个版本,并且它会在其类路径中找到该版本。

“魔法”必须在容器级别完成(我认为),因为这只能通过使用不同的类加载器加载库的每个版本,然后调整每个 WAR 文件可见的类加载器来实现。

那么,你听说过这样做吗?如果是这样,如何?或者告诉我它叫什么,以便我进一步研究。

最佳答案

关于 Tomcat,对于第 7 版,您可以使用 VirtualWebappLocader像这样

<Context>
<Loader className="org.apache.catalina.loader.VirtualWebappLoader"
virtualClasspath="/usr/shared/lib/spring-3/*.jar,/usr/shared/classes" />
</Context>

对于第 8 个版本 Pre- & Post- Resources应该改用

<Context>
<Resources>
<PostResources className="org.apache.catalina.webresources.DirResourceSet"
base="/usr/shared/lib/spring-3" webAppMount="/WEB-INF/lib" />
<PostResources className="org.apache.catalina.webresources.DirResourceSet"
base="/usr/shared/classes" webAppMount="/WEB-INF/classes" />
</Resources>
</Context>

不要忘记将相应的 context.xml 放入您的 webapp 的 META-INF 中。

For the jetty以及其他容器可以使用相同的技术。唯一的区别在于如何为 webapp 指定额外的类路径元素。


更新上面的示例不共享加载的类,但想法是一样的——使用自定义类加载器。这只是一个非常丑陋的示例,它还试图防止在取消部署期间类加载器泄漏。


SharedWebappLoader

package com.foo.bar;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.loader.WebappLoader;

public class SharedWebappLoader extends WebappLoader {

private String pathID;
private String pathConfig;

static final ThreadLocal<ClassLoaderFactory> classLoaderFactory = new ThreadLocal<>();

public SharedWebappLoader() {
this(null);
}

public SharedWebappLoader(ClassLoader parent) {
super(parent);
setLoaderClass(SharedWebappClassLoader.class.getName());
}

public String getPathID() {
return pathID;
}

public void setPathID(String pathID) {
this.pathID = pathID;
}

public String getPathConfig() {
return pathConfig;
}

public void setPathConfig(String pathConfig) {
this.pathConfig = pathConfig;
}

@Override
protected void startInternal() throws LifecycleException {
classLoaderFactory.set(new ClassLoaderFactory(pathConfig, pathID));
try {
super.startInternal();
} finally {
classLoaderFactory.remove();
}
}

}

SharedWebappClassLoader

package com.foo.bar;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.loader.ResourceEntry;
import org.apache.catalina.loader.WebappClassLoader;

import java.net.URL;

public class SharedWebappClassLoader extends WebappClassLoader {

public SharedWebappClassLoader(ClassLoader parent) {
super(SharedWebappLoader.classLoaderFactory.get().create(parent));
}

@Override
protected ResourceEntry findResourceInternal(String name, String path) {
ResourceEntry entry = super.findResourceInternal(name, path);
if(entry == null) {
URL url = parent.getResource(name);
if (url == null) {
return null;
}

entry = new ResourceEntry();
entry.source = url;
entry.codeBase = entry.source;
}
return entry;
}

@Override
public void stop() throws LifecycleException {
ClassLoaderFactory.removeLoader(parent);
}
}

类加载器工厂

package com.foo.bar;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class ClassLoaderFactory {

private static final class ConfigKey {
private final String pathConfig;
private final String pathID;
private ConfigKey(String pathConfig, String pathID) {
this.pathConfig = pathConfig;
this.pathID = pathID;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

ConfigKey configKey = (ConfigKey) o;

if (pathConfig != null ? !pathConfig.equals(configKey.pathConfig) : configKey.pathConfig != null)
return false;
if (pathID != null ? !pathID.equals(configKey.pathID) : configKey.pathID != null) return false;

return true;
}

@Override
public int hashCode() {
int result = pathConfig != null ? pathConfig.hashCode() : 0;
result = 31 * result + (pathID != null ? pathID.hashCode() : 0);
return result;
}
}

private static final Map<ConfigKey, ClassLoader> loaders = new HashMap<>();
private static final Map<ClassLoader, ConfigKey> revLoaders = new HashMap<>();
private static final Map<ClassLoader, Integer> usages = new HashMap<>();

private final ConfigKey key;

public ClassLoaderFactory(String pathConfig, String pathID) {
this.key = new ConfigKey(pathConfig, pathID);
}

public ClassLoader create(ClassLoader parent) {
synchronized (loaders) {
ClassLoader loader = loaders.get(key);
if(loader != null) {
Integer usageCount = usages.get(loader);
usages.put(loader, ++usageCount);
return loader;
}

Properties props = new Properties();
try (InputStream is = new BufferedInputStream(new FileInputStream(key.pathConfig))) {
props.load(is);
} catch (IOException e) {
throw new RuntimeException(e);
}

String libsStr = props.getProperty(key.pathID);
String[] libs = libsStr.split(File.pathSeparator);
URL[] urls = new URL[libs.length];
try {
for(int i = 0, len = libs.length; i < len; i++) {
urls[i] = new URL(libs[i]);
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}

loader = new URLClassLoader(urls, parent);
loaders.put(key, loader);
revLoaders.put(loader, key);
usages.put(loader, 1);

return loader;
}
}

public static void removeLoader(ClassLoader parent) {
synchronized (loaders) {
Integer val = usages.get(parent);
if(val > 1) {
usages.put(parent, --val);
} else {
usages.remove(parent);
ConfigKey key = revLoaders.remove(parent);
loaders.remove(key);
}
}
}

}

第一个应用的context.xml

<Context>
<Loader className="com.foo.bar.SharedWebappLoader"
pathConfig="${catalina.base}/conf/shared.properties"
pathID="commons_2_1"/>
</Context>

第二个app的context.xml

<Context>
<Loader className="com.foo.bar.SharedWebappLoader"
pathConfig="${catalina.base}/conf/shared.properties"
pathID="commons_2_6"/>
</Context>

$TOMCAT_HOME/conf/shared.properties

commons_2_1=file:/home/xxx/.m2/repository/commons-lang/commons-lang/2.1/commons-lang-2.1.jar
commons_2_6=file:/home/xxx/.m2/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar

关于java - WAR 共享的容器级版本化库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25897954/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com