Java模块 -- jar包热部署/热卸载

之前一篇文章Java模块 -- WatchService监听服务 实现了监听目录的功能。

后来我寻摸着,可以根据这个功能,实现另一个功能:动态加载jar包和卸载jar包。


我来详细说说我这个功能的需求,顺便说说我经手的服务框架:

之前在工作中,主要是开发服务层的框架任务中心框架、ServiceCenter发布中心框架等。

这些框架是干嘛的呢?

举个现实的的需求啊...

我现在要和好几家公司做服务对接,每个提供的查询服务是不同的。

按照正常的思路,我们需要写好几个个服务端,提供好几种不同的查询服务,至于提供Http还是WebSerice服务这个另说。

你想想 后面如果还有其他公司要做服务对接,我是不是还要再写服务?

是不是 很麻烦?


有人提出,我可以写一个大的服务端,统一入口,在服务端中,通过不同的查询类型,通过代码if...else...if...else...来判断它想要调用哪个查询服务。

这个主意不错,不过这种方式灵活嘛?能只支持动态扩展嘛?

你想,如果要新增一个查询类型,我是不是要改动服务端的代码?要增加一个if判断语句?


为了实现灵活和动态扩展的要求,我们可以将业务代码从服务端代码中剥离出来。


专门写一个框架,这个框架的作用就是对外发布服务(HTTP、WebService),客户端查询鉴权、资源隔离等功能。

具体的查询业务逻辑,使用SDK应用模块的方式集成到服务框架中,这样子,框架就是框架,模块就是模块,新增业务模块,无需改动框架代码。

实现 框架与业务模块的分离。


如果要新对接一个公司,提供某种查询,我们就不用重新开发一个服务,仅需要编写查询业务的代码,然后将jar包放到框架目录中,框架自动加载,实现灵活配置,动态扩展。


你看啊...每次我新增一个业务jar包,都需要重新框架程序,是不是很麻烦...

如此,我就想,是不是需要一个动态加载和卸载的功能。


关于卸载,关于jar包卸载,我们并不能真正的从JVM中卸载JVM...

我使用的是"假卸载",我将加载的 业务主类放在放在集合中,每次需要的时候,从集合中取出,并实例化...

卸载的时候,我将集合中的类,删除即可....

实现"假卸载"...

加载类的问题...

由于我这里采用的是Java注解的方式,使用自定义的ClassLoader会出现问题...

这个后面还需要仔细测测...

这里参考的是:

http://www.cnblogs.com/cm4j/p/hot_deploy.html


下面是核心代码:

// ---------------------------热部署--------------------------------------

static {
	// 当前进程pid
	String name = ManagementFactory.getRuntimeMXBean().getName();
	pid = name.split("@")[0];
	logger.info("当前进程pid:" + pid);
}

/**
 * 动态监控modules目录,sdk应用热部署
 * 
 * @throws Exception
 */
