WatchService是jdk7之后nio包中的新功能。可以看作是文件监控器,通过操作系统原生文件系统来运行。 针对单点多appkey的情况,可以注册开启多个监控器。 每个监控器可看作是后台线程,通过监控文件发出的信号来实现监控。
1、感知系统配置文件的变化,修改配置文件内容即时生效,无需重启服务器 2、监控磁盘中的文件变化
用watchservice修改配置文件方式仅适合于比较小的项目,例如只有一两台服务器,而且配置文件是可以直接修改的。例如 Spring mvc 以 war 包的形式部署,可以直接修改resources 中的配置文件。如果是 Spring boot 项目,还想用这种方式的话,就要引用一个外部可以编辑的文件,比如一个固定的目录,因为 spring boot 大多数以 jar 包部署,打到包里的配置文件没办法直接修改。如果是比较大的项目,最好还是用配置中心,例如携程的 Apollo、Consul 等。
1、WatchService 实例化
watchService = FileSystems.getDefault().newWatchService();
2、使用 Path 来指定要监控的目录
String filePath = new ClassPathResource(configName).getFile().getParent();
Path path = Paths.get(filePath);
3、将 Path 注册到 WatchService
使用Path.register() 方法注册要监控指定目录的那些事件(创建、修改、删除)
StandardWatchEventKinds.ENTRYCREATE //创建 StandardWatchEventKinds.ENTRYMODIFY //修改 StandardWatchEventKinds.ENTRY_DELETE //删除
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);
4、创建监听配置文件守护线程
使用WatchService监听配置文件所在目录内容的变化,包括修改、删除事件。通过后台线程实现阻塞等待内容变化事件,一旦发现有变更,则重新装载配置文件
核心代码块:
private void pollingMonitor() {
while (true) {
try {
// 尝试获取监控池的变化,如果没有则一直等待
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
if (PropsUtil.CONFIG_NAME.equals(event.context().toString())) {
logger.info("监控到{}发生{}操作,将重新加载", event.context(), event.kind());
PropsUtil.getInstance().loadProperties(event.context().toString());
break;
}
}
// 完成一次监控就需要重置监控器一次
watchKey.reset();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5、注册关闭钩子,当JVM停止时关闭WatchService。
public class WatcherHookThead implements Runnable {
private static Logger logger = LoggerFactory.getLogger(WatcherHookThead.class);
private WatchService watchService;
public WatcherHookThead(WatchService watchService) {
super();
this.watchService = watchService;
}
@Override
public void run() {
try {
watchService.close();
logger.info("watcher close success...");
} catch (IOException e) {
logger.error("watcher close fail:" + e.getMessage(), e);
}
}
}
public void shutDownWatcher() {
Thread shutDownThread = new Thread(new WatcherHookThead(watchService));
shutDownThread.setName("shutDownThread");
Runtime.getRuntime().addShutdownHook(shutDownThread);
}
1、测试代码
public void testValueIfChange() {
while (true) {
String value = PropsUtil.getInstance().getValue("username");
try {
System.out.println(StringUtil.format("value--->{}", value));
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
初始内容 username=helloworld
不停服务,将username修改为test,观察控制台打印value的值是否动态生效
value--->helloworld
2018-06-29 14:57:57.132 INFO 11988 --- [ watcherThread] com.demo.watchserver.util.WatcherThread : 监控到application.properties发生ENTRY_MODIFY操作,将重新加载
value--->test
通过控制台可以说明,value已经动态修改,说明配置文件即时被加载
https://github.com/lyb-geek/first-watchserver