java.nio.file.WatchService 实时监控文件变化的示例代码

网友投稿 577 2022-07-27


目录1.示例代码2.其实并没有实时

在平时的开发过程中,会有很多场景需要实时监听文件的变化,如下:1、通过实时监控 mysql 的 binlog 日志实现数据同步2、修改配置文件后,希望系统可以实时感知3、应用系统将日志写入文件中,日志监控系统可以实时抓取日志,分析日志内容并进行报警4、类似 ide 工具,可以实时感知管理的工程下的文件变更

在 java 语言中,从 JDK7 开始,新增了java.nio.file.WatchService类,用来实时监控文件的变化。

1.示例代码

FileWatchedService 类:

package org.learn.file;

import java.io.IOException;

import java.nio.file.FileSystems;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.nio.file.StandardWatchEventKinds;

import java.nio.file.WatchEvent;

import java.nio.file.WatchKey;

import java.nio.file.WatchService;

import java.util.List;

/**

* 实时监控文件的变化

*

* @author zhibo

* @date 2019-07-30 20:37

*/

public class FileWatchedService {

private WatchService watchService;

private FileWatchedListener listener;

/**

*

* @param path 要监听的目录,注意该 Path 只能是目录,否则会报错 java.nio.file.NotDirectoryException: /Users/zhibo/logs/a.log

* @param listener 自定义的 listener,用来处理监听到的创建、修改、删除事件

* @throws IOException

*/

public FileWatchedService(Path path, FileWatchedListener listener) throws IOException {

watchService = FileSystems.getDefault().newWatchService();

path.register(watchService,

/// 监听文件创建事件

StandardWatchEventKinds.ENTRY_CREATE,

/// 监听文件删除事件

StandardWatchEventKinds.ENTRY_DELETE,

/// 监听文件修改事件

StandardWatchEventKinds.ENTRY_MODIFY);

//

// path.register(watchService,

// new WatchEvent.Kind[]{

// StandardWatchEventKinds.ENTRY_MODIFY,

// StandardWatchEventKinds.ENTRY_CREATE,

// StandardWatchEventKinds.ENTRY_DELETE

// },

// SensitivityWatchEventModifier.HIGH);

this.listener = listener;

}

private void watch() throws InterruptedException {

while (true) {

WatchKey watchKey = watchService.take();

List> watchEventList = watchKey.pollEvents();

for (WatchEvent> watchEvent : watchEventList) {

WatchEvent.Kind kind = watchEvent.kind();

WatchEvent curEvent = (WatchEvent) watchEvent;

if (kind =MbVXKzUerS= StandardWatchEventKinds.OVERFLOW) {

listener.onOverflowed(curEvent);

continue;

} else if (kind == StandardWatchEventKinds.ENTRY_CREATE) {

listener.onCreated(curEvent);

continue;

} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {

listener.onModified(curEvent);

continue;

} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {

listener.onDeleted(curEvent);

continue;

}

}

/**

* WatchKey 有两个状态:

* {@link sun.nio.fs.AbstractWatchKey.State.READY ready} 就绪状态:表示可以监听事件

* {@link sun.nio.fs.AbstractWatchKey.State.SIGNALLED signalled} 有信息状态:表示已经监听到事件,不可以接续监听事件

* 每次处理完事件后,必须调用 reset 方法重置 watchKey 的状态为 ready,否则 watchKey 无法继续监听事件

*/

if (!watchKey.reset()) {

break;

}

}

}

public static void main(String[] args) {

try {

Path path = Paths.get("/Users/zhibo/logs/");

FileWatchedService fileWatchedService = new FileWatchedService(path, new FileWatchedAdapter());

fileWatchedService.watch();

} catch (IOException e) {

http:// e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

FileWatchedListener 类:

package org.learn.file;

import java.nio.file.Path;

import java.nio.file.WatchEvent;

public interface FileWatchedListener {

void onCreated(WatchEvent watchEvent);

void onDeleted(WatchEvent watchEvent);

void onModified(WatchEvent watchEvent);

void onOverflowed(WatchEvent watchEvent);

}

FileWatchedAdapter 类:

package org.learn.file;

import java.nio.file.Path;

import java.nio.file.WatchEvent;

import java.text.DateFormat;

import java.text.SimpleDateFormat;

import java.util.Calendar;

/**

* 文件监听适配器

*

* @author zhibo

* @date 2019-07-31 11:07

*/

public class FileWatchedAdapter implements FileWatchedListener {

@Override

public void onCreated(WatchEvent watchEvent) {

Path fileName = watchEvent.context();

System.out.println(String.format("文件【%s】被创建,时间:%s", fileName, now()));

}

@Override

public void onDeleted(WatchEvent watchEvent) {

Path fileName = watchEvent.context();

System.out.println(String.format("文件【%s】被删除,时间:%s", fileName, now()));

}

@Override

public void onModified(WatchEvent watchEvent) {

Path fileName = watchEvent.context();

System.out.println(String.format("文件【%s】被修改,时间:%s", fileName, now()));

}

@Override

public void onOverflowed(WatchEvent watchEvent) {

Path fileName = watchEvent.context();

System.out.println(String.format("文件【%s】被丢弃,时间:%s", fileName, now()));

}

private String now(){

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");

return dateFormat.format(Calendar.getInstance().getTime());

}

}

执行以上代码,启动监控任务,然后我在/Users/zhibo/logs/目录中创建、修改、删除文件,命令如下:

应用程序感知到文件变化,打印日志如下:

2.其实并没有实时

大家可以看到,监控任务基本上是以 10 秒为单位进行日志打印的,也就是说修改一个文件,WatchService 10秒之后才能感知到文件的变化,没有想象中的那么实时。根据以上的经验,推测可能是 WatchService 做了定时的操作,时间间隔为 10 秒。通过翻阅源代码发现,在 PollingWatchService 中确实存在一个固定时间间隔的调度器,如下图:

该调度器的时间间隔有 SensitivityWatchEventModifier 进行控制,该类提供了 3 个级别的时间间隔,分别为2秒、10秒、30秒,默认值为 10秒。SensitivityWatchEventModifier 源码如下:

package com.sun.nio.file;

import java.nio.file.WatchEvent.Modifier;

public enum SensitivityWatchEventModifier implements Modifier {

HIGH(2),

MEDIUM(10),

LOW(30);

private final int sensitivity;

public int sensitivityValueInSeconds() {

return this.sensitivity;

}

private SensitivityWatchEventModifier(int var3) {

this.sensitivity = var3;

}

}

通过改变时间间隔来进行验证,将

path.register(watchService,

/// 监听文件创建事件

StandardWatchEventKinds.ENTRY_CREATE,

/// 监听文件删除事件

StandardWatchEventKinds.ENTRY_DELETE,

/// 监听文件修改事件

StandardWatchEventKinds.ENTRY_MODIFY);

修改为:

path.register(watchService,

new WatchEvent.Kind[]{

StandardWatchEventKinds.ENTRY_MODIFY,

StandardWatchEventKinds.ENTRY_CREATE,

StandardWatchEventKinds.ENTRY_DELETE

},

SensitivityWatchEventModifier.HIGH);

查看日志,发现正如我们的推断,WatchService 正以每 2 秒的时间间隔感知文件变化。在 stackoverflow 中也有人提出了该问题,问题:Is Java 7 WatchService Slow for Anyone Else,我的 mac 系统中确实存在该问题,由于手头没有 windows、linux 系统,因此无法进行这两个系统的验证。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring操作JdbcTemplate数据库的方法学习
下一篇:spring学习JdbcTemplate数据库事务管理(springjdbc事务控制)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~