在Java中监控日志文件变化的3种方法
Java文件的操作
在研究规则引擎时,如果规则是以文件的形式存储的,就需要对指定的目录或文件进行监控,感知规则是否发生了变化,然后再加载。
当然,在其他业务场景中,如动态加载配置文件、监控日志文件、监控FTP文件变化等,也会遇到类似的情况。
今天我将分享三种解决方案。
方案一:计划任务+文件lastModified
这个方案是我想到的最简单、最直接的方案。通过定时任务,轮询查询文件的最后修改时间,并与上次进行比较。
如果有变化,说明文件被修改了,需要重新加载或用相应的业务逻辑处理。
在之前的文章《挖掘JDK监控文件中的错误》中,已经写了一个具体的例子,也提出了它的不足之处。
下面是一些示例代码。
对于文件变化频率较低的场景,这个方案实现起来很简单,基本上可以满足要求。但是,如前文所述,需要注意Java 8和Java 9中File#lastModified 的错误。
但是,如果这个方案用于文件目录的改变,其缺点就有些明显了。比如,操作频繁,在遍历、保存状态、比较状态等方面失去了效率,操作系统的功能也无法得到充分的发挥。
方案2:WatchService
在Java 7中,增加了java.nio.file.WatchService ,可以实现对文件变化的监控。
WatchService 是一个基于操作系统的文件系统监视器。它可以监测系统中所有文件的变化,不需要遍历或比较。它是基于信号发送和接收的监控,具有很高的效率。
上面的演示展示了WatchService 的基本用法,注释部分也解释了每个API的具体作用。
WatchService 所监控的文件类型也变得更加丰富。
ENTRY_CREATE目标被创建。ENTRY_DELETE目标删除。ENTRY_MODIFY目标修改。OVERFLOW一个特殊的事件,表明该事件被放弃或丢失。
如果你看一下WatchService 实现类的源代码(PollingWatchService),你会发现它基本上是启动一个独立的线程来监控文件变化。
也就是说,我们需要手动实现的部分也是由WatchService 内部为我们完成的。
如果你在验证的时候写一个demo,你会明显感觉到WatchService 监控文件的变化不是实时的,有时需要几秒钟的时间来监控文件的变化。
以实现类PollingWatchService 为例,查看源代码,你可以看到以下代码。
也就是说,监听器是由调度器根据固定的时间间隔来控制的,这个时间间隔是在SensitivityWatchEventModifier 类中定义的。
这个类提供了三个级别的时间间隔,2秒、10秒和30秒,默认值是10秒。
这个时间间隔可以在path#register 中传递。
与方案1相比,它实现起来很简单,效率也很高。
缺点也很明显。只有当前目录下的文件和目录可以被监控,子目录不能被监控。
而且我们还看到,监控只能被看作是准实时的,监控时间只能采取API默认提供的三个值。
API在Stack Overflow上也提出了Java 7在Mac OS,甚至Windows和Linux系统下有延迟的问题。
我没有验证过其他操作系统。如果你遇到类似的问题,你可以自己去搜索和解决。
方案3:Apache Commons-IO
这里我们需要使用一个开源的框架来实现,也就是几乎每个项目都会引入的commons-io 类库。
引入相应的依赖项。
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.7</version></dependency>
注意不同的版本需要不同的JDK支持,2.7需要Java 8及以上版本。
文件监控的实现commons-io ,位于org.apache.commons.io.monitor 包中。基本的使用过程如下。
- 定制文件监听器类并继承
FileAlterationListenerAdaptor,以处理文件和目录的创建、修改和删除。 - 定制一个文件监控类,通过指定一个目录来创建一个观察者
FileAlterationObserver。 - 在监控中添加一个文件系统观察者,并添加一个文件监听器。
- 调用并执行。
第一步:创建一个文件监听器。根据需要在不同的方法中实现相应的业务逻辑处理。
第2步:封装一个用于文件监控的工具类。
核心是创建一个观察者FileAlterationObserver ,封装文件路径Path和监听器FileAlterationListener ,然后将其交给FileAlterationMonitor 。
第三步:调用并执行。
执行该程序,你会发现每隔1秒就会输入一次日志。当文件被改变时,相应的日志也会被打印出来。
onStartmodify:/xxx/xxx/temp/1.txtonStoponStartonStop
当然,相应的监听时间间隔可以在创建FileMonitor 时修改。
在这个方案中,监听器本身会启动一个线程进行计时处理。在每次运行中,将首先调用事件监听器处理类的onStart 方法,然后检查是否有变化,并调用相应事件的方法。
例如,如果onChange 文件的内容发生了变化,在检查之后,调用onStop 方法,释放当前线程所占用的CPU资源,等待下一个时间区间的唤醒,再次运行。
监听器是以文件目录为根基的,还可以设置过滤器来监控相应的文件变化。
过滤器的设置可以在FileAlterationObserver 的构造函数中查看。
到目前为止,已经介绍了基于Java的三种监控文件变化的方案。
感谢你阅读这篇文章。
请继续关注更多内容。