forked from alibaba/jvm-sandbox
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request alibaba#381 from zhaoyb1990/master
Add class routing feature
- Loading branch information
Showing
22 changed files
with
1,062 additions
and
32 deletions.
There are no files selected for viewing
44 changes: 44 additions & 0 deletions
44
sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/routing/AbstractRouting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.alibaba.jvm.sandbox.api.routing; | ||
|
||
/** | ||
* {@link AbstractRouting} | ||
* <p> | ||
* 抽象路由规则实现;包装了一层{@link AbstractRouting#appRouting()}逻辑,帮助找到应用的业务类加载器并优先使用 | ||
* | ||
* @author zhaoyb1990 | ||
*/ | ||
public abstract class AbstractRouting implements RoutingExt { | ||
|
||
@Override | ||
public RoutingInfo getSpecialRouting() { | ||
// 优先根据启动方式使用业务类加载器路由 | ||
RoutingInfo appRouting = appRouting(); | ||
if (appRouting != null) { | ||
return appRouting; | ||
} | ||
return getSecondary(); | ||
} | ||
|
||
/** | ||
* 应用类加载器 | ||
* | ||
* @return 类路由器 | ||
*/ | ||
protected RoutingInfo appRouting() { | ||
return LaunchedRouting.useLaunchingRouting(getPattern()); | ||
} | ||
|
||
/** | ||
* 兜底路由信息 | ||
* | ||
* @return 类路由器 | ||
*/ | ||
protected abstract RoutingInfo getSecondary(); | ||
|
||
/** | ||
* 类路由的匹配器 | ||
* | ||
* @return 匹配表达式 | ||
*/ | ||
protected abstract String[] getPattern(); | ||
} |
69 changes: 69 additions & 0 deletions
69
sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/routing/LaunchedRouting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.alibaba.jvm.sandbox.api.routing; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* {@link LaunchedRouting} | ||
* <p> | ||
* 根据应用启动方式,匹配对应的容器的类加载器进行路由 | ||
* | ||
* @author zhaoyb1990 | ||
*/ | ||
public class LaunchedRouting { | ||
|
||
private final static Pattern CLASS_PATTERN = Pattern.compile("\\S*([_a-zA-Z0-9.*])\\S*"); | ||
|
||
private final static Map<String, String> CLASSLOADER_WRAPPER = new HashMap<String, String>(); | ||
|
||
static { | ||
/* tomcat */ | ||
CLASSLOADER_WRAPPER.put("org.apache.catalina.startup.Bootstrap", | ||
"org.apache.catalina.loader.ParallelWebappClassLoader"); | ||
/* springboot */ | ||
CLASSLOADER_WRAPPER.put("org.springframework.boot.loader.JarLauncher", | ||
"org.springframework.boot.loader.LaunchedURLClassLoader"); | ||
/* pandora boot*/ | ||
CLASSLOADER_WRAPPER.put("com.taobao.pandora.boot.loader.SarLauncher", | ||
"com.taobao.pandora.boot.loader.LaunchedURLClassLoader"); | ||
} | ||
|
||
/** | ||
* 获取sun.java.command系统属性 | ||
* | | ||
* v | ||
* 找到启动类 | ||
* | | ||
* v | ||
* 映射classloader | ||
* | ||
* @param pattern 类匹配表达式 | ||
* @return 类路由规则 | ||
*/ | ||
public static RoutingInfo useLaunchingRouting(String... pattern) { | ||
String sunJavaCommand = System.getProperty("sun.java.command"); | ||
String launchClass = matching(sunJavaCommand); | ||
if (isEmpty(launchClass)) { | ||
return null; | ||
} | ||
String classLoaderName = CLASSLOADER_WRAPPER.get(launchClass); | ||
if (isEmpty(classLoaderName)) { | ||
return null; | ||
} | ||
return RoutingInfo.withTargetClassloaderName(classLoaderName, pattern); | ||
} | ||
|
||
private static String matching(String source) { | ||
Matcher matcher = CLASS_PATTERN.matcher(source); | ||
if (matcher.find()) { | ||
return matcher.group(0); | ||
} | ||
return ""; | ||
} | ||
|
||
private static boolean isEmpty(String sequence) { | ||
return sequence == null || sequence.isEmpty(); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/routing/RoutingExt.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.alibaba.jvm.sandbox.api.routing; | ||
|
||
/** | ||
* <p> | ||
* 提供给模块的扩展类路由SPI | ||
* <p> | ||
* 使用该扩展能力需要在模块资源路径(src/main/resources)下创建yaml配置文件(META-INFO/class-routing-config.yaml),开启路由开关并配置SPI模式 | ||
* <p> | ||
* 详细配置方式,可参考(META-INFO/class-routing-config.yaml) | ||
* | ||
* @author zhaoyb1990 | ||
* @since {@code sandbox-common-api:1.4.0} | ||
*/ | ||
public interface RoutingExt { | ||
|
||
/** | ||
* 获取类的特殊路由方式 | ||
* | ||
* @return 类路由方式 | ||
*/ | ||
RoutingInfo getSpecialRouting(); | ||
} |
147 changes: 147 additions & 0 deletions
147
sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/routing/RoutingInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package com.alibaba.jvm.sandbox.api.routing; | ||
|
||
/** | ||
* <p> | ||
* 提供给模块使用的目标类路由白名单,匹配到pattern的类将通过目标类加载器加载 | ||
* <p> | ||
* - 模块可以通过引入业务包进行编码,不用通过反射调用(引入jar: scope=provided) | ||
* - 一些框架类型自定义类隔离机制(类似SOFA/Pandora)可自由选择类加载 | ||
* | ||
* @author zhaoyb1990 | ||
*/ | ||
public class RoutingInfo { | ||
|
||
/** | ||
* 路由类型 | ||
*/ | ||
public enum Type { | ||
/** | ||
* 被pattern匹配的类,由目标的类的类加载器进行加载 | ||
*/ | ||
TARGET_CLASS, | ||
/** | ||
* 被pattern匹配的类,由目标类载器进行加载 | ||
*/ | ||
TARGET_CLASS_LOADER, | ||
/** | ||
* 被pattern匹配的类,由目标类加载器进行加载 | ||
*/ | ||
TARGET_CLASS_LOADER_NAME | ||
} | ||
|
||
/** | ||
* 路由匹配正则表达式 | ||
*/ | ||
private String[] pattern; | ||
|
||
/** | ||
* 路由类型 | ||
*/ | ||
private Type type; | ||
|
||
/** | ||
* 目标类 | ||
*/ | ||
private String targetClass; | ||
|
||
/** | ||
* 目标类加载器的名字 | ||
*/ | ||
private String targetClassLoaderName; | ||
|
||
/** | ||
* 目标类加载器 | ||
*/ | ||
private ClassLoader targetClassloader; | ||
|
||
private RoutingInfo() {} | ||
|
||
/** | ||
* 使用目标类的加载器进行路由 | ||
* | ||
* @param targetClass 目标类 | ||
* @param pattern 匹配类正则 | ||
*/ | ||
public RoutingInfo(String targetClass, String... pattern) { | ||
this.type = Type.TARGET_CLASS; | ||
this.pattern = pattern; | ||
this.targetClass = targetClass; | ||
} | ||
|
||
/** | ||
* 使用目标类的加载器进行路由 | ||
* | ||
* @param classLoader 目标类加载器 | ||
* @param pattern 匹配类正则 | ||
*/ | ||
public RoutingInfo(ClassLoader classLoader, String... pattern) { | ||
this.type = Type.TARGET_CLASS_LOADER; | ||
this.pattern = pattern; | ||
this.targetClassloader = classLoader; | ||
} | ||
|
||
/** | ||
* 使用目标类名称进行路由 | ||
* | ||
* @param targetClass 目标类名称(如果出现多个类加载器,会使用第一个,默认会屏蔽sandbox自身的类加载器) | ||
* @param pattern 匹配类正则 | ||
* @return 路由规则 | ||
*/ | ||
public static RoutingInfo withTargetClass(String targetClass, String... pattern) { | ||
RoutingInfo routingInfo = new RoutingInfo(); | ||
routingInfo.type = Type.TARGET_CLASS; | ||
routingInfo.pattern = pattern; | ||
routingInfo.targetClass = targetClass; | ||
return routingInfo; | ||
} | ||
|
||
/** | ||
* 使用目标类加载器进行路由 | ||
* | ||
* @param targetClassloader 目标加载器 | ||
* @param pattern 匹配类正则 | ||
* @return 路由规则 | ||
*/ | ||
public static RoutingInfo withTargetClassloader(ClassLoader targetClassloader, String... pattern) { | ||
RoutingInfo routingInfo = new RoutingInfo(); | ||
routingInfo.type = Type.TARGET_CLASS_LOADER; | ||
routingInfo.pattern = pattern; | ||
routingInfo.targetClassloader = targetClassloader; | ||
return routingInfo; | ||
} | ||
|
||
/** | ||
* 使用目标类路由器名称进行路由 | ||
* | ||
* @param targetClassLoaderName 目标类路由器名称, 通过classLoader#getClass()#getName()提取 | ||
* @param pattern 匹配类正则 | ||
* @return 路由规则 | ||
*/ | ||
public static RoutingInfo withTargetClassloaderName(String targetClassLoaderName, String... pattern) { | ||
RoutingInfo routingInfo = new RoutingInfo(); | ||
routingInfo.type = Type.TARGET_CLASS_LOADER_NAME; | ||
routingInfo.pattern = pattern; | ||
routingInfo.targetClassLoaderName = targetClassLoaderName; | ||
return routingInfo; | ||
} | ||
|
||
public String[] getPattern() { | ||
return pattern; | ||
} | ||
|
||
public Type getType() { | ||
return type; | ||
} | ||
|
||
public String getTargetClass() { | ||
return targetClass; | ||
} | ||
|
||
public ClassLoader getTargetClassloader() { | ||
return targetClassloader; | ||
} | ||
|
||
public String getTargetClassLoaderName() { | ||
return targetClassLoaderName; | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/util/ClassloaderUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.alibaba.jvm.sandbox.api.util; | ||
|
||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
/** | ||
* {@link ClassloaderUtil} | ||
* <p> | ||
* 类加载器工具 | ||
* | ||
* @author zhaoyb1990 | ||
*/ | ||
public class ClassloaderUtil { | ||
|
||
private final static String PANDORA_CLASSLOADER = "com.taobao.pandora.service.loader.ModuleClassLoader"; | ||
|
||
private final static Set<String> BIZ_CLASS_LOADERS = new HashSet<String>(); | ||
|
||
static { | ||
// tomcat | ||
BIZ_CLASS_LOADERS.add("org.apache.catalina.loader.ParallelWebappClassLoader"); | ||
// spring boot | ||
BIZ_CLASS_LOADERS.add("org.springframework.boot.loader.LaunchedURLClassLoader"); | ||
// pandora boot | ||
BIZ_CLASS_LOADERS.add("com.taobao.pandora.boot.loader.LaunchedURLClassLoader"); | ||
} | ||
|
||
/** | ||
* 包装类加载器名称 | ||
* | ||
* @param classLoader 类加载器 | ||
* @return 类加载器名称 | ||
*/ | ||
public static String wrapperName(ClassLoader classLoader) { | ||
if (classLoader == null) { | ||
return "BootstrapClassLoader"; | ||
} | ||
String name = classLoader.getClass().getName(); | ||
if (PANDORA_CLASSLOADER.equals(name)) { | ||
return classLoader.toString(); | ||
} | ||
return name; | ||
} | ||
|
||
/** | ||
* 查询当前应用的容器类加载器 | ||
* | ||
* @param classLoaders 类加载器 | ||
* @return true / false | ||
*/ | ||
public static ClassLoader findLaunchedClassLoader(Set<ClassLoader> classLoaders) { | ||
for (ClassLoader classLoader : classLoaders) { | ||
if (BIZ_CLASS_LOADERS.contains(wrapperName(classLoader))) { | ||
return classLoader; | ||
} | ||
} | ||
return null; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
sandbox-api/src/main/resources/META-INF/class-routing-config.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# 路由启用总开关: true - 打开 | false - 关闭 | ||
classRoutingEnable: true | ||
|
||
# 路由配置读取方式: | ||
# 方式一: yaml - 通过yaml方式配置,比较纯净轻量级方式,通过文件读取生成配置,不支持targetClassLoader模式 | ||
# 方式二: spi - 通过SPI方式配置,支持targetClassLoader(指定classloader)路由,该方式会多一次模块jar的加载,有额外内存开销 | ||
routingConfigType: yaml | ||
|
||
# yaml方式配置列表,配置参数如下: | ||
# usingApp: 是否优先使用业务容器类加载器 | ||
# type: 路由匹配类型 | ||
# targetName: 匹配器目标类型 | ||
# pattern: 路由匹配表达式,{@link RoutingURLClassLoader$Routing }, 支持多个匹配表达式 | ||
routingConfigs: | ||
# dubbo 目标类名方式路由参考 | ||
- | ||
usingApp: true | ||
type: 'targetClass' | ||
targetName: 'org.apache.dubbo.rpc.model.ApplicationModel' | ||
pattern: ['^org.apache.dubbo..*'] | ||
|
||
# http 目标类名方式路由参考 | ||
- | ||
usingApp: true | ||
type: 'targetClass' | ||
targetName: 'javax.servlet.http.HttpServlet' | ||
pattern: [ '^javax.servlet..*' ] | ||
|
||
# dubbo 目标类加载器模式路由参考 | ||
- | ||
type: 'targetClassloaderName' | ||
targetName: 'org.apache.catalina.loader.WebappClassLoader' | ||
pattern: ['^org.apache.dubbo..*'] |
Oops, something went wrong.