spring cloud Config源码解析(五)

阅读更多

了解完spring.cloud.config.client模块的代码,现在来了解下spring.cloud.config.monitor模块的代码。通过spring.factories启动文件可用看到只配置了一个自动配置类EnvironmentMonitorAutoConfiguration,我我们来看下这个类。

 进入到EnvironmentMonitorAutoConfiguration类,通过头部的注解可以了解到它是一个配置类,并且只能运行在WebApplication环境,然后通过注解@Import引入了FileMonitorConfiguration类。

 进入到FileMonitorConfiguration类,可以看到它实现了SmartLifecycle和ResourceLoaderAware接口,从这2个接口中可以看到一个是用于生命周期的管理,一个是用于资源文件的加载。通过FileMonitorConfiguration这个类的注解可以了解到它定义了一个定时任务。

 首先看看FileMonitorConfiguration类实现的SmartLifecycle接口的start()方法

public synchronized void start() {
   if (!this.running) {
      this.directory = getFileRepo();
      if (this.directory != null && !this.directory.isEmpty()) {
         log.info("Monitoring for local config changes: " + this.directory);
         try {
            this.watcher = FileSystems.getDefault().newWatchService();
            for (Path path : this.directory) {
               walkDirectory(path);
            }
         }
         catch (IOException e) {
         }
      }
      else {
         log.info("Not monitoring for local config changes");
      }
      this.running = true;
   }
}

 在初始化的时候字段running状态为false的时候通过getFileRepo()方法获取Set集合的文件路径

private Set getFileRepo() {
   if (this.scmRepository != null) {
      try {

         Resource resource = this.resourceLoader.getResource(this.scmRepository.getUri());
         if (resource instanceof FileSystemResource) {
            return Collections.singleton(Paths.get(resource.getURI()));
         }
      }
      catch (IOException e) {
         log.error("Cannot resolve URI for path: " + this.scmRepository.getUri());
      }
   }
   if (this.nativeEnvironmentRepository != null) {
      Set paths = new LinkedHashSet<>();
      for (String path : this.nativeEnvironmentRepository.getSearchLocations()) {
         Resource resource = this.resourceLoader.getResource(path);
         if (resource.exists()) {
            try {
               paths.add(Paths.get(resource.getURI()));
            }
            catch (Exception e) {
               log.error("Cannot resolve URI for path: " + path);
            }
         }
      }
      return paths;
   }
   return null;
}

在getFileRepo()方法中,它是通过ResourceLoader去加载配置的服务地址,返回获取的Resource信息,如果本地环境nativeEnvironmentRepository不为空,则去本地环境加载路径值。获取到path路径的Set集合后通过 FileSystems.getDefault().newWatchService()定义一个WatchService的监控类,然后循环path路径的Set集合,通过walkDirectory()方法来添加对文件的监控信息,git的文件不需要。

在看EnvironmentMonitorAutoConfiguration类,它定义了一个PropertyPathEndpoint的bean,这个bean引入了Bus模块的相关配置,因为这个类要配合bus模块来应用,我们进入到PropertyPathEndpoint类,可以看到它是一个RestController的API类,可以通过spring.cloud.config.monitor.endpoint.path自定义访问路径,然后加上monitor路径进行访问,同时它实现了ApplicationEventPublisherAware接口用于事件发布,我们看下它的2个path路径,都是通过POST方式访问的,但是notifyByForm()方法定义了consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE的访问条件,最终也是调用notifyByPath()方法,我们看下该方法

@RequestMapping(method = RequestMethod.POST)
public Set notifyByPath(@RequestHeader HttpHeaders headers,
      @RequestBody Map request) {
   PropertyPathNotification notification = this.extractor.extract(headers, request);
   if (notification != null) {

      Set services = new LinkedHashSet<>();

      for (String path : notification.getPaths()) {
         services.addAll(guessServiceName(path));
      }
      if (this.applicationEventPublisher != null) {
         for (String service : services) {
            log.info("Refresh for: " + service);
            this.applicationEventPublisher
                  .publishEvent(new RefreshRemoteApplicationEvent(this,
                        this.busId, service));
         }
         return services;
      }

   }
   return Collections.emptySet();
}

 在notifyByPath()方法中,通过PropertyPathNotificationExtractor类的具体实现类去实现extract()方法,我们主要看下它的实现类BasePropertyPathNotificationExtractor类,这个类主要是用于git相关的解析,我们看下extract()方法

public PropertyPathNotification extract(MultiValueMap headers,
        Map request) {
    if (requestBelongsToGitRepoManager(headers)) {
        if (request.get("commits") instanceof Collection) {
            Set paths = new HashSet<>();
            @SuppressWarnings("unchecked")
            Collection> commits = (Collection>) request
                    .get("commits");
            addPaths( paths, commits );
            if (!paths.isEmpty()) {
                return new PropertyPathNotification(paths.toArray(new String[0]));
            }
        }
    }
    return null;
}

 在extract()方法中,首先是通过requestBelongsToGitRepoManager()方法判断是否包含特定的推送信息,这里我们以GithubPropertyPathNotificationExtractor为例

protected boolean requestBelongsToGitRepoManager(MultiValueMap headers) {
    return "push".equals(headers.getFirst("X-Github-Event"));
}

 通过requestBelongsToGitRepoManager()方法判断头部中的X-Github-Event是否是push事件,然后获取request集合中的commits信息,将获取到的文件改变的推送路径加载到paths集合中,返回PropertyPathNotification对象。

   获取到PropertyPathNotification对象后,在PropertyPathEndpoint类的notifyByPath()方法中定义了一个services的LinkedHashSet集合,该services用来添加在PropertyPathNotification对象中获取到的path路径,我们看下guessServiceName()方法

private Set guessServiceName(String path) {
   Set services = new LinkedHashSet<>();
   if (path != null) {
      String stem = StringUtils
            .stripFilenameExtension(StringUtils.getFilename(StringUtils.cleanPath(path)));
      // TODO: correlate with service registry
      int index = stem.indexOf("-");
      while (index >= 0) {
         String name = stem.substring(0, index);
         String profile = stem.substring(index + 1);
         if ("application".equals(name)) {
            services.add("*:" + profile);
         }
         else if (!name.startsWith("application")) {
            services.add(name + ":" + profile);
         }
         index = stem.indexOf("-", index + 1);
      }
      String name = stem;
      if ("application".equals(name)) {
         services.add("*");
      }
      else if (!name.startsWith("application")) {
         services.add(name);
      }
   }
   return services;
}

在guessServiceName()方法中,首先对路径进行了消除获取到了字符串为stem的文件后缀的名字,然后通过一系列的判断对文件进行加*操作,用于事件发布的需要。退出guessServiceName()方法后,在PropertyPathEndpoint类的notifyByPath()方法中对services进行了事件的发布通过RefreshRemoteApplicationEvent事件。

  介绍完PropertyPathEndpoint类,我们在回头看看EnvironmentMonitorAutoConfiguration类中定义的static类PropertyPathNotificationExtractorConfiguration,在该类中定义了一系列的PropertyPathNotificationExtractor接口实现类,在这些类上面都定义了prefix的配置路径,默认上是都加载,具体可以参考前面提到的GithubPropertyPathNotificationExtractor类,这里不在一一说明。 

你可能感兴趣的:(spring,cloud,spring,cloud,config,spring,cloud,config,monitor)