文件监听之WatchService浅析

简介

WatchService是jdk7之后nio包中的新功能。可以看作是文件监控器,通过操作系统原生文件系统来运行。 针对单点多appkey的情况,可以注册开启多个监控器。 每个监控器可看作是后台线程,通过监控文件发出的信号来实现监控。

应用场景

1、感知系统配置文件的变化,修改配置文件内容即时生效,无需重启服务器 2、监控磁盘中的文件变化

用watchservice修改配置文件方式仅适合于比较小的项目,例如只有一两台服务器,而且配置文件是可以直接修改的。例如 Spring mvc 以 war 包的形式部署,可以直接修改resources 中的配置文件。如果是 Spring boot 项目,还想用这种方式的话,就要引用一个外部可以编辑的文件,比如一个固定的目录,因为 spring boot 大多数以 jar 包部署,打到包里的配置文件没办法直接修改。如果是比较大的项目,最好还是用配置中心,例如携程的 Apollo、Consul 等。

动态修改配置即时生效实现方法

1、WatchService 实例化

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

2、使用 Path 来指定要监控的目录

String filePath = new ClassPathResource(configName).getFile().getParent();
Path path = Paths.get(filePath);

3、将 Path 注册到 WatchService

使用Path.register() 方法注册要监控指定目录的那些事件(创建、修改、删除)

StandardWatchEventKinds.ENTRYCREATE //创建 StandardWatchEventKinds.ENTRYMODIFY //修改 StandardWatchEventKinds.ENTRY_DELETE //删除

path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);

4、创建监听配置文件守护线程

使用WatchService监听配置文件所在目录内容的变化,包括修改、删除事件。通过后台线程实现阻塞等待内容变化事件,一旦发现有变更,则重新装载配置文件

核心代码块:

private void pollingMonitor() {
        while (true) {
            try {
                // 尝试获取监控池的变化,如果没有则一直等待
                WatchKey watchKey = watchService.take();
                for (WatchEvent<?> event : watchKey.pollEvents()) {
                    if (PropsUtil.CONFIG_NAME.equals(event.context().toString())) {
                        logger.info("监控到{}发生{}操作,将重新加载", event.context(), event.kind());
                        PropsUtil.getInstance().loadProperties(event.context().toString());
                        break;
                    }
                }
                // 完成一次监控就需要重置监控器一次
                watchKey.reset();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

5、注册关闭钩子,当JVM停止时关闭WatchService。

public class WatcherHookThead implements Runnable {
    private static Logger logger = LoggerFactory.getLogger(WatcherHookThead.class);

    private WatchService watchService;

    public WatcherHookThead(WatchService watchService) {
        super();
        this.watchService = watchService;
    }

    @Override
    public void run() {

        try {
            watchService.close();
            logger.info("watcher close success...");
        } catch (IOException e) {
            logger.error("watcher close fail:" + e.getMessage(), e);
        }
    }

}
public void shutDownWatcher() {
        Thread shutDownThread = new Thread(new WatcherHookThead(watchService));
        shutDownThread.setName("shutDownThread");
        Runtime.getRuntime().addShutdownHook(shutDownThread);
    }

验证动态修改配置是否生效测试

1、测试代码

public void testValueIfChange() {
        while (true) {
            String value = PropsUtil.getInstance().getValue("username");
            try {
                System.out.println(StringUtil.format("value--->{}", value));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

application.properties

初始内容 username=helloworld

不停服务,将username修改为test,观察控制台打印value的值是否动态生效

value--->helloworld
2018-06-29 14:57:57.132  INFO 11988 --- [  watcherThread] com.demo.watchserver.util.WatcherThread  : 监控到application.properties发生ENTRY_MODIFY操作,将重新加载
value--->test

通过控制台可以说明,value已经动态修改,说明配置文件即时被加载

demo地址

https://github.com/lyb-geek/first-watchserver

原文发布于微信公众号 - Linyb极客之路(gh_c420b2cf6b47)

原文发表时间:2018-07-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木木玲

Netty 源码解析 ——— Netty 优雅关闭流程

48930
来自专栏Youngxj

[教程]黑客级别的批量处理文件

25630
来自专栏凉城

[教程]黑客级别的批量处理文件

24950
来自专栏菩提树下的杨过

java学习:eclipse + Weblogic 12c + svn 集成开发环境搭建

网上有很多文章都说eclipse要安装额外的插件才能支持weblogic,可能以前需要这样,但自从bea的weblogic被oracle收购后,现在已经很简单了...

33390
来自专栏安恒网络空间安全讲武堂

Python编写渗透工具学习笔记一 | 0x07 Python实现键盘记录器

0x07 Python实现键盘记录器 这份代码比较经典,里面的注释也写的很详细,我也就直接放出来给大家一起学习一下。 简单说一说 我们定义了pyhook的hoo...

565100
来自专栏技术博客

C#简单的面试题目(六)

76.HashMap和Hashtable的区别。 答:HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于H...

9720
来自专栏FreeBuf

BASH漏洞初挖掘

最近,BASH爆出来一个远程代码执行的漏洞CVE-2014-6271。 BASH除了可以将shell变量导出为环境变量,还可以将shell函数导出为环境变量!当...

19550
来自专栏北京马哥教育

Python imports指南

来源:Python程序员 ID:pythonbuluo 声明:如果你每天写Python,你会发现这篇文章中没有新东西。 这是专为那些像运维人员等偶尔使用Pyt...

26250
来自专栏MasiMaro 的技术博文

OLEDB 数据变更通知

除了之前介绍的接口,OLEDB还定义了其他一些支持回调的接口,可以异步操作OLEDB对象或者得到一些重要的事件通知,从而使应用程序有机会进行一些必要的处理。其中...

11830
来自专栏Linux驱动

36.Linux驱动调试-根据oops定位错误代码行

1.当驱动有误时,比如,访问的内存地址是非法的,便会打印一大串的oops出来 1.1以LED驱动为例 将open()函数里的ioremap()屏蔽掉,直接使用物...

30180

扫码关注云+社区

领取腾讯云代金券