PathNode(Path)
StandardWatchEventKind(WatchEvent)
Watchable(WatchKey WatchService WatchEvent)
WatchKey(PathNode WatchEvent WatchService)
WatchService(WatchKey Path)
WatchEvent
FileSystem(WatchService)
Path>Watchable

WatchEvent
StandardWatchEventKind(ENTRY_CREATE; ENTRY_DELETE; ENTRY_MODIFY; OVERFLOW)

WatchService  WatchKey

WatchService
1 register 注册监视目录 ,生成watchedKey(遍历子目录,每个文件都注册一个事件),添加到watchedKeys

1.1

    public WatchKey register(Path root, boolean fireCreatedEventOnIndex, WatchEvent.Kind<?>... events) {
if (events == null || events.length == 0)
throw new UnsupportedOperationException("null events");
if (this.service.isShutdown())
throw new IllegalStateException("服务已经关闭");
if (!root.exists())
throw new IllegalArgumentException("监视的目录不存在");
WatchKey key = new WatchKey(root, this, fireCreatedEventOnIndex, events);
resetKey(key);
return key;
}

1.1.1 WatchKey

   public WatchKey(final Path path, final WatchService watcher, boolean fireCreatedEventOnIndex,
WatchEvent.Kind<?>... events) {
valid = true;
this.watcher = watcher;
// 建立内存索引
this.root = new PathNode(path, true);
if (events != null) {
for (WatchEvent.Kind<?> event : events) {
filterSet.add(event);
}
}
LinkedList<WatchEvent<?>> changedEvents = new LinkedList<WatchEvent<?>>();
index(this.root, fireCreatedEventOnIndex, changedEvents);
this.changedEvents = changedEvents;
}

1.1.1.1 WatchKey  为每个目录下子文件建立一个ENTRY_CREATE 监听事件

  private void index(PathNode node, boolean fireCreatedEventOnIndex, LinkedList<WatchEvent<?>> changedEvents) {
File file = node.getPath().getFile();
if (!file.isDirectory()) {
return;
}
File[] subFiles = file.listFiles();
if (subFiles != null) {
for (File subFile : subFiles) {
PathNode subNode = new PathNode(new Path(subFile), false);
if (fireCreatedEventOnIndex) {
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, subNode.getPath()));
}
node.addChild(subNode);
if (subNode.getPath().isDirectory()) {
index(subNode, fireCreatedEventOnIndex, changedEvents);
}
}
}
}

2  启动的一个线程,定时扫描watchedKeys,调用watchedKey.check(),
  如果有变化,watchedKeys 删掉watchedKey ,changedKeys add watchedKey

  

    public WatchService(long checkInterval) {
service = Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(new CheckThread(), checkInterval, checkInterval, TimeUnit.MILLISECONDS);
}
    private final class CheckThread implements Runnable {
public void run() {
check();
}
} /**
* 主动check
*/
public void check() {
synchronized (this) {
Iterator<WatchKey> it = watchedKeys.iterator();
while (it.hasNext()) {
WatchKey key = it.next();
try {
if (key.check()) {
changedKeys.add(key);
it.remove();
}
}
catch (Throwable t) {
log.error("检测WatchKey异常,key=" + key, t);
}
}
}
}

2.1 key.check()

  

    boolean check() {
if (this.changedEvents != null && this.changedEvents.size() > 0)
return true;
if (!this.valid)
return false;
List<WatchEvent<?>> list = new LinkedList<WatchEvent<?>>();
if (check(root, list)) {
this.changedEvents = list;
return true;
}
else {
return false;
}
}

2.1.1

  

    private boolean check(PathNode node, List<WatchEvent<?>> changedEvents) {
Path nodePath = node.getPath();
File nodeNewFile = new File(nodePath.getAbsolutePath());
if (nodePath != null) {
if (node.isRoot()) {
if (!nodeNewFile.exists())
return fireOnRootDeleted(changedEvents, nodeNewFile);//触发删除事件,添加到当前watchedKey的changedEvents
else {
return checkNodeChildren(node, changedEvents, nodeNewFile);
}
}
else {
return checkNodeChildren(node, changedEvents, nodeNewFile);
}
}
else
throw new IllegalStateException("PathNode没有path");
}

