Skip to content

如何使用

清英 edited this page May 9, 2018 · 10 revisions

1:引入POM

JarsLink Maven Repo

<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 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);

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 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请求转发

可以把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);

}