0
点赞
收藏
分享

微信扫一扫

三. 微服务源码阅读-Config Center 源码

小安子啊 2022-03-12 阅读 35

6. Config Center 源码

分布式配置中心服务端

org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(ConfigServerConfiguration.Marker.class)
@EnableConfigurationProperties(ConfigServerProperties.class)
@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class,
      ResourceRepositoryConfiguration.class, ConfigServerEncryptionConfiguration.class,
      ConfigServerMvcConfiguration.class, ResourceEncryptorConfiguration.class })
public class ConfigServerAutoConfiguration {

}

接收获取配置信息请求的 Controller

org.springframework.cloud.config.server.config.ConfigServerMvcConfiguration

@Bean
@RefreshScope // 属性可以刷新
public EnvironmentController environmentController(
      EnvironmentRepository envRepository, ConfigServerProperties server) {
   EnvironmentController controller = new EnvironmentController(
         encrypted(envRepository, server), this.objectMapper);
   controller.setStripDocumentFromYaml(server.isStripDocumentFromYaml());
   controller.setAcceptEmpty(server.isAcceptEmpty());
   return controller;
}

这是其中一个请求

org.springframework.cloud.config.server.environment.EnvironmentController#properties

@RequestMapping("/{name}-{profiles}.properties")
public ResponseEntity<String> properties(@PathVariable String name,
                                         @PathVariable String profiles,
                                         @RequestParam(defaultValue = "true") boolean resolvePlaceholders)
    throws IOException {
    return labelledProperties(name, profiles, null, resolvePlaceholders);
}

public Environment labelled(@PathVariable String name, @PathVariable String profiles,
                            @PathVariable String label) {
    return getEnvironment(name, profiles, label, false);
}

// 获取环境数据
public Environment getEnvironment(String name, String profiles, String label,
                                  boolean includeOrigin) {
    if (name != null && name.contains("(_)")) {
        // "(_)" is uncommon in a git repo name, but "/" cannot be matched
        // by Spring MVC
        name = name.replace("(_)", "/");
    }
    if (label != null && label.contains("(_)")) {
        // "(_)" is uncommon in a git branch name, but "/" cannot be matched
        // by Spring MVC
        label = label.replace("(_)", "/");
    }
    // 去库中查询
    Environment environment = this.repository.findOne(name, profiles, label,
                                                      includeOrigin);
    if (!this.acceptEmpty
        && (environment == null || environment.getPropertySources().isEmpty())) {
        throw new EnvironmentNotFoundException("Profile Not found");
    }
    return environment;
}

在这里就从 github(gitee也可以) 上 clone,和更新配置信息并且保存到了本地

其实可以读取的位置很多, 我们可以从实现类看出来(组合,本地,db,redis)

EnvironmentRepository#findOne(String,String, String, boolean)

image-20220312011747778

当然我们要看的不是这个方法,是下面那个

AbstractScmEnvironmentRepository#findOne(String, String, String, boolean)

@Override
public synchronized Environment findOne(String application, String profile,
      String label, boolean includeOrigin) {
   NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(
         getEnvironment(), new NativeEnvironmentProperties());
    // 同步获取
   Locations locations = getLocations(application, profile, label);
   delegate.setSearchLocations(locations.getLocations());
    // 委托给NativeEnvironmentRepository执行
   Environment result = delegate.findOne(application, profile, "", includeOrigin);
   result.setVersion(locations.getVersion());
   result.setLabel(label);
   return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
         getUri());
}

org.springframework.cloud.config.server.environment.JGitEnvironmentRepository#getLocations

	@Override
	public synchronized Locations getLocations(String application, String profile,
			String label) {
		if (label == null) {
			label = this.defaultLabel;
		}
		String version = refresh(label); // 根据标签刷新
		return new Locations(application, profile, label, version,
				getSearchLocations(getWorkingDirectory(), application, profile, label));
	}

org.springframework.cloud.config.server.environment.JGitEnvironmentRepository#refresh

