首页 > 编程语言 >Java 项目中一种简单的动态修改配置即时生效的方式 WatchService

Java 项目中一种简单的动态修改配置即时生效的方式 WatchService

时间:2023-04-28 17:36:23浏览次数:65  
标签:Java 配置文件 WatchService 即时 StandardWatchEventKinds 监控器 监控 tomcat

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

原始方式

原始方式指的是每次要修改配置的时候,都要重新打包发布或者重启服务器。

假设我们用 spring mvc 开发,开发完成后打成 war 包部署到 tomcat 上,如果这时我们修改一个短信接口地址。

我们要做如下操作:

1、打开配置文件,修改配置信息;

2、编译打包;

3、停止 tomcat ,删除旧的项目目录;

4、将新的 war 包放到 webapps ,启动 tomcat。

当然,可以直接在 tomcat 中找到这个项目的配置文件,然后修改,但同样需要重启 tomcat 。

如果只是单纯做开发或者测试,除了有点浪费时间外,当然可以接受。那么,既不想浪费时间又不想重启 tomcat 呢,有没有办法呢。这就轮到本文介绍的这种方式了。

WatchService 方式

Java 提供了 WatchService 接口,这个接口是利用操作系统本身的文件监控器对目录和文件进行监控,当被监控对象发生变化时,会有信号通知,从而可以高效的发现变化。

这种方式大致的原理:先根据操作系统 new 一个监控器( WatchService ),然后选择要监控的配置文件所在目录或文件,然后订阅要监控的事件,例如创建、删除、编辑,最后向被监控位置注册这个监控器。一旦触发对应我们所订阅的事件时,执行相应的逻辑即可。

先上代码吧,这是在一个 spring mvc 项目里,监控的是 resources 目录。

@Repository
public class ConfigWatcher {

    private static final Logger logger = LoggerFactory.getLogger(ConfigWatcher.class);

    private static WatchService watchService;

