使用JDK动态加载jar包和JavaBean反射机制实现基于接口代理的RPC分布式服务调用

分布式服务框架概述:

服务协调中间件:Zookeeper 负责服务注册、发现、通知。

服务提供方:

服务启动 使用 Spring  ApplicationListener 接口 当容器中bean 完成加载时,变量容器中的bean 将RPC bean 发布到Zookeeper ,并保存到内存。

服务监听 使用netty 启动远程服务请求监听端口,接收到远程服务请求数据 通过反射方式调用本地服务。

服务消费方:

服务发现  通过主动拉取Zookeeper 数据和watch 监听服务变更。

服务调用 本地生成远程接口代理,代理接口将请求数据打包通过netty 发送到服务提供方监听端口。

服务消费方 基于以上流程实现远程服务调用,本地代理接口向服务提供方发生请求数据时包含的三元组是 服务编码(唯一标识一个服务bean)、方法名称、参数类型(唯一标识一个javabean 方法)。这就需要在消费方引入服务接口jar包。

通常我们是在工程的POM 文件中添加maven 坐标的方式依赖远程服务的接口API。但是这种方式不易扩展,业务变更需要调用新接口时需要通过编写新代码重新发布系统。

于是便设计一套基于JDK动态加载jar包和JavaBean反射机制的服务调用方式,方案图解:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21ybGljaGVuZ3lp,size_16,color_FFFFFF,t_70

 

主要实现类:

public class RMURLJarLoader extends URLClassLoader {

    Logger logger = LoggerFactory.getLogger(RMURLJarLoader.class);

    // 缓存jar数据.

    private JarURLConnection cacheJar = null;

    public RMURLJarLoader() {

        super(new URL[] {});

    }

    /**

     * 将指定的文件url添加到类加载器的classpath中去,并缓存jar connection,方便以后卸载jar

     * 一个可想类加载器的classpath中添加的文件url

     *

     * @param

     */

    public void addURLJar(URL file) {

        try {

            // 打开并缓存文件url连接

            URLConnection uc = file.openConnection();

            if (uc instanceof JarURLConnection) {

                uc.setUseCaches(true);

                ((JarURLConnection) uc).getManifest();

                cacheJar = (JarURLConnection) uc;

            }

        } catch (Exception e) {

            System.err.println("Failed to cache plugin JAR file: " + file.toExternalForm());

            logger.error("Failed to cache plugin JAR file: " + file.toExternalForm());

        }

        try {

            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();

            Method add = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });

            add.setAccessible(true);

            add.invoke(classLoader, file);

        } catch (Exception exp) {

            logger.error("Failed to cache plugin JAR file: " + file.toExternalForm());

        }

    }

    public void unloadJarFile() {

        if (cacheJar == null) {

            return;

        }

        try {

            System.err.println("Unloading plugin JAR file " + cacheJar.getJarFile().getName());

            cacheJar.getJarFile().close();

            cacheJar = null;

        } catch (Exception e) {

            System.err.println("Failed to unload JAR file\n" + e);

            logger.error("Failed to unload JAR file\n" + e);

        } finally {

        }

    }

}
public class RMURLClassLoader implements DisposableBean {

    private final static Logger logger = LoggerFactory.getLogger(RMURLJarLoader.class);

    private final static ConcurrentHashMap<String, RMURLJarLoader> LOCAL_CACHE = new ConcurrentHashMap<>();

    private RMURLJarLoader loadJar(String jarPath) {

        RMURLJarLoader jar = LOCAL_CACHE.get(jarPath);

        if (jar != null) {

            return jar;

        }

        jar = new RMURLJarLoader();

        try {

            URI uri = new URI(jarPath);

            jar.addURLJar(uri.toURL());

            LOCAL_CACHE.put(jarPath, jar);

        } catch (URISyntaxException e) {

            logger.error("Laod jar:" + jarPath + " exception!");

        } catch (MalformedURLException e) {

            logger.error("Laod jar:" + jarPath + " exception!");

        }

        return jar;

    }

    public Class loadClass(String jarPath, String fullClassName) {

        if (StringUtils.isNullOrEmpty(jarPath) || StringUtils.isNullOrEmpty(fullClassName)) {

            return null;

        }

        RMURLJarLoader jar = LOCAL_CACHE.get(jarPath);

        if (jar == null) {

            jar = loadJar(jarPath);

        }

        try {

            return jar.loadClass(fullClassName);

        } catch (ClassNotFoundException e) {

            logger.error("Laod jar:" + jarPath + "class :" + fullClassName + " exception!");

        }

        return null;

    }

    public void unloadJarFile(String jarPath) {

        RMURLJarLoader jar = LOCAL_CACHE.get(jarPath);

        if (jar == null) {

            return;

        }

        jar.unloadJarFile();

        jar = null;

        LOCAL_CACHE.remove(jarPath);

    }

/**

 * Invoked by a BeanFactory on destruction of a singleton.

 *

 * @throws Exception in case of shutdown errors.

 * Exceptions will get logged but not rethrown to allow

 * other beans to release their resources too.

 */

@Override

public void destroy() throws Exception {

    for (RMURLJarLoader jar : LOCAL_CACHE.values()) {

        jar.unloadJarFile();

    }

}

}

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值