Java Review - 使用Event Bus实现目录变化的监控
文章目錄
- Pre
- 需求
- Event Bus案例實戰(zhàn)
- 小結(jié)
Pre
Java Review - Java進程內(nèi)部的消息中間件_Event Bus設(shè)計模式
需求
監(jiān)控文件的實時變化 , 就這一句話???
分析一下其中一個有問題的思路:
程序首次啟動時獲取該文件的最后修改時間并且做文件的首次解析,然后每隔一段指定的時間檢查一次文件最后被修改的時間,如果與記錄的時間相等則等待下次的采集(Balking Pattern),否則進行新一輪的采集并且更新時間。
這。。。。有問題啊 , 比如在采集時間間隔內(nèi),文件發(fā)生了N次變化,只能獲取到最后一次,其根本原因是文件的變化不會通知到應(yīng)用程序, 我只能傻傻的輪詢~
新思路:
JDK自1.7版本后提供了WatchService類,該類可以基于事件通知的方式監(jiān)控文件或者目錄的任何變化,文件的改變相當于每一個事件(Event)的發(fā)生,針對不同的時間執(zhí)行不同的動作,結(jié)合NIO2.0中提供的WatchService和Event Bus實現(xiàn)文件目錄的監(jiān)控的功能。
Event Bus案例實戰(zhàn)
import com.artisan.bfzm.eventbus.EventBus;import java.nio.file.*;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/2 16:37* @mark: show me the code , change the world*/ public class DirectoryTargetMonitor {private WatchService watchService;private final EventBus eventBus;private final Path path;private volatile boolean start = false;public DirectoryTargetMonitor(final EventBus eventBus,final String targetPath) {this(eventBus, targetPath, "");}/**** 構(gòu)造Monitor的時候需要傳入EventBus以及需要監(jiān)控的目錄* @param eventBus* @param targetPath* @param morePaths*/public DirectoryTargetMonitor(final EventBus eventBus,final String targetPath, final String... morePaths) {this.eventBus = eventBus;this.path = Paths.get(targetPath, morePaths);}public void startMonitor() throws Exception {this.watchService = FileSystems.getDefault().newWatchService();//為路徑注冊感興趣的事件this.path.register(watchService,StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_CREATE);System.out.printf("The directory [%s] is monitoring... \n", path);this.start = true;while (start) {WatchKey watchKey = null;try {//當有事件發(fā)生時會返回對應(yīng)的WatchKeywatchKey = watchService.take();watchKey.pollEvents().forEach(event ->{WatchEvent.Kind<?> kind = event.kind();Path path = (Path) event.context();Path child = DirectoryTargetMonitor.this.path.resolve(path);//提交FileChangeEvent到EventBuseventBus.post(new FileChangeEvent(child, kind));});} catch (Exception e) {this.start = false;} finally {if (watchKey != null) {watchKey.reset();}}}}public void stopMonitor() throws Exception {System.out.printf("The directory [%s] monitor will be stop...\n", path);Thread.currentThread().interrupt();this.start = false;this.watchService.close();System.out.printf("The directory [%s] monitor will be stop done.\n", path);} }在創(chuàng)建WatchService之后將文件的修改、刪除、創(chuàng)建等注冊給了WatchService,在指定目錄下發(fā)生諸如此類的事件之后便會收到通知,我們將事件類型和發(fā)生變化的文件Path封裝成FileChangeEvent提交給Event Bus.
import java.nio.file.Path; import java.nio.file.WatchEvent;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/2 16:48* @mark: show me the code , change the world*/ public class FileChangeEvent {private final Path path;private final WatchEvent.Kind<?> kind;public FileChangeEvent(Path path, WatchEvent.Kind<?> kind) {this.path = path;this.kind = kind;}public Path getPath() {return path;}public WatchEvent.Kind<?> getKind() {return kind;} }FileChangeEvent是對WatchEvent.Kind和Path的包裝,一旦目錄發(fā)生任何改變,都會提交FileChangeEvent事件。
目錄監(jiān)控的程序我們已經(jīng)實現(xiàn)了,下面就來寫一個接受文件目錄變化的Subscriber,也就是當目錄發(fā)生變化時用來接受事件的方法
import com.artisan.bfzm.eventbus.Subscribe;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/2 17:06* @mark: show me the code , change the world*/ public class FileChangeListener {@Subscribepublic void onChange(FileChangeEvent event) {System.out.printf("%s-%s-%s\n", Thread.currentThread().getName(),event.getPath(), event.getKind());} }onChange方法由@Subscribe標記,但沒有指定topic,當有事件發(fā)送到了默認的topic上之后,該方法將被調(diào)用執(zhí)行,接下來我們將FileChangeListener的實例注冊給Event Bus并且啟動Monitor程序
import com.artisan.bfzm.eventbus.AsyncEventBus; import com.artisan.bfzm.eventbus.EventBus;import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/2 17:15* @mark: show me the code , change the world*/ public class FileDirTest {public static void main(String[] args) throws Exception {ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);final EventBus eventBus = new AsyncEventBus(executor);//注冊eventBus.register(new FileChangeListener());DirectoryTargetMonitor monitor = new DirectoryTargetMonitor(eventBus, "C:\\Users\\artisan\\Desktop\\aaa");monitor.startMonitor();} }小結(jié)
EventBus有點類似于GOF設(shè)計模式中的監(jiān)聽者模式,但是EventBus提供的功能更加強大,使用起來也更加靈活,EventBus中的Subscriber不需要繼承任何類或者實現(xiàn)任何接口,在使用EventBus時只需要持有Bus的引用即可。
在EventBus的設(shè)計中有三個非常重要的角色(Bus、Registry和Dispatcher),Bus主要提供給外部使用的操作方法,Registry注冊表用來整理記錄所有注冊在EventBus上的Subscriber,Dispatcher主要負責對Subscriber消息進行推送(用反射的方式執(zhí)行方法),但是考慮到程序的靈活性,Dispatcher方法中又提供了Executor的多態(tài)方式。
總結(jié)
以上是生活随笔為你收集整理的Java Review - 使用Event Bus实现目录变化的监控的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java Review - Java进程
- 下一篇: DDD - 如何理解Entity与VO