2.1.1.1 监听新增事件 修改事件,若有讲事件添加到当前watchedKey的changedEvents

  private boolean checkNodeChildren(PathNode node, List<WatchEvent<?>> changedEvents, File nodeNewFile) {
boolean changed = false;
Iterator<PathNode> it = node.getChildren().iterator();
// 用于判断是否有新增文件或者目录的现有名称集合
Set<String> childNameSet = new HashSet<String>();
while (it.hasNext()) {
PathNode child = it.next();
Path childPath = child.getPath();
childNameSet.add(childPath.getName());
File childNewFile = new File(childPath.getAbsolutePath());
// 1、判断文件是否还存在
if (!childNewFile.exists() && filterSet.contains(StandardWatchEventKind.ENTRY_DELETE)) {
changed = true;
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_DELETE, 1, childPath));
it.remove();// 移除节点
}
// 2、如果是文件,判断是否被修改
if (childPath.isFile()) {
if (checkFile(changedEvents, child, childNewFile) && !changed) {
changed = true;
} }
// 3、递归检测目录
if (childPath.isDirectory()) {
if (check(child, changedEvents) && !changed) {
changed = true;
}
}
} // 查看是否有新增文件
File[] newChildFiles = nodeNewFile.listFiles();
if(newChildFiles!=null)
for (File newChildFile : newChildFiles) {
if (!childNameSet.contains(newChildFile.getName())
&& filterSet.contains(StandardWatchEventKind.ENTRY_CREATE)) {
changed = true;
Path newChildPath = new Path(newChildFile);
changedEvents.add(new WatchEvent<Path>(StandardWatchEventKind.ENTRY_CREATE, 1, newChildPath));
PathNode newSubNode = new PathNode(newChildPath, false);
node.addChild(newSubNode);// 新增子节点
// 如果是目录,递归调用
if (newChildFile.isDirectory()) {
checkNodeChildren(newSubNode, changedEvents, newChildFile);
}
}
}
return changed;
}

3 如何使用

 private void startCheckLocalDir(final String filePath) {
final WatchService watcher = FileSystem.getDefault().newWatchService(); Path path = new Path(new File(filePath));
// 注册事件
watcher.register(path, true, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_DELETE,
StandardWatchEventKind.ENTRY_MODIFY);
// 第一次运行,主动check
checkAtFirst(watcher);
singleExecutor.execute(new Runnable() {
public void run() {
log.debug(">>>>>>已经开始监控目录<<<<<<");
// 无限循环等待事件
while (isRun) {
// 凭证
WatchKey key;
try {
key = watcher.take();
}
catch (InterruptedException x) {
continue;
}
// reset,如果无效,跳出循环,无效可能是监听的目录被删除
if (!processEvents(key)) {
log.error("reset unvalid,监控服务失效");
break;
}
}
log.debug(">>>>>>退出监控目录<<<<<<");
watcher.close(); } });
}

3.1 处理触发的事件,并继续监听

 /**
* 处理触发的事件
*
* @param key
* @return
*/
@SuppressWarnings({ "unchecked" })
private boolean processEvents(WatchKey key) {
/**
* 获取事件集合
*/
for (WatchEvent<?> event : key.pollEvents()) {
// 事件的类型
// WatchEvent.Kind<?> kind = event.kind(); // 通过context方法得到发生事件的path
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path eventPath = ev.context(); String realPath = eventPath.getAbsolutePath();
if (ev.kind() == StandardWatchEventKind.ENTRY_CREATE || ev.kind() == StandardWatchEventKind.ENTRY_MODIFY) { String grandpaDir = null;
try {
grandpaDir = FileUtils.getGrandpaDir(realPath);
}
catch (Exception e1) { }
if (!Constants.BASE_DIR.equals(grandpaDir)) {
log.error("无效的文件进入监控目录: " + realPath);
continue;
}
existFiles.put(realPath, System.currentTimeMillis());
if (log.isInfoEnabled()) {
log.info(realPath + "文件被添加或更新");
}
}
else if (ev.kind() == StandardWatchEventKind.ENTRY_DELETE) {
String grandpaDir = null;
try {
grandpaDir = FileUtils.getGrandpaDir(realPath);
}
catch (Exception e1) { }
if (Constants.BASE_DIR.equals(grandpaDir)) {
// 删除的是文件
existFiles.remove(realPath);
if (log.isInfoEnabled()) {
log.info(realPath + "文件被被删除");
}
}
else {
// 删除的是目录
Set<String> keySet = new HashSet<String>(existFiles.keySet());
for (String filePath : keySet) {
if (filePath.startsWith(realPath)) {
existFiles.remove(filePath);
if (log.isInfoEnabled()) {
log.info(filePath + "文件被删除");
}
}
} }
}
}
return key.reset();//WatchService 继续监听
}

