场景
配置文件动态生效的场景本身是特别多的
- 普罗米修斯的增添抓取实例,他是有filesd的。你只要把新的实例写入到文件中,再下次读取配置文件的时候,就可以生效了。他是通过定时访问配置文件做到的,可以通过配置访问间隔来控制频率。
- log4j的配置如果想不重启生效,我们一般会使用他默认带的加载方式,这样会定时的去访问path,看看是否是更新了。
PropertyConfigurator.configureAndWatch(path,1000);
上面举了几个例子,实现都是通过定时访问的方式实现的。这种实现比较好理解,定时看文件的最后更改时间,如果时间变了,就重新读取一次文件,否则的话则继续等一段时间。
问题
说了这种实现,用过的都有一点问题,就是需要根据具体的场景来设置参数,因为定时的去访问,如果文件没有修改,就是一次无效的执行。代码白跑了一次。例如设置为每2秒跑一次,1小时都不改一次,这都是无效的运行。所以时间应该设置长一点,例如2分钟。但是当修改了以后,最糟糕,得2分钟以后看到效果。这里就出现了资源和生效时间的一个平衡。
java7特性
解决上面平衡问题的一个方式,就是利用java7的目录的事件来替换定时访问的逻辑。
我们先看看支持多少种事件。
/**
* A special event to indicate that events may have been lost or
* discarded.
*
*/
public static final WatchEvent.Kind<Object> OVERFLOW =
new StdWatchEventKind<Object>("OVERFLOW", Object.class);
/**
* Directory entry created.
*
*/
public static final WatchEvent.Kind<Path> ENTRY_CREATE =
new StdWatchEventKind<Path>("ENTRY_CREATE", Path.class);
/**
* Directory entry deleted.
*
*/
public static final WatchEvent.Kind<Path> ENTRY_DELETE =
new StdWatchEventKind<Path>("ENTRY_DELETE", Path.class);
/**
* Directory entry modified.
*
*/
public static final WatchEvent.Kind<Path> ENTRY_MODIFY =
new StdWatchEventKind<Path>("ENTRY_MODIFY", Path.class);
其实就是增,删,改,丢失。这些事件都是针对目录的,如果你要监控/data/test.txt。那我们就可以给/data注册监控事件,以此看到test.txt的改变。
知道了事件,我们的使用方式就很套路了。
//获取文件系统的WatchService
WatchService watcher = FileSystems.getDefault().newWatchService();
//给dir(Path)注册一个监控的事件,事件如上
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
for (;;) {
// 等待触发的事件
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
//下面是根据事件的业务逻辑
……
}
// 重置
boolean valid = key.reset();
}
上面的代码非常接近nio的socket,都是获取等待事件的通知。
套路化
上面的代码流程非常的套路,我们可以稍微提取一些功能,简化代码。
final DirWatcher dw = new DirWatcher(true);
dw.registerAllEvents(Paths.get("c:/hello")).addHandler(new WatcherResultHandler() {
public void handleResult(Path path, Kind<?> event) {
...
}
}).processEvents();
我们只要关心注册的目录,然后再WatcherResultHandler里写处理事件的逻辑即可。
https://github.com/xpbob/commonio
具体的代码如上,欢迎交流。
注意事项
- 这个功能是java7里的,所以有jdk的限制
- 针对文件增的事件。增加的事件是一个比较麻烦的事情,尤其是对网络传输的过程,如果传到一般断了,我们也会受到增加的事件,一般的文件的网络传输都是RandomAccessFile的应用,因为可以把文件分开传递,或者支持从特定位置继续写。这样复杂的逻辑,接收完以后,文件事件处理。需要在事件的处理上做很多的逻辑,例如可以使用中间文件,xx.tmp等等。不处理.tmp的文件,直到合成一个完整的文件为止。加md5文件,当读取到md5文件的时候去检查传输的文件。