Apollo源码解析——ConfigService配置读取接口(客户端定时拉取接口)


一、概述

上文讲了客户端定时轮询的接口,这里我们看下Config Service配置读取的接口的实现。

二、代码流程

1. ConfigController#queryConfig方法

代码很长,但是大体流程就是根据客户端的参数从服务端查询出来最新的配置,然后根据clientSideReleaseKey和服务端的ReleaseKey进行比较,判断配置有没有更新,这里重点看下loadConfig方法,怎么加载最新的配置

  @GetMapping(value = "/{appId}/{clusterName}/{namespace:.+}")
  public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
                                  @PathVariable String namespace,
                                  @RequestParam(value = "dataCenter", required = false) String dataCenter,
                                  @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
                                  @RequestParam(value = "ip", required = false) String clientIp,
                                  @RequestParam(value = "messages", required = false) String messagesAsString,
                                  HttpServletRequest request, HttpServletResponse response) throws IOException {
    String originalNamespace = namespace;
    //strip out .properties suffix
    // 若 Namespace 名以 .properties 结尾, 移除该结尾,并设置到 ApolloConfigNotification中
    namespace = namespaceUtil.filterNamespaceName(namespace);
    //fix the character case issue, such as FX.apollo <-> fx.apollo
    // 获得归一化的 Namespace 名字。因为,客户端 Namespace 会填错大消息
    namespace = namespaceUtil.normalizeNamespace(appId, namespace);

    // 若 clientIp 未提交,从 Request 中获取
    if (Strings.isNullOrEmpty(clientIp)) {
      clientIp = tryToGetClientIp(request);
    }

    // 解析 messageAsString 参数,创建 ApolloNotificationMessages 对象
    ApolloNotificationMessages clientMessages = transformMessages(messagesAsString);

    // 获取 Release 数组
    List<Release> releases = Lists.newLinkedList();

    // 获得 Namespace 对应的 Release 对象
    String appClusterNameLoaded = clusterName;
    if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
      // 获取 release 对象
      Release currentAppRelease = configService.loadConfig(appId, clientIp, appId, clusterName, namespace,
          dataCenter, clientMessages);

      if (currentAppRelease != null) {
        // 添加到 Release 数组中
        releases.add(currentAppRelease);
        //we have cluster search process, so the cluster name might be overridden
        // 获得 Release 对应的Cluster 名字
        appClusterNameLoaded = currentAppRelease.getClusterName();
      }
    }

    //if namespace does not belong to this appId, should check if there is a public configuration
    // 若 Namespace 未关联类型,则获取关联的 namespace 的 Release 对象
    if (!namespaceBelongsToAppId(appId, namespace)) {
      // 获得 Release 对象
      Release publicRelease = this.findPublicConfig(appId, clientIp, clusterName, namespace,
              dataCenter, clientMessages);
      // 添加到 Release 数组中
      if (Objects.nonNull(publicRelease)) {
        releases.add(publicRelease);
      }
    }

    // 若获得不到 Release , 返回状态码为 404 的响应
    if (releases.isEmpty()) {
      response.sendError(HttpServletResponse.SC_NOT_FOUND,
          String.format(
              "Could not load configurations with appId: %s, clusterName: %s, namespace: %s",
              appId, clusterName, originalNamespace));
      Tracer.logEvent("Apollo.Config.NotFound",
          assembleKey(appId, clusterName, originalNamespace, dataCenter));
      return null;
    }

    // 记录 InstanceConfig
    auditReleases(appId, clusterName, dataCenter, clientIp, releases);

    // 计算 Config Service 的合并 ReleaseKey
    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)
            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));

    // 对于 Client 的合并 Release key 。 若相等,说明没有改变,返回状态码为 302 的响应
    if (mergedReleaseKey.equals(clientSideReleaseKey)) {
      // Client side configuration is the same with server side, return 304
      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      Tracer.logEvent("Apollo.Config.NotModified",
          assembleKey(appId, appClusterNameLoaded, originalNamespace, dataCenter));
      return null;
    }

    // 创建 ApolloConfig 对象
    ApolloConfig apolloConfig = new ApolloConfig(appId, appClusterNameLoaded, originalNamespace,
            mergedReleaseKey);
    // 合并 Release 的配置,并将结果设置到 ApolloConfig中
    apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));

    Tracer.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded,
        originalNamespace, dataCenter));
    return apolloConfig;
  }

2. AbstractConfigService#loadConfig方法