public String refresh(String label) {
   Git git = null;
   try {
      git = createGitClient();
      if (shouldPull(git)) {

org.springframework.cloud.config.server.environment.JGitEnvironmentRepository#createGitClient

private Git createGitClient() throws IOException, GitAPIException {
    File lock = new File(getWorkingDirectory(), ".git/index.lock");
    if (lock.exists()) {
        // The only way this can happen is if another JVM (e.g. one that
        // crashed earlier) created the lock. We can attempt to recover by
        // wiping the slate clean.
        this.logger.info("Deleting stale JGit lock file at " + lock);
        lock.delete();
    }
    if (new File(getWorkingDirectory(), ".git").exists()) {
        return openGitRepository();
    }
    else {
        return copyRepository(); // 复制
    }
}

private synchronized Git copyRepository() throws IOException, GitAPIException {
    deleteBaseDirIfExists();
    getBasedir().mkdirs();
    Assert.state(getBasedir().exists(), "Could not create basedir: " + getBasedir());
    if (getUri().startsWith(FILE_URI_PREFIX)) {
        return copyFromLocalRepository();
    }
    else {
        return cloneToBasedir(); // 克隆基本目录
    }
}


private Git cloneToBasedir() throws GitAPIException {
    CloneCommand clone = this.gitFactory.getCloneCommandByCloneRepository()
        .setURI(getUri()).setDirectory(getBasedir());
    configureCommand(clone);
    try {
        return clone.call(); // 执行克隆命令
    }
    catch (GitAPIException e) {
        this.logger.warn("Error occured cloning to base directory.", e);
        deleteBaseDirIfExists();
        throw e;
    }
}

服务端的加密 controller

org.springframework.cloud.config.server.config.ConfigServerEncryptionConfiguration

@Configuration(proxyBeanMethods = false)
public class ConfigServerEncryptionConfiguration {

   @Autowired(required = false)
   private TextEncryptorLocator encryptor;

   @Autowired
   private ConfigServerProperties properties;

   @Bean
   public EncryptionController encryptionController() { // 装配controller
      EncryptionController controller = new EncryptionController(this.encryptor);
      controller.setDefaultApplicationName(this.properties.getDefaultApplicationName());
      controller.setDefaultProfile(this.properties.getDefaultProfile());
      return controller;
   }
}

encrypt的mapping

EncryptionController#encrypt(java.lang.String, org.springframework.http.MediaType)

@RequestMapping(value = "encrypt", method = RequestMethod.POST)
public String encrypt(@RequestBody String data,
                      @RequestHeader("Content-Type") MediaType type) {
    return encrypt(defaultApplicationName, defaultProfile, data, type);
}

@RequestMapping(value = "/encrypt/{name}/{profiles}", method = RequestMethod.POST)
public String encrypt(@PathVariable String name, @PathVariable String profiles,
                      @RequestBody String data, @RequestHeader("Content-Type") MediaType type) {
    TextEncryptor encryptor = getEncryptor(name, profiles, "");
    validateEncryptionWeakness(encryptor);
    String input = stripFormData(data, type, false);
    Map<String, String> keys = helper.getEncryptorKeys(name, profiles, input);
    String textToEncrypt = helper.stripPrefix(input);
    String encrypted = helper.addPrefix(keys,
                                        encryptorLocator.locate(keys).encrypt(textToEncrypt));
    logger.info("Encrypted data");
    return encrypted;
}

Config客户端

org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration

@Bean
@ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class)
@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
public ConfigServicePropertySourceLocator configServicePropertySource(
      ConfigClientProperties properties) {
   ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
         properties);
   return locator;
}

org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#locate

这个方法就发起了对 config 服务端的调用获取服务列表

for (String label : labels) {
    // 获取远程的环境信息
   Environment result = getRemoteEnvironment(restTemplate, properties,
         label.trim(), state);
   if (result != null) {
      log(result);
...
      return composite;
   }
}

org.springframework.cloud.config.client.ConfigServicePropertySourceLocator#getRemoteEnvironment

for (int i = 0; i < noOfUrls; i++) {
   Credentials credentials = properties.getCredentials(i);
   String uri = credentials.getUri();
   String username = credentials.getUsername();
   String password = credentials.getPassword();

   logger.info("Fetching config from server at : " + uri);

   try {
      HttpHeaders headers = new HttpHeaders();
      headers.setAccept(
            Collections.singletonList(MediaType.parseMediaType(V2_JSON)));
      addAuthorizationToken(properties, headers, username, password);
      if (StringUtils.hasText(token)) {
         headers.add(TOKEN_HEADER, token);
      }
      if (StringUtils.hasText(state) && properties.isSendState()) {
         headers.add(STATE_HEADER, state);
      }

      final HttpEntity<Void> entity = new HttpEntity<>((Void) null, headers); // 封装成entity
      response = restTemplate.exchange(uri + path, HttpMethod.GET, entity,
            Environment.class, args);
   }
   catch (HttpClientErrorException e) {
   }

   if (response == null || response.getStatusCode() != HttpStatus.OK) {
      return null;
   }

   Environment result = response.getBody();
   return result;
}

配置的动态刷新

当调用 http://localhost:8080/actuator/refresh 接口的时候就会调用到这个类

@Endpoint(id = "refresh")
public class RefreshEndpoint {

   private ContextRefresher contextRefresher;

   public RefreshEndpoint(ContextRefresher contextRefresher) {
      this.contextRefresher = contextRefresher;
   }

   @WriteOperation
   public Collection<String> refresh() { // 刷新
      Set<String> keys = this.contextRefresher.refresh();
      return keys;
   }

}

org.springframework.cloud.context.refresh.ContextRefresher#refresh

public synchronized Set<String> refresh() {
   Set<String> keys = refreshEnvironment();
   this.scope.refreshAll();
   return keys;
}

先更新 spring 容器的属性,然后再更新 @RefreshScope 注解的类刷新,其实就是自定义 scope,由自己维护实例

举报

相关推荐

0 条评论