之前一篇文章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();
}