在方法中会继续调用findRelease来查找对应配置最新的Release版本

  @Override
  public Release loadConfig(String clientAppId, String clientIp, String configAppId, String configClusterName,
      String configNamespace, String dataCenter, ApolloNotificationMessages clientMessages) {
    // load from specified cluster first
    if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, configClusterName)) {
      Release clusterRelease = findRelease(clientAppId, clientIp, configAppId, configClusterName, configNamespace,
          clientMessages);

      if (Objects.nonNull(clusterRelease)) {
        return clusterRelease;
      }
    }

    // try to load via data center
    if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, configClusterName)) {
      Release dataCenterRelease = findRelease(clientAppId, clientIp, configAppId, dataCenter, configNamespace,
          clientMessages);
      if (Objects.nonNull(dataCenterRelease)) {
        return dataCenterRelease;
      }
    }

    // fallback to default release
    return findRelease(clientAppId, clientIp, configAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, configNamespace,
        clientMessages);
  }
  1. AbstractConfigService#findRelease方法

这里会判断是不是灰度,如果是灰度调用findActiveOne方法,不是灰度,调用findLatestActiveRelease方法,下面有两个不同的实现DefaultConfigServiceConfigServiceWithCache

  private Release findRelease(String clientAppId, String clientIp, String configAppId, String configClusterName,
      String configNamespace, ApolloNotificationMessages clientMessages) {
    // 读取灰度发布编号
    Long grayReleaseId = grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(clientAppId, clientIp, configAppId,
        configClusterName, configNamespace);

    Release release = null;

    // 读取灰度 Release 对象
    if (grayReleaseId != null) {
      release = findActiveOne(grayReleaseId, clientMessages);
    }

    //非灰度,获取最新的,并且有效的 Release 对象
    if (release == null) {
      release = findLatestActiveRelease(configAppId, configClusterName, configNamespace, clientMessages);
    }

    return release;
  }
  1. DefaultConfigService

这里其实可以看出就是直接去数据库中查询配置最新的版本了


  @Autowired
  private ReleaseService releaseService;

  @Override
  protected Release findActiveOne(long id, ApolloNotificationMessages clientMessages) {
    return releaseService.findActiveOne(id);
  }

  @Override
  protected Release findLatestActiveRelease(String configAppId, String configClusterName, String configNamespace,
                                            ApolloNotificationMessages clientMessages) {
    return releaseService.findLatestActiveRelease(configAppId, configClusterName,
        configNamespace);
  }

  @Override
  public void handleMessage(ReleaseMessage message, String channel) {
    // since there is no cache, so do nothing
  }

5.ConfigServiceWithCache

基于Guava Cache的实现类

public class ConfigServiceWithCache extends AbstractConfigService {
  private static final Logger logger = LoggerFactory.getLogger(ConfigServiceWithCache.class);
  private static final long DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES = 60;//1 hour
  private static final String TRACER_EVENT_CACHE_INVALIDATE = "ConfigCache.Invalidate";
  private static final String TRACER_EVENT_CACHE_LOAD = "ConfigCache.LoadFromDB";
  private static final String TRACER_EVENT_CACHE_LOAD_ID = "ConfigCache.LoadFromDBById";
  private static final String TRACER_EVENT_CACHE_GET = "ConfigCache.Get";
  private static final String TRACER_EVENT_CACHE_GET_ID = "ConfigCache.GetById";
  private static final Splitter STRING_SPLITTER =
      Splitter.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).omitEmptyStrings();

  @Autowired
  private ReleaseService releaseService;

  @Autowired
  private ReleaseMessageService releaseMessageService;

  private LoadingCache<String, ConfigCacheEntry> configCache;

  private LoadingCache<Long, Optional<Release>> configIdCache;

  private ConfigCacheEntry nullConfigCacheEntry;
  1. ConfigServiceWithCache#initialize方法

初始化缓存对象

 @PostConstruct
  void initialize() {
    // 初始化 configCache
    configCache = CacheBuilder.newBuilder()
        .expireAfterAccess(DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES, TimeUnit.MINUTES)
        .build(new CacheLoader<String, ConfigCacheEntry>() {
          @Override
          public ConfigCacheEntry load(String key) throws Exception {
            //格式不正确,返回 nullConfigCacheEntry
            List<String> namespaceInfo = STRING_SPLITTER.splitToList(key);
            if (namespaceInfo.size() != 3) {
              Tracer.logError(
                  new IllegalArgumentException(String.format("Invalid cache load key %s", key)));
              return nullConfigCacheEntry;
            }

            Transaction transaction = Tracer.newTransaction(TRACER_EVENT_CACHE_LOAD, key);
            try {
              // 获取最新的 ReleaseMessage 对象
              ReleaseMessage latestReleaseMessage = releaseMessageService.findLatestReleaseMessageForMessages(Lists
                  .newArrayList(key));
              // 获得最新的,并且有效的 Release 对象
              Release latestRelease = releaseService.findLatestActiveRelease(namespaceInfo.get(0), namespaceInfo.get(1),
                  namespaceInfo.get(2));

              transaction.setStatus(Transaction.SUCCESS);

              // 获得通知编号
              long notificationId = latestReleaseMessage == null ? ConfigConsts.NOTIFICATION_ID_PLACEHOLDER : latestReleaseMessage
                  .getId();

              //若 latestReleaseMessage 和 lasestReleaseMessage 都为空,返回 nullConfigCacheEntry
              if (notificationId == ConfigConsts.NOTIFICATION_ID_PLACEHOLDER && latestRelease == null) {
                return nullConfigCacheEntry;
              }

              //创建 ConfigCacheEntry
              return new ConfigCacheEntry(notificationId, latestRelease);
            } catch (Throwable ex) {
              transaction.setStatus(ex);
              throw ex;
            } finally {
              transaction.complete();
            }
          }
        });
    // 初始化 configIdCache
    configIdCache = CacheBuilder.newBuilder()
        .expireAfterAccess(DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES, TimeUnit.MINUTES)
        .build(new CacheLoader<Long, Optional<Release>>() {
          @Override
          public Optional<Release> load(Long key) throws Exception {
            Transaction transaction = Tracer.newTransaction(TRACER_EVENT_CACHE_LOAD_ID, String.valueOf(key));
            try {
              // 获得 Release 对象
              Release release = releaseService.findActiveOne(key);

              transaction.setStatus(Transaction.SUCCESS);

              return Optional.ofNullable(release);
            } catch (Throwable ex) {
              transaction.setStatus(ex);
              throw ex;
            } finally {
              transaction.complete();
            }
          }
        });
  }
  1. ConfigServiceWithCache#handleMessage方法