diamond源码阅读-目录监控的更多相关文章

  1. diamond源码阅读-diamond-client

    读取数据 DiamondManager manager = new DefaultDiamondManager("DEFAULT_GROUP", "zml", ...

  2. diamond源码阅读-获取服务器列表

    serverAddressProcessor public synchronized void start() { if (isRun) { return; } isRun = true; initH ...

  3. diamond源码阅读-diamond-server

    diamond-server 1 增加一条数据 /diamond-server/admin.do?method=postConfig 1.1 调用 this.configService.addConf ...

  4. diamond源码阅读-循环探测配置信息是否变化rotateCheckConfigInfo

    rotateCheckConfigInfo 这是一个定时任务,循环调用 /** * 循环探测配置信息是否变化,如果变化,则再次向DiamondServer请求获取对应的配置信息 */ private ...

  5. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  6. 应用监控CAT之cat-client源码阅读(一)

    CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...

  7. 【安卓本卓】Android系统源码篇之(一)源码获取、源码目录结构及源码阅读工具简介

    前言        古人常说,“熟读唐诗三百首,不会作诗也会吟”,说明了大量阅读诗歌名篇对学习作诗有非常大的帮助.做开发也一样,Android源码是全世界最优秀的Android工程师编写的代码,也是A ...

  8. CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程

    最开始使用CI框架的时候,就打算写一个CI源码阅读的笔记系列,可惜虎头蛇尾,一直没有行动.最近项目少,总算是有了一些时间去写一些东西.于是准备将之前的一些笔记和经验记录下来,一方面权作备忘,另一方面时 ...

  9. Kubernetes 学习(九)Kubernetes 源码阅读之正式篇------核心组件之 Scheduler

    0. 前言 继续上一篇博客阅读 Kubernetes 源码,参照<k8s 源码阅读>首先学习 Kubernetes 的一些核心组件,首先是 kube-scheduler 本文严重参考原文: ...

随机推荐

  1. Android SDK 目录说明

    Android SDK目录说明: AVD Manager.exe:虚拟机管理工具 SDK Manager.exe:sdk管理工具 tools目录:包括测试.调试.第三方工具.模拟器.数据管理工具等. ...

  2. onItemClick(AdapterView<?> parent, View view, int position, long id)

    Public Methods public abstract void onItemClick (AdapterView<?> parent, View view, int positio ...

  3. 常用SQL收藏

    原文:常用SQL收藏 MSSQL Split表字段 --拆分字符串之后匹配结果集合 CREATE FUNCTION [dbo].[fnSplit]( @sInputList VARCHAR(8000) ...

  4. Winform打砖块游戏制作step by step第4节---小球移动

    一 引子 为了让更多的编程初学者,轻松愉快地掌握面向对象的思考方法,对象继承和多态的妙用,故推出此系列随笔,还望大家多多支持. 预备知识,无GDI画图基础的童鞋请先阅读一篇文章让你彻底弄懂WinFor ...

  5. 设计模式之中介者模式(php实现)

    github地址:https://github.com/ZQCard/design_pattern /** * 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性. ...

  6. Java 7 新功能: 省略finally, 保证资源正常关闭

    class MyResource implements Closeable{ @Override public void close() throw IOException{ } } try( myR ...

  7. 2017.7.7 在eclipse中快速查找类:ctrl+shift+T

    快捷键:ctrl+shift+T,用于快速找到某个类.

  8. 关于web后门权限防删的一个新思路

    见土司有一帖子 具体看下面连接 https://www.t00ls.net/thread-26444-1-2.html 这是php的 其实IIS也有类似的 这是MSF生成的一个ASPX执行shellc ...

  9. php 位运算符的实际应用(权限判断)

    关键代码: define("READ", 1); define("WRITE", 2); define("DELETE", 4); defi ...

  10. iOS项目开发实战——使用同步请求获取网页源码

    网络请求一般分为同步请求和异步请求,同步请求假设訪问时间过长,会造成界面卡死状态,用户体验不是非常好.可是请求速度较快的话,也能够考虑使用同步訪问.如今先来学习同步訪问. (1)在viewDidLoa ...