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. PHP数组输出三种形式 PHP打印数组

    PHP数组输出三种形式 PHP打印数组   $bbbb=array("11"=>"aaa","22"=>"bbb&qu ...

  2. zookeeper 学习笔记1(转)

    本文转自https://www.cnblogs.com/fanguangdexiaoyuer/p/7077520.html 感谢作者 可以设置观察的操作:exists,getChildren,getD ...

  3. Git:fatal: The remote end hung up unexpectedly

    一.配置公共密钥 https://help.github.com/articles/generating-ssh-keys/ 二.设置缓冲值(push文件较大时导致错误) \.git\config [ ...

  4. jqGrid怎么设置初始化页面时不加载数据(不向服务器请求数据)

    最近做一些表格一直用到jqGrid,今天遇到一个问题: 1.就是页面加载的时候数据不显示,点击搜索才根据请求从服务器返回并显示内容. 2.默认不从服务器请求数据(不然在开发者工具下会显示请求不到数据的 ...

  5. Spring MVC 解读——<mvc:annotation-driven/>

    Spring MVC 解读——<mvc:annotation-driven/> 一.AnnotationDrivenBeanDefinitionParser 通常如果我们希望通过注解的方式 ...

  6. Elasticsearch教程(七) elasticsearch Insert 插入数据(Java)

    首先我不赞成再采用一些中间件(jar包)来解决和 Elasticsearch  之间的交互,比如 Spring-data-elasticsearch.jar 系列一样,用就得依赖它.而 Elastic ...

  7. HTML5 Canvas 画钟表

    画钟表是2D画图的老生常谈,我也不能免俗弄了一个.代码如下: <!DOCTYPE html> <html lang="utf-8"> <meta ht ...

  8. CCF计算机职业资格认证 2015年3月第2题 数字排序 解法和思路

    问题描写叙述 给定n个整数,请统计出每一个整数出现的次数.按出现次数从多到少的顺序输出. 输入格式 输入的第一行包括一个整数n,表示给定数字的个数. 第二行包括n个整数.相邻的整数之间用一个空格分隔, ...

  9. red5截屏

    在red5-web.xml添加 <bean id="rtmpSampleAccess" class="org.red5.server.stream.RtmpSamp ...

  10. JavaScript 匹配浮点数

    //匹配整数,正小数 appUtils.bindEvent($(_pageId+" .remind-list-con input"),function(){ var reg = / ...