激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

香港云服务器
服務器之家 - 編程語言 - JAVA教程 - Java可以如何實現文件變動的監聽的示例

Java可以如何實現文件變動的監聽的示例

2021-04-04 14:07小灰灰Blog JAVA教程

本篇文章主要介紹了Java可以如何實現文件變動的監聽的示例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

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

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

那么,這個功能是怎么實現的呢?

I. 問題描述及分析

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

現在的問題時,我需要在這個文件的內容發生變動時,應用可以感知這種變動,并重新加載文件內容,更新應用內部緩存

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

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

II. 設計與實現

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

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

那么一個很簡單的實現就比較容易了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class FileUpTest {
 
  private long lastTime;
 
  @Test
  public void testFileUpdate() {
    File file = new File("/tmp/alarmConfig");
 
    // 首先文件的最近一次修改時間戳
    lastTime = file.lastModified();
 
    // 定時任務,每秒來判斷一下文件是否發生變動,即判斷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();
        }
      }
    },0, 1, TimeUnit.SECONDS);
 
 
    try {
      Thread.sleep(1000 * 60);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

上面這個屬于一個非常簡單,非常基礎的實現了,基本上也可以滿足我們的需求,那么這個實現有什么問題呢?

定時任務的執行中,如果出現了異常會怎樣?

對上面的代碼稍作修改

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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();
    }
  }
}

實際測試,發現只有首次修改的時候,觸發了上面的代碼,但是再次修改則沒有效果了,即當拋出異常之后,定時任務將不再繼續執行了,這個問題的主要原因是因為 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. 即如果定時任務執行過程中遇到發生異常,則后面的任務將不再執行。

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

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

III. 進階版

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

首先maven依賴

?
1
2
3
4
5
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class PropertiesConfListenerHelper {
 
  public static boolean registerConfChangeListener(File file, Function<File, Map<String, AlarmConfig>> func) {
    try {
      // 輪詢間隔 5 秒
      long interval = TimeUnit.SECONDS.toMillis(5);
 
 
      // 因為監聽是以目錄為單位進行的,所以這里直接獲取文件的根目錄
      File dir = file.getParentFile();
 
      // 創建一個文件觀察器用于過濾
      FileAlterationObserver observer = new FileAlterationObserver(dir,
          FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
              FileFilterUtils.nameFileFilter(file.getName())));
 
      //設置文件變化監聽器
      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);
    }
  }
}

針對上面的實現,簡單說明幾點:

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
  }
}

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

補充JDK版本

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
public void testFileUpWather() throws IOException {
  // 說明,這里的監聽也必須是目錄
  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()) { // 重設WatchKey
          break;
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }).start();
 
 
  try {
    Thread.sleep(1000 * 60 * 10);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

IV. 小結

使用Java來實現配置文件變動的監聽,主要涉及到的就是兩個點

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

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

  1. 千萬不要在定時任務 or 文件變動的回調方法中拋出異常!!!

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://my.oschina.net/u/566591/blog/1620377

延伸 · 閱讀

精彩推薦
716
主站蜘蛛池模板: 一区二区久久久久草草 | 国产精品一二三区在线观看 | 国产精品视频久久久 | 2级毛片 | 久久精品23 | 污污的视频在线观看 | 欧美成人午夜 | 久久久久久久久久久久99 | 黄www片| 国产成人小视频在线观看 | 亚洲视色| 成人资源在线观看 | 粉嫩粉嫩一区二区三区在线播放 | 精品一区二区电影 | 91色综合综合热五月激情 | 狠狠操在线观看 | 国产成人高清在线观看 | 国产三级精品最新在线 | 成人做爰高潮片免费视频美国 | 中文字幕欧美在线 | 国产精品成人免费一区久久羞羞 | xnxx18日本| 一级性色 | 性生大片免费观看一片黄动漫 | 欧美久久久一区二区三区 | 国产精品成人一区二区三区电影毛片 | 色av成人天堂桃色av | 亚洲一区成人在线 | 国产精品99一区二区 | 亚洲性一区| 精品久久久久久久久久久下田 | 99re色| 欧美视频网| 国产一区二区三区视频在线观看 | 久久久久99999 | 石原莉奈日韩一区二区三区 | 国产99久久 | 日本一级黄色大片 | 羞羞视频在线免费 | 亚洲一区二区三区视频 | 在线观看中文字幕国产 |