    @PostConstruct
    public void init() {
        logger.info("启动配置文件监控器");
        try {
            watchService = FileSystems.getDefault().newWatchService();
            URL url = ConfigWatcher.class.getResource("/");
            Path path = Paths.get(url.toURI());
            path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        /**
         * 启动监控线程
         */
        Thread watchThread = new Thread(new WatchThread());
        watchThread.setDaemon(true);
        watchThread.start();

        /**注册关闭钩子*/
        Thread hook = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    watchService.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(hook);
    }

    public class WatchThread implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    // 尝试获取监控池的变化,如果没有则一直等待
                    WatchKey watchKey = watchService.take();
                    for (WatchEvent<?> event : watchKey.pollEvents()) {
                        String editFileName = event.context().toString();
                        logger.info(editFileName);
                        /**
                         * 重新加载配置
                         */
                    }
                    watchKey.reset();//完成一次监控就需要重置监控器一次
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

代码非常简单,一看就懂,在项目启动的时候,用 FileSystems.getDefault().newWatchService() 创建一个 WatchService,这是根据操作系统来的。然后获取 resources 目录的 URL,并由此获取 Path,然后调用 Path 对象的 register 方法,注册监控器,订阅了编辑和创建事件。事件在 StandardWatchEventKinds 类中定义,共有四种:

1、StandardWatchEventKinds#OVERFLOW

2、StandardWatchEventKinds#ENTRY_CREATE

3、StandardWatchEventKinds#ENTRY_DELETE

4、StandardWatchEventKinds#ENTRY_MODIFY

然后单独启动了一个 WatchThread 线程来处理变化逻辑,在一个 while 无限循环中调用 take() 方法,直到有变化发生,一旦是我们监控的配置文件发生了变化,则调用我们的逻辑重新加载配置。另外,每次有变化发生后,要调用 watchKey.reset() 方法来重置监控器。

最后,还要注册一个 hook,在 jvm 关闭的时候可以关闭监控器。

有了这种方式,当我们有一些配置变化的时候,就可以直接到 tomcat 下修改配置文件,不用重启就可以生效了。

本文主要介绍的是这种方式,上面也说了,这种方式只适合非常简单的项目,对于大型项目,就需要用到更高级的方式了。

配置中心的方式

当项目复杂度变高,配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制可能都变成项目中要考虑的问题,这个时候,单纯依赖配置文件就显得力不从心了。

目前比较用的比较多的配置中心有etcd、zookeeper、disconf、Apollo 等。disconf、Apollo 都是属于拿来即用的,功能完善,而且有配套的 UI。而 etcd 和 zookeeper 需要一些定制开发。

各位同学可以根据需要自行选择,更详细的内容可以自行搜索和实践。

最后,各位同学不妨到我的公众号里互动一下 :古时的风筝

Java 项目中一种简单的动态修改配置即时生效的方式 WatchService_WatchService

人生没有回头路,珍惜当下。



标签:Java,配置文件,WatchService,即时,StandardWatchEventKinds,监控器,监控,tomcat
From: https://blog.51cto.com/u_15717245/6235028

相关文章

  • Java继承
    Java继承继承的概念基础主要解决的问题是:共性抽取特点:子类可以拥有父类的"内容"子类还可以拥有自己的专有内容继承的格式在继承关系中,"子类就是一个父类,子类可以被当做父类看待."父类的格式定义:(一个普通的类的定义)子类的格式定义:publicclass子类名称exten......
  • Java多线程之---用 CountDownLatch 说明 AQS 的实现原理
    本文基于jdk1.8。CountDownLatch的使用前面的文章中说到了volatile以及用volatile来实现自旋锁,例如java.util.concurrent.atomic包下的工具类。但是volatile的使用场景毕竟有限,很多的情况下并不是适用,这个时候就需要synchronized或者各种锁实现了。今天就来说一下几......
  • BTrace : Java 线上问题排查神器
    BTrace是什么BTrace是检查和解决线上的问题的杀器,BTrace可以通过编写脚本的方式,获取程序执行过程中的一切信息,并且,注意了,不用重启服务,是的,不用重启服务。写好脚本,直接用命令执行即可,不用动原程序的代码。原理总体来说,BTrace是基于动态字节码修改技术(Hotswap)来实现运行时java......
  • Java 中的几种线程池,你之前用对了吗
    好久不发文章了,难道是因为忙,其实是因为懒。这是一篇关于线程池使用和基本原理的科普水文,如果你经常用到线程池,不知道你的用法标准不标准,是否有隐藏的OOM风险。不经常用线程池的同学,还有对几种线程的使用不甚了解的同学可以读一下此文。为什么要使用线程池虽然大家应该都已经很清......
  • Java1.8 新特性之Stream流
    转:Java1.8新特性之Stream流JDK1.8新特性 ......
  • Java 接口中的方法1.8,为了区别函数式接口
    Java接口中的方法函数式接口式指的式接口冲抽象方法只有一个  函数式接口 java中Comparator接口明明不止一个抽象方法,为什么也算函数式接口?  ......
  • Java算数运算符(++和--)
    1.++和--单独使用就是自增和自减i++-->i=i+1++i-->i=i+1i---->i=i-1--i-->i=i-12.++和--作为表达式使用j=++i-->先自增后赋值-->i=i+1;j=ij=i++-->先赋值后自增-->j=i;i=i+1j=--i-->先自减后赋值-->i=i-1;j=ij=i---->先......
  • 十个最好的Java性能故障排除工具
    推荐十个最好的Java性能故障排除工具:1.jconsole是随着JDK1.5而推出的。这是一个Java监测和管理控制台-JMX兼容的图形工具来监测Java虚拟机。它能够同时监测本地和远程的JVMs。详情可查看:jconsole工具介绍2.VisualVM集成了几个现有的JDK软件工具,轻量级内存和CPU信息概要能力。......
  • Java程序性能优化
    一、避免在循环条件中使用复杂表达式在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。例子:importjava.util.Vector;classCEL{voidmethod(Vectorvector){for(inti=0;i<vector.si......
  • Java设计模式-单例模式
    一、前言单例模式是一种设计模式,它确保一个类只能创建一个实例,并提供一种全局访问这个实例的方式。在Java中,单例模式可以通过多种方式来实现,其中最常见的是使用私有构造函数和静态方法实现二、基本语法在Java中,实现单例模式的方式有多种,其中最常见的实现方式包括以下几种:1、......