如何使用 Jarslink
1:引入 POM
<dependency>
<groupId>com.alipay.jarslink</groupId>
<artifactId>jarslink-api</artifactId>
<version>1.5.0.20180213</version>
</dependency>
JarsLink 依赖的 POM 也需要引入
<properties>
<slf4j.version>1.7.7</slf4j.version>
<apache.commons.lang.version>2.6</apache.commons.lang.version>
<apache.commons.collections.version>3.2.1</apache.commons.collections.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${apache.commons.lang.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${apache.commons.collections.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2:引入 jarslink BEAN
在系统中引入以下两个 BEAN。
<!-- 模块加载引擎 -->
<bean name="moduleLoader" class="com.alipay.jarslink.api.impl.ModuleLoaderImpl"></bean>
<!-- 模块管理器 -->
<bean name="moduleManager" class="com.alipay.jarslink.api.impl.ModuleManagerImpl"></bean>
3:集成 JarsLink API
使用 JarsLink API 非常简单,只需要继承 AbstractModuleRefreshScheduler,并提供模块的配置信息,代码如下:
public class ModuleRefreshSchedulerImpl extends AbstractModuleRefreshScheduler {
@Override
public List<ModuleConfig> queryModuleConfigs() {
return ImmutableList.of(ModuleManagerTest.buildModuleConfig());
}
public static ModuleConfig buildModuleConfig() {
URL demoModule = Thread.currentThread().getContextClassLoader().getResource("META-INF/spring/demo-1.0.0.jar");
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setName("demo");
moduleConfig.setEnabled(true);
moduleConfig.setVersion("1.0.0.20170621");
moduleConfig.setProperties(ImmutableMap.of("svnPath", new Object()));
moduleConfig.setModuleUrl(ImmutableList.of(demoModule));
return moduleConfig;
}
这个调度器在 bean 初始化的时候会启动一个调度任务,每分钟刷新一次模块,如果模块的版本号发生变更则会更新模块。实现这个方法时,必须把模块(jar包)下载到机器本地,模块的配置信息说明如下:
- name:全局唯一,建议使用英文,忽略大小写。
- enabled:当前模块是否可用,默认可用,卸载模块时可以设置成 false。
- version:模块的版本,如果版本号和之前加载的不一致,框架则会重新加载模块。
- Properties:spring 属性配置文件。
- moduleUrl:模块的本地存放地址。
- overridePackages:需要突破双亲委派的包名,一般不推荐使用,范围越小越好,如 com.alipay.XX。
把 ModuleRefreshSchedulerImpl
类注册成 Spring 的 bean。
<bean
class="com.alipay.**.ModuleRefreshSchedulerImpl">
<property name="moduleManager" ref="moduleManager" />
<property name="moduleLoader" ref="moduleLoader" />
</bean>
注意:queryModuleConfigs() 方法返回的 ModuleConfig
列表中不能有 name 值相同的 ModuleConfig
,如果有多个 ModuleConfig
的 name 值相同会在运行时抛出异常 java.lang.IllegalArgumentException:Multiple entries with same key:.....
JarsLink API 暂时不提供模块可视化管理能力,所以需要使用其他系统来管理和发布模块。目前可以通过com.alipay. jarslink.api.ModuleManager#getModules
获取运行时所有模块的信息。
你也可以使用API来加载并注册模块,详细使用方式可以参考 ModuleManagerTest
,代码如下。
// 1:加载模块
Module module = moduleLoader.load(buildModuleConfig());
//2:注册模块
ModuleManager moduleManager = new ModuleManagerImpl();
moduleManager.register(module);
3:开发模块
在模块中只需要实现并开发 Action,代码如下:
public class HelloWorldAction implements Action<ModuleConfig, ModuleConfig> {
@Override
public ModuleConfig execute(ModuleConfig actionRequest) {
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setName(actionRequest.getName());
moduleConfig.setEnabled(actionRequest.getEnabled());
moduleConfig.setVersion(actionRequest.getVersion());
moduleConfig.setModuleUrl(actionRequest.getModuleUrl());
moduleConfig.setProperties(actionRequest.getProperties());
moduleConfig.setOverridePackages(actionRequest.getOverridePackages());
return moduleConfig;
}
@Override
public String getActionName() {
return "helloworld";
}
}
5:调用接口
开发者需要利用 JarsLink API 把请求转发给模块,先根据模块名查找模块,再根据 aciton name 查找 Action,最后执行 Action。
//查找模块
Module findModule = moduleManager.find(module.getName());
Assert.assertNotNull(findModule);
//查找和执行Action
String actionName = "helloworld";
ModuleConfig moduleConfig = new ModuleConfig();
moduleConfig.setName("h");
moduleConfig.setEnabled(true);
ModuleConfig result = findModule.doAction(actionName, moduleConfig);
6:卸载模块
卸载模块调用 remove 方法,该方法会返回卸载的模块。
Module remove = moduleManager.remove(module.getName());
7:更新模块
更新模块需要更新 JAR 包和版本号,并重新注册模块。
ModuleConfig moduleConfig = new ModuleConfig();
URL demoModule=Thread.currentThread().getContextClassLoader().getResource("a-1.1.jar");
moduleConfig.setModuleUrl(ImmutableList.of(demoModule));
moduleConfig.setVersion(“1.1”);
Module module = moduleLoader.load(moduleConfig)
Module removedModule = moduleManager.register(module);
其他特性
Spring 配置
通过 moduleConfig 的 Properties 属性可以设置 Spring bean 变量的配置信息。
1:定义变量
<bean class="com.alipay.XX.UserService">
<property name="url" value="${url}" />
</bean>
2:配置变量信息
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("url", "127.0.0.1");
moduleConfig.setProperties(properties);
3:排除 spring 配置文件
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("exclusion_confige_name", "text.xml");
moduleConfig.setProperties(properties);
排除多个文件用逗号分隔。
最佳实践
HTTP 请求转发
可以把 HTTP 请求转发给模块处理。
private ModuleManager moduleManager;
@RequestMapping(value = "module/{moduleName}/{actionName}/process.json", method = { RequestMethod.GET,RequestMethod.POST })
public Object process(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> pathVariables = resolvePathVariables(request);
String moduleName = pathVariables.get("moduleName").toUpperCase()
String actionName = pathVariables.get("actionName").toUpperCase()
String actionRequest = XXX;
return moduleManager.doAction(moduleName,
actionName, actionRequest);
}
private Map<String, String> resolvePathVariables(HttpServletRequest request) {
return (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
}
消息请求转发
可以把消息转发给模块进行处理。遵循默认大于配置的方式,你可以把TOPIC当做模块名,EventCode当做ActionName来转发请求。
如何发布模块
有两个方案:
方案1 拉模式
- 1:在本地编译代码打包成JAR包。
- 2:把JAR上传到一个远程文件服务器,比如阿里云的OSS。
- 3:应用从远程服务器下载JAR,可以把配置信息存放在JAR里,比如版本号,JAR名。
方案2 推模式
- 1:在本地编译代码打包成JAR包。
- 2:把JAR直接SCP到服务器上的某个目录。
- 3:应用检查服务指定目录的JAR是否有更新,如果有更新就进行加载。
接口说明
JarsLink 框架最重要的两个接口是 ModuleManager和ModuleLoader。
ModuleManager 接口
ModuleManager 负责注册,卸载,查找模块和执行 Action。
import java.util.List;
import java.util.Map;
/**
* 模块管理者, 提供注册,移除和查找模块能力
*
* @author tengfei.fangtf
* @version $Id: ModuleManager.java, v 0.1 2017年05月30日 2:55 PM tengfei.fangtf Exp $
*/
public interface ModuleManager {
/**
* 根据模块名查找Module
* @param name
* @return
*/
Module find(String name);
/**
* 获取所有已加载的Module
*
* @return
*/
List<Module> getModules();
/**
* 注册一个Module
*
* @param module 模块
* @return 新模块
*/
Module register(Module module);
/**
* 移除一个Module
*
* @param name 模块名
* @return 被移除的模块
*/
Module remove(String name);
/**
* 获取发布失败的模块异常信息
*
* @return
*/
Map<String, String> getErrorModuleContext();
}
ModuleLoader 接口
ModuleLoader 只负责加载模块。
public interface ModuleLoader {
/**
* 根据配置加载一个模块,创建一个新的ClassLoadr加载jar里的class
*
* @param moduleConfig 模块配置信息
*
* @return 加载成功的模块
*/
Module load(ModuleConfig moduleConfig);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论