Java动态修改配置即时生效的方式WatchService

 更新时间:2018年06月24日 11:46:30   作者:风的姿态  
这篇文章给大家分享了Java动态修改配置即时生效的方式WatchService的相关知识点内容,有兴趣的朋友可以参考学习下。

这种方式仅适合于比较小的项目,例如只有一两台服务器,而且配置文件是可以直接修改的。例如 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中实现String字符串用逗号隔开

    Java中实现String字符串用逗号隔开

    这篇文章主要介绍了Java中实现String字符串用逗号隔开,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java 模拟真正的并发请求详情

    Java 模拟真正的并发请求详情

    有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了。下文我们就来学习Java 如何模拟真正的并发请求
    2021-09-09
  • SpringBoot实现发送邮件任务

    SpringBoot实现发送邮件任务

    这篇文章主要为大家详细介绍了SpringBoot实现发送邮件任务,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • Java中finally关键字对返回值的影响详解

    Java中finally关键字对返回值的影响详解

    这篇文章主要介绍了Java中finally关键字对返回值的影响详解,执行完try catch里面内容准备return时,如果还有finally需要执行这是编译器会为我们增加一个全局变量去暂存return 的值,等到finally执行完成去return这个全局变量,需要的朋友可以参考下
    2024-01-01
  • Java @SentinelResource全面介绍

    Java @SentinelResource全面介绍

    在实际应用过程中,我们可能需要限流的层面不仅限于接口。可能对于某个方法的调用限流,对于某个外部资源的调用限流等都希望做到控制。对此,我们需要学习使用@SentinelResource注解,灵活的定义控制资源以及如何配置控制策略
    2022-08-08
  • 浅析Java中内部锁和显示锁的使用

    浅析Java中内部锁和显示锁的使用

    在java中锁主要两类,内部锁synchronized和显示锁java.util.concurrent.locks.Lock,本文将来和大家聊聊这两者的相关知识,需要的可以参考一下
    2023-06-06
  • Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

    Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

    这篇文章主要介绍了Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解,  定时任务就是在指定时间执行程序,或周期性执行计划任务,Java中实现定时任务的方法有很多,本文从从JDK自带的一些方法来实现定时任务的需求,需要的朋友可以参考下
    2024-01-01
  • SpringBoot监听Redis key失效事件的实现代码

    SpringBoot监听Redis key失效事件的实现代码

    这篇文章给大家介绍了SpringBoot实现监听Redis key失效事件的方法,文中通过代码示例给大家讲解的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2024-02-02
  • 使用Java自制一个一个Nacos

    使用Java自制一个一个Nacos

    Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台,本文将尝试用Java实现一个Nacos,感兴趣的可以了解下
    2024-01-01
  • Java解析xml文件遇到特殊符号异常的情况(处理方案)

    Java解析xml文件遇到特殊符号异常的情况(处理方案)

    这篇文章主要介绍了Java解析xml文件遇到特殊符号&会出现异常的解决方案,实现思路很简单通过在读取xml文件使用SAX解析前读取reader,具体实现方法及示例代码跟随小编一起看看吧
    2021-05-05

最新评论