-
Notifications
You must be signed in to change notification settings - Fork 705
如何使用
<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>
在系统中引入以下两个BEAN。
<!-- 模块加载引擎 -->
<bean name="moduleLoader" class="com.alipay.jarslink.api.impl.ModuleLoaderImpl"></bean>
<!-- 模块管理器 -->
<bean name="moduleManager" class="com.alipay.jarslink.api.impl.ModuleManagerImpl"></bean>
使用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 id="moduleRefreshScheduler"
class="com.alipay.**.ModuleRefreshSchedulerImpl">
<property name="moduleManager" ref="moduleManager" />
<property name="moduleLoader" ref="moduleLoader" />
</bean>
JarsLink API 暂时不提供模块可视化管理能力,所以需要使用其他系统来管理和发布模块。目前可以通过com.alipay. jarslink.api.ModuleManager#getModules
获取运行时所有模块的信息。
你也可以使用API来加载并注册模块,详细使用方式可以参考ModuleManagerTest
,代码如下。
//1:加载模块
Module module = moduleLoader.load(buildModuleConfig());
//2:注册模块
ModuleManager moduleManager = new ModuleManagerImpl();
moduleManager.register(module);
在模块中只需要实现并开发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";
}
}
开发者需要利用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);
卸载模块调用remove方法,该方法会返回卸载的模块。
Module remove = moduleManager.remove(module.getName());
更新模块需要更新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);
通过moduleConfig的Properties属性可以设置Spring bean变量的配置信息。
1:定义变量
<bean id="userService" 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请求转发给模块处理。
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负责注册,卸载,查找模块和执行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只负责加载模块。
public interface ModuleLoader {
/**
* 根据配置加载一个模块,创建一个新的ClassLoadr加载jar里的class
*
* @param moduleConfig 模块配置信息
*
* @return 加载成功的模块
*/
Module load(ModuleConfig moduleConfig);
}