欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java可以如何實現(xiàn)文件變動的監(jiān)聽的示例

 更新時間:2018年02月09日 14:33:35   作者:小灰灰Blog  
本篇文章主要介紹了Java可以如何實現(xiàn)文件變動的監(jiān)聽的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

應(yīng)用中使用logback作為日志輸出組件的話,大部分會去配置 `logback.xml` 這個文件,而且生產(chǎn)環(huán)境下,直接去修改logback.xml文件中的日志級別,不用重啟應(yīng)用就可以生效 那么,這個功能是怎么實現(xiàn)的呢?

應(yīng)用中使用logback作為日志輸出組件的話,大部分會去配置 logback.xml 這個文件,而且生產(chǎn)環(huán)境下,直接去修改logback.xml文件中的日志級別,不用重啟應(yīng)用就可以生效

那么,這個功能是怎么實現(xiàn)的呢?

I. 問題描述及分析

針對上面的這個問題,首先拋出一個實際的case,在我的個人網(wǎng)站 Z+中,所有的小工具都是通過配置文件來動態(tài)新增和隱藏的,因為只有一臺服務(wù)器,所以配置文件就簡化的直接放在了服務(wù)器的某個目錄下

現(xiàn)在的問題時,我需要在這個文件的內(nèi)容發(fā)生變動時,應(yīng)用可以感知這種變動,并重新加載文件內(nèi)容,更新應(yīng)用內(nèi)部緩存

一個最容易想到的方法,就是輪詢,判斷文件是否發(fā)生修改,如果修改了,則重新加載,并刷新內(nèi)存,所以主要需要關(guān)心的問題如下:

  1. 如何輪詢?
  2. 如何判斷文件是否修改?
  3. 配置異常,會不會導(dǎo)致服務(wù)不可用?(即容錯,這個與本次主題關(guān)聯(lián)不大,但又比較重要...)

II. 設(shè)計與實現(xiàn)

問題抽象出來之后,對應(yīng)的解決方案就比較清晰了

  1. 如何輪詢 ? --》 定時器 Timer, ScheduledExecutorService 都可以實現(xiàn)
  2. 如何判斷文件修改? --》根據(jù) java.io.File#lastModified 獲取文件的上次修改時間,比對即可

那么一個很簡單的實現(xiàn)就比較容易了:

public class FileUpTest {

  private long lastTime;

