前言
前些天在分析性能问题的时候,突然发现一个文件同步的方法严重阻塞了线程。查看代码才发现,每次同步的时候都会全量的获取当前的所有文件。在文件数量较大的场景下,给系统带来的非常严重的阻塞。
找到问题原因就好办了,全量获取文件不是一个长期且行之有效的方式。因此,需要另一种方案来替代全量查询。
WatchService是什么?
WatchService是jdk1.7版本引进的,位于nio包下。
WatchService看作是文件监控器,通过操作系统原生文件系统来运行。
针对单点多appkey的情况,可以注册开启多个监控器。
每个监控器可看作是后台线程,通过监控文件发出的信号来实现监控。
核心方法介绍
WatchService作为文件监控器,支持阻塞和非阻塞两种模式。对应两种模式的方法如下
阻塞(BIO)
WatchKey take():获取下一次的监听结果,没有则一直等待;
非阻塞(NIO)
WatchKey poll():获取下一次监听结果,没有则返回null;
WatchKey poll(long timeout, TimeUnit unit):获取下一次监听结果,最多等待指定时间,没有则返回null;
监听事件
WatchService监听事件宝库及事件丢失,创建,修改,重命名等
StandardWatchEventKinds.OVERFLOW
StandardWatchEventKinds.ENTRY_CREATE
StandardWatchEventKinds.ENTRY_MODIFY
StandardWatchEventKinds.ENTRY_DELETE
WatchService使用
@Component
public class FileWatchDog implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(FileWatchDog.class);
@Override
public void run(String... args) throws Exception {
System.out.println("start to watch files changes");
//实例化WatchService对象
WatchService watchService = FileSystems.getDefault().newWatchService();
String url = "C:\\data";
//构建Path对象
Path path = Paths.get(url);
//注册监听事件
path.register(watchService,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE);
//新建定时任务线程池,仅作为示例使用,实际项目请按标准使用。
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
ScheduledFuture<?> schedule = threadPool.scheduleAtFixedRate(
() -> {
Thread.currentThread().setName("watch-dog-"+Thread.currentThread().getId());
//获取监听结果,没有返回null
WatchKey key = watchService.poll();
if (null == key) {
logger.info("暂无文件变化");
return;
}
//利用 key.pollEvents() 方法返回一系列的事件列表
for (WatchEvent<?> event : key.pollEvents()) {
//得到 监听的事件类型
WatchEvent.Kind kind = event.kind();
if (kind.name().equals(StandardWatchEventKinds.ENTRY_CREATE.name())) {
//业务逻辑代码
System.out.println("监控到文件新增,开始执行同步");
}
//得到 监听的文件/目录的路径
Path pathName = (Path) event.context();
logger.info(kind.name() + pathName);
//每次的到新的事件后,需要重置监听池
key.reset();
}
}, 1000, 10000, TimeUnit.MILLISECONDS
);
}
}