
监听日志文件变化的三种方法,推荐第三种!
背景
在研究规则引擎时,如果规则以文件的形式存储,那么就需要监听指定的目录或文件来感知规则是否变化,进而进行加载。当然,在其他业务场景下,比如想实现配置文件的动态加载、日志文件的监听、FTP文件变动监听等都会遇到类似的场景。
本文给大家提供三种解决方案,并分析其中的利弊,建议收藏,以备不时之需。
方案一:定时任务 + File#lastModified
这个方案是最简单,最能直接想到的解决方案。通过定时任务,轮训查询文件的最后修改时间,与上一次进行对比。如果发生变化,则说明文件已经修改,进行重新加载或对应的业务逻辑处理。
在上篇文章《JDK的一个Bug,监听文件变更要小心了》中已经编写了具体的实例,并且也提出了其中的不足。
这里再把实例代码贴出来:
对于文件低频变动的场景,这种方案实现简单,基本上可以满足需求。不过像上篇文章中提到的那样,需要注意Java 8和Java 9中File#lastModified的Bug问题。
但该方案如果用在文件目录的变化上,缺点就有些明显了,比如:操作频繁,效率都损耗在遍历、保存状态、对比状态上了,无法充分利用OS的功能。
方案二:WatchService
在Java 7中新增了java.nio.file.WatchService,通过它可以实现文件变动的监听。WatchService是基于操作系统的文件系统监控器,可以监控系统所有文件的变化,无需遍历、无需比较,是一种基于信号收发的监控,效率高。
上述demo展示了WatchService的基本使用方式,注解部分也说明了每个API的具体作用。
通过WatchService监听文件的类型也变得更加丰富:
● ENTRY_CREATE 目标被创建
● ENTRY_DELETE 目标被删除
● ENTRY_MODIFY 目标被修改
● OVERFLOW 一个特殊的Event,表示Event被放弃或者丢失
如果查看WatchService实现类(PollingWatchService)的源码,会发现,本质上就是开启了一个独立的线程来监控文件的变化:
也就是说,本来需要我们手动实现的部分,也由WatchService内部帮我们完成了。
如果你编写一个demo,进行验证时,会很明显的感觉到WatchService监控文件的变化并不是实时的,有时候要等几秒才监听到文件的变化。以实现类PollingWatchService为例,查看源码,可以看到如下代码:
也就是说监听器由按照固定时间间隔的调度器来控制的,而这个时间间隔在SensitivityWatchEventModifier类中定义:
该类提供了3个级别的时间间隔,分别为2秒、10秒、30秒,默认值为10秒。这个时间间隔可以在path#register时进行传递:
相对于方案一,实现起来简单,效率高。不足的地方也很明显,只能监听当前目录下的文件和目录,不能监视子目录,而且我们也看到监听只能算是准实时的,而且监听时间只能取API默认提供的三个值。
该API在Stack Overflow上也有人提出Java 7在Mac OS下有延迟的问题,甚至涉及到Windows和Linux系统,笔者没有进行其他操作系统的验证,如果你遇到类似的问题,可参考对应的文章,寻求解决方案:https://blog.csdn.net/claram/article/details/97919664 。
方案三:Apache Commons-IO
方案一我们自己来实现,方案二借助于JDK的API来实现,方案三便是借助于开源的框架来实现,这就是几乎每个项目都会引入的commons-io类库。
引入相应依赖:
注意,不同的版本需要不同的JDK支持,2.7需要Java 8及以上版本。
commons-io对实现文件监听的实现位于org.apache.commons.io.monitor包下,基本使用流程如下:
● 自定义文件监听类并继承 FileAlterationListenerAdaptor 实现对文件与目录的创建、修改、删除事件的处理;
● 自定义文件监控类,通过指定目录创建一个观察者 FileAlterationObserver;
● 向监视器添加文件系统观察器,并添加文件监听器;
● 调用并执行。
第一步:创建文件监听器。根据需要在不同的方法内实现对应的业务逻辑处理。
第二步:封装一个文件监控的工具类,核心就是创建一个观察者FileAlterationObserver,将文件路径Path和监听器FileAlterationListener进行封装,然后交给FileAlterationMonitor。
第三步:调用并执行:
执行程序,会发现每隔1秒输入一次日志。当文件发生变更时,也会打印出对应的日志:
当然,对应的监听时间间隔,可以通过在创建FileMonitor时进行修改。
该方案中监听器本身会启动一个线程定时处理。在每次运行时,都会先调用事件监听处理类的onStart方法,然后检查是否有变动,并调用对应事件的方法;比如,onChange文件内容改变,检查完后,再调用onStop方法,释放当前线程占用的CPU资源,等待下次间隔时间到了被再次唤醒运行。
监听器是基于文件目录为根源的,也可以可以设置过滤器,来实现对应文件变动的监听。过滤器的设置可查看FileAlterationObserver的构造方法:
小结
至此,基于Java实现监听文件变化的三种方案便介绍完毕。经过上述分析及实例,大家已经看到,并没有完美的解决方案,根据自己的业务情况及系统的容忍度可选择最适合的方案。而且,在此基础上可以新增一些其他的辅助措施,来避免具体方案中的不足之处。
文章转载自公众号:程序员新视界