这里发现他也监听了配置变更的消息,然后清空对应的缓存,重新从db中load

  public void handleMessage(ReleaseMessage message, String channel) {
    logger.info("message received - channel: {}, message: {}", channel, message);
    if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(message.getMessage())) {
      return;
    }

    try {
      //清空对应的缓存
      invalidate(message.getMessage());

      //warm up the cache
      // 预热缓存,读取 ConfigCacheEntry 对象 ,重新从 DB 中加载
      configCache.getUnchecked(message.getMessage());
    } catch (Throwable ex) {
      //ignore
    }
  }
  1. ConfigServiceWithCache#findLatestActiveRelease方法

首先从缓存中读取,然后根据通知编号比较,如果客户端比较大,说明缓存已经过期了,那么清空对应的缓存,重新从db中加载返回

  protected Release findLatestActiveRelease(String appId, String clusterName, String namespaceName,
                                            ApolloNotificationMessages clientMessages) {
    //根据 appId + clusterName + namesapceName ,获得 ReleaseMessage 的 'message'
    String key = ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName);

    Tracer.logEvent(TRACER_EVENT_CACHE_GET, key);

    // 从缓存 configCache 中,读取 ConfigCacheEntry对象
    ConfigCacheEntry cacheEntry = configCache.getUnchecked(key);

    //cache is out-dated
    //若客户端的通知编号更大,说明缓存已经过期
    if (clientMessages != null && clientMessages.has(key) &&
            clientMessages.get(key) > cacheEntry.getNotificationId()) {
      //invalidate the cache and try to load from db again
      // 清空对应的缓存
      invalidate(key);
      //读取 ConfigCacheEntry 对象,重新从 DB 中加载
      cacheEntry = configCache.getUnchecked(key);
    }
  1. ConfigServiceWithCache#findActiveOne方法

这里直接从缓存中读取

 protected Release findActiveOne(long id, ApolloNotificationMessages clientMessages) {
    Tracer.logEvent(TRACER_EVENT_CACHE_GET_ID, String.valueOf(id));
    // 从缓存 configIdCache 中,读取 Release 对象
    return configIdCache.getUnchecked(id).orElse(null);
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Apollo是一个开源的自动驾驶平台,提供了多种语言接口开发者使用。其中,Apollo Python接口是用于实现客户端的一种方式。 通过Apollo Python接口开发者可以使用Python语言来与Apollo平台进行交互,实现自动驾驶相关的功能。Python接口提供了一系列的类和方法,用于获取传感器数据、发送控制指令、进行路径规划等操作。 具体来说,Apollo Python接口实现了以下功能: 1. 获取传感器数据:通过接口可以获取车辆的激光雷达、摄像头、GPS等传感器数据,用于感知周围环境。 2. 发送控制指令:可以通过接口发送控制指令,如加速、刹车、转向等,控制车辆的行驶。 3. 路径规划:可以使用接口进行路径规划,根据当前位置和目标位置计算出最优路径。 4. 地图数据查询:可以查询地图数据,包括道路信息、交通标志等,用于辅助决策和规划路径。 使用Apollo Python接口实现客户端需要按照以下步骤进行: 1. 安装Apollo开发环境:首先需要安装Apollo开发环境,并配置好相关依赖。 2. 导入Python接口库:在Python代码中导入Apollo Python接口库,以便使用其中的类和方法。 3. 初始化接口:在代码中初始化Apollo Python接口,连接到Apollo平台。 4. 使用接口功能:根据需求使用接口提供的功能,如获取传感器数据、发送控制指令等。 5. 关闭接口:在程序结束时关闭Apollo Python接口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值