private void watchModulesDirWithHotDeployment() throws Exception {

	String modulesDirPath = new File(ServiceCenterConstant.SERVICECENTER_MODULE_DIR_PATH).getAbsolutePath();

	try {

		// 获取文件系统的WatchService对象
		modulesDirHotDeploymentWatchService = FileSystems.getDefault().newWatchService();

		// 监听'filePath'下是否有新建的目录;register()方法后面监听事件种类还可以增加。
		Paths.get(modulesDirPath).register(modulesDirHotDeploymentWatchService,
				StandardWatchEventKinds.ENTRY_CREATE);

	} catch (Exception e) {
		logger.error(e.getMessage(), e);
	}

	// 设置后台线程
	Thread watchThread = new Thread(new Runnable() {

		@Override
		public void run() {

			while (true) {

				try {

					// 获取下一个文件改动事件
					WatchKey key = modulesDirHotDeploymentWatchService.take();

					List moduleLibsPath = new ArrayList();
					for (WatchEvent event : key.pollEvents()) {

						/** 睡眠一会儿,防止目录移动慢、解压缩慢 */
						Thread.sleep(2000);

						String moduleName = event.context().toString();

						String moduleDirPath = modulesDirPath + "/" + moduleName;

						moduleLibsPath.addAll(findMouldLibs(new File(moduleDirPath + "/lib")));

						for (File moduleLib : moduleLibsPath) {

							// 虚拟机加载
							vm = VirtualMachine.attach(pid);
							vm.loadAgent(moduleLib.getAbsolutePath());

						}

						// 测试使用,模拟调用sdk
						Class clazz = Class.forName("com.sc.service.sample.SampleSDKQueryServiceModule");
						Object obj = clazz.newInstance();
						ServiceCenterBaseClass service = (ServiceCenterBaseClass) obj;
						service.loadLogger(logger);
						service.process("123");

						/** SDK应用模块注解解析 */
						getInformationBySDKAnnotation();

						/** 解析SDK应用模块下的配置文件 */
						analyModuleConfigFile(new File(moduleDirPath));

						/** 加载对应模块的线程池 */
						loadThreadPool(moduleName);

					}

					// 重设WatchKey
					boolean valid = key.reset();

					// 如果重设失败,退出监听
					if (!valid) {
						break;
					}

					logger.info("configs : " + LoadConfig.getInstance().toString());

					Thread.sleep(5000);

				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}

			}
		}

	});

	// 设置为后台守护线程
	watchThread.setDaemon(true);
	watchThread.start();

}


// -------------------------------热卸载-------------------------------

private void watchModuleDirWithUninstalling() throws Exception {

	String modulesDirPath = new File(ServiceCenterConstant.SERVICECENTER_MODULE_DIR_PATH).getAbsolutePath();

	try {

		// 获取文件系统的WatchService对象
		modulesDirUninstallWatchService = FileSystems.getDefault().newWatchService();

		// 监听'filePath'下是否有新建的目录;register()方法后面监听事件种类还可以增加。
		Paths.get(modulesDirPath).register(modulesDirUninstallWatchService, StandardWatchEventKinds.ENTRY_DELETE);

	} catch (Exception e) {
		logger.error(e.getMessage(), e);
	}

	// 设置后台线程
	Thread watchThread = new Thread(new Runnable() {

		@Override
		public void run() {

			while (true) {

				try {

					// 获取下一个文件改动事件
					WatchKey key = modulesDirUninstallWatchService.take();

					for (WatchEvent event : key.pollEvents()) {

						/** 被删除SDK的模块名、模块类型 */
						String moduleName = event.context().toString();

						// 测试使用,模拟调用sdk
						Class clazz = Class.forName("com.sc.service.sample.SampleSDKQueryServiceModule");
						Object obj = clazz.newInstance();
						ServiceCenterBaseClass service = (ServiceCenterBaseClass) obj;
						service.loadLogger(logger);
						service.process("123");

						logger.info("unloaded moduleName : " + moduleName + " is start...");

						String moduleType = moduleNameWithModuleType.get(moduleName);

						/** 删除内存中对应模块名、模块类型的配置 */
						/** 客户端再调用,就无法获取到对应的SDK-Class,以达到卸载的目的 */
						/** 同时,每次客户端调用完成之后,手动将ServiceCenterBaseClass对象置为null */
						moduleTypeWithClass.remove(moduleType);
						moduleTypeWithModuleName.remove(moduleType);
						moduleNameWithModuleType.remove(moduleName);
						moduleNameWithModulePoolParams.remove(moduleName);
						moduleNameWithModuleLog4jFilePath.remove(moduleName);
						moduleTypeWithModuleConfigFile.remove(moduleType);
						moduleNameWithModuleThreadPool.remove(moduleName);

						logger.info("unloaded moduleName : " + moduleName + " is end...");

					}

					// 重设WatchKey
					boolean valid = key.reset();

					// 如果重设失败,退出监听
					if (!valid) {
						break;
					}

					logger.info("configs : " + LoadConfig.getInstance().toString());

					Thread.sleep(2000);
					System.gc();

					Thread.sleep(5000);

				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}

			}
		}

	});

	// 设置为后台守护线程
	watchThread.setDaemon(true);
	watchThread.start();

}


你可能感兴趣的:(JAVA,模块)