  @Test
  public void testFileUpdate() {
    File file = new File("/tmp/alarmConfig");

    // 首先文件的最近一次修改時間戳
    lastTime = file.lastModified();

    // 定時任務(wù),每秒來判斷一下文件是否發(fā)生變動,即判斷l(xiāng)astModified是否改變
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        if (file.lastModified() > lastTime) {
          System.out.println("file update! time : " + file.lastModified());
          lastTime = file.lastModified();
        }
      }
    },0, 1, TimeUnit.SECONDS);


    try {
      Thread.sleep(1000 * 60);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

上面這個屬于一個非常簡單,非?;A(chǔ)的實現(xiàn)了,基本上也可以滿足我們的需求,那么這個實現(xiàn)有什么問題呢?

定時任務(wù)的執(zhí)行中,如果出現(xiàn)了異常會怎樣?

對上面的代碼稍作修改

public class FileUpTest {

  private long lastTime;

  private void ttt() {
    throw new NullPointerException();
  }

  @Test
  public void testFileUpdate() {
    File file = new File("/tmp/alarmConfig");

    lastTime = file.lastModified();

    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
      @Override
      public void run() {
        if (file.lastModified() > lastTime) {
          System.out.println("file update! time : " + file.lastModified());
          lastTime = file.lastModified();
          ttt();
        }
      }
    }, 0, 1, TimeUnit.SECONDS);


    try {
      Thread.sleep(1000 * 60 * 10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

實際測試,發(fā)現(xiàn)只有首次修改的時候,觸發(fā)了上面的代碼,但是再次修改則沒有效果了,即當(dāng)拋出異常之后,定時任務(wù)將不再繼續(xù)執(zhí)行了,這個問題的主要原因是因為 ScheduledExecutorService 的原因了

直接查看ScheduledExecutorService的源碼注釋說明

If any execution of the task encounters an exception, subsequent executions are suppressed.Otherwise, the task will only terminate via cancellation or termination of the executor. 即如果定時任務(wù)執(zhí)行過程中遇到發(fā)生異常,則后面的任務(wù)將不再執(zhí)行。

所以,使用這種姿勢的時候,得確保自己的任務(wù)不會拋出異常,否則后面就沒法玩了

對應(yīng)的解決方法也比較簡單,整個catch一下就好

III. 進階版

前面是一個基礎(chǔ)的實現(xiàn)版本了,當(dāng)然在java圈,基本上很多常見的需求,都是可以找到對應(yīng)的開源工具來使用的,當(dāng)然這個也不例外,而且應(yīng)該還是大家比較屬性的apache系列

首先maven依賴

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>

主要是借助這個工具中的 FileAlterationObserver, FileAlterationListener, FileAlterationMonitor 三個類來實現(xiàn)相關(guān)的需求場景了,當(dāng)然使用也算是很簡單了,以至于都不太清楚可以再怎么去說明了,直接看下面從我的一個開源項目quick-alarm中拷貝出來的代碼

public class PropertiesConfListenerHelper {

  public static boolean registerConfChangeListener(File file, Function<File, Map<String, AlarmConfig>> func) {
    try {
      // 輪詢間隔 5 秒
      long interval = TimeUnit.SECONDS.toMillis(5);


      // 因為監(jiān)聽是以目錄為單位進行的,所以這里直接獲取文件的根目錄
      File dir = file.getParentFile();

      // 創(chuàng)建一個文件觀察器用于過濾
      FileAlterationObserver observer = new FileAlterationObserver(dir,
          FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
              FileFilterUtils.nameFileFilter(file.getName())));

      //設(shè)置文件變化監(jiān)聽器
      observer.addListener(new MyFileListener(func));
      FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
      monitor.start();

      return true;
    } catch (Exception e) {
      log.error("register properties change listener error! e:{}", e);
      return false;
    }
  }


  static final class MyFileListener extends FileAlterationListenerAdaptor {

    private Function<File, Map<String, AlarmConfig>> func;

    public MyFileListener(Function<File, Map<String, AlarmConfig>> func) {
      this.func = func;
    }

    @Override
    public void onFileChange(File file) {
      Map<String, AlarmConfig> ans = func.apply(file); // 如果加載失敗,打印一條日志
      log.warn("PropertiesConfig changed! reload ans: {}", ans);
    }
  }
}

針對上面的實現(xiàn),簡單說明幾點:

  1. 這個文件監(jiān)聽,是以目錄為根源,然后可以設(shè)置過濾器,來實現(xiàn)對應(yīng)文件變動的監(jiān)聽
  2. 如上面registerConfChangeListener方法,傳入的file是具體的配置文件,因此構(gòu)建參數(shù)的時候,撈出了目錄,撈出了文件名作為過濾
  3. 第二參數(shù)是jdk8語法,其中為具體的讀取配置文件內(nèi)容,并映射為對應(yīng)的實體對象

一個問題,如果 func方法執(zhí)行時,也拋出了異常,會怎樣?

實際測試表現(xiàn)結(jié)果和上面一樣,拋出異常之后,依然跪,所以依然得注意,不要跑異常

那么簡單來看一下上面的實現(xiàn)邏輯,直接扣出核心模塊

public void run() {
  while(true) {
    if(this.running) {
      Iterator var1 = this.observers.iterator();

      while(var1.hasNext()) {
        FileAlterationObserver observer = (FileAlterationObserver)var1.next();
        observer.checkAndNotify();
      }

      if(this.running) {
        try {
          Thread.sleep(this.interval);
        } catch (InterruptedException var3) {
          ;
        }
        continue;
      }
    }

    return;
  }
}

從上面基本上一目了然,整個的實現(xiàn)邏輯了,和我們的第一種定時任務(wù)的方法不太一樣,這兒直接使用線程,死循環(huán),內(nèi)部采用sleep的方式來來暫停,因此出現(xiàn)異常時,相當(dāng)于直接拋出去了,這個線程就跪了

補充JDK版本

jdk1.7,提供了一個WatchService,也可以用來實現(xiàn)文件變動的監(jiān)聽,之前也沒有接觸過,才知道有這個東西,然后搜了一下使用相關(guān),發(fā)現(xiàn)也挺簡單的,看到有博文說明是基于事件驅(qū)動式的,效率更高,下面也給出一個簡單的示例demo

@Test
public void testFileUpWather() throws IOException {
  // 說明,這里的監(jiān)聽也必須是目錄
  Path path = Paths.get("/tmp");
  WatchService watcher = FileSystems.getDefault().newWatchService();
  path.register(watcher, ENTRY_MODIFY);

  new Thread(() -> {
    try {
      while (true) {
        WatchKey key = watcher.take();
        for (WatchEvent<?> event : key.pollEvents()) {
          if (event.kind() == OVERFLOW) {
            //事件可能lost or discarded 
            continue;
          }
          Path fileName = (Path) event.context();
          System.out.println("文件更新: " + fileName);
        }
        if (!key.reset()) { // 重設(shè)WatchKey
          break;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }).start();


  try {
    Thread.sleep(1000 * 60 * 10);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

IV. 小結(jié)

使用Java來實現(xiàn)配置文件變動的監(jiān)聽,主要涉及到的就是兩個點

  1. 如何輪詢: 定時器(Timer, ScheduledExecutorService), 線程死循環(huán)+sleep
  2. 文件修改: File#lastModified

整體來說,這個實現(xiàn)還是比較簡單的,無論是自定義實現(xiàn),還是依賴 commos-io來做,都沒太大的技術(shù)成本,但是需要注意的一點是:

  1. 千萬不要在定時任務(wù) or 文件變動的回調(diào)方法中拋出異常?。?!

為了避免上面這個情況,一個可以做的實現(xiàn)是借助EventBus的異步消息通知來實現(xiàn),當(dāng)文件變動之后,發(fā)送一個消息即可,然后在具體的重新加載文件內(nèi)容的方法上,添加一個 @Subscribe注解即可,這樣既實現(xiàn)了解耦,也避免了異常導(dǎo)致的服務(wù)異常 (如果對這個實現(xiàn)有興趣的可以評論說明)

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 分享一個簡單的java爬蟲框架

    分享一個簡單的java爬蟲框架

    這篇文章主要介紹了分享一個簡單的java爬蟲框架,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • springboot整合mybatis-plus 實現(xiàn)分頁查詢功能

    springboot整合mybatis-plus 實現(xiàn)分頁查詢功能

    這篇文章主要介紹了springboot整合mybatis-plus 實現(xiàn)分頁查詢功能,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • java實現(xiàn)文件夾解壓和壓縮

    java實現(xiàn)文件夾解壓和壓縮

    這篇文章主要為大家詳細介紹了java實現(xiàn)文件夾解壓和壓縮,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • 30分鐘入門Java8之默認方法和靜態(tài)接口方法學(xué)習(xí)

    30分鐘入門Java8之默認方法和靜態(tài)接口方法學(xué)習(xí)

    這篇文章主要介紹了30分鐘入門Java8之默認方法和靜態(tài)接口方法學(xué)習(xí),詳細介紹了默認方法和接口,有興趣的可以了解一下。
    2017-04-04
  • Java返回可變引用對象問題整理

    Java返回可變引用對象問題整理

    在本篇文章里小編給大家分享的是關(guān)于Java返回可變引用對象問題整理內(nèi)容,需要的朋友們可以學(xué)習(xí)下。
    2020-04-04
  • 淺談Java編程中string的理解與運用

    淺談Java編程中string的理解與運用

    這篇文章主要介紹了淺談Java編程中string的理解與運用,還是比較不錯的,這里分享給大家,供需要的朋友參考。
    2017-11-11
  • Java的異常處理體系詳解

    Java的異常處理體系詳解

    這篇文章主要介紹了Java的異常處理體系,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 使用Spring動態(tài)修改bean屬性的key

    使用Spring動態(tài)修改bean屬性的key

    這篇文章主要介紹了使用Spring動態(tài)修改bean屬性的key方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • java實現(xiàn)基于SMTP發(fā)送郵件的方法

    java實現(xiàn)基于SMTP發(fā)送郵件的方法

    這篇文章主要介紹了java實現(xiàn)基于SMTP發(fā)送郵件的方法,實例分析了java基于SMTP服務(wù)發(fā)送郵件的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • Java中的OkHttpClient 工具類

    Java中的OkHttpClient 工具類

    這篇文章主要介紹了Java中的OkHttpClient工具類,具有很好的參考價值,希望杜大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論