apollo客户端-首次获取server数据?

......

public class ApolloApplicationContextInitializer implements 
public class ApolloApplicationContextInitializer implements
// 这个接口允许在ConfigurableApplicationContext(如AnnotationConfigApplicationContext或XmlWebApplicationContext等)刷新(refresh)之前对其进行初始化操作。
// 可以在应用上下文启动但还未加载 bean 定义之前,对上下文进行自定义的设置和初始化操作
ApplicationContextInitializer<ConfigurableApplicationContext>, 
// 这个接口允许在 Spring 应用的Environment准备好之后对其进行进一步的处理和定制。
// 可以在 Spring 应用上下文加载环境变量、系统属性和配置文件等之后,对Environment对象进行额外的配置和调整。
EnvironmentPostProcessor, 
Ordered 
{
    // 默认顺序
    public static final int DEFAULT_ORDER = 0;
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
    // namespace分割
    private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
    // apollo系统特性
    private static final String[] APOLLO_SYSTEM_PROPERTIES = new String[]{
        "app.id",
        "apollo.cluster",
        "apollo.cacheDir", 
        "apollo.accesskey.secret", 
        "apollo.meta", 
        "apollo.property.order.enable"
    };
    // 拿到一个线程安全的配置工厂对象
    private final ConfigPropertySourceFactory configPropertySourceFactory = (ConfigPropertySourceFactory)SpringInjector.getInstance(ConfigPropertySourceFactory.class);
    private int order = 0;

    public ApolloApplicationContextInitializer() {
    }

    // ApolloApplicationContextInitializer在initialize方法中对应用上下文进行初始化操作
    @Override
    public void initialize( /**当前应用上下文**/ConfigurableApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        if (!(Boolean)environment.getProperty("apollo.bootstrap.enabled", Boolean.class, false)) {
            logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, "apollo.bootstrap.enabled");
        } else {
            logger.debug("Apollo bootstrap config is enabled for context {}", context);
            this.initialize(environment);
        }
    }

    // 当前累自定义的方法:initialize
    protected void initialize(/**当前应用的配置环境**/ConfigurableEnvironment environment) {
        // 检测是否已经有了组合属性对象(经过处理话后,会在环境对象中创建一个,没有则说明尚未完成初始化)
        if (!environment.getPropertySources().contains("ApolloBootstrapPropertySources")) {
            // 获取配置的apollo的namespace,默认为:application
            String namespaces = environment.getProperty("apollo.bootstrap.namespaces", "application");
            logger.debug("Apollo bootstrap namespaces: {}", namespaces);
            // 按照英文:“,”进行分割,拿到命名空间集合
            List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
            
            // 创建一个组合属性对象
            CompositePropertySource composite = new CompositePropertySource("ApolloBootstrapPropertySources");

            // 拿到迭代器:准备迭代处理
            Iterator i$ = namespaceList.iterator();
            while(i$.hasNext()) {
                // 命名空间名称
                String namespace = (String)i$.next();
                // 拿到具体的配置(重点逻辑:这里面有从apollo-server获取陪配置数据的逻辑)
                Config config = ConfigService.getConfig(namespace);
                // 往组合属性对象中丢数据
                composite.addPropertySource(this.configPropertySourceFactory.getConfigPropertySource(namespace, config));
            }
            // 最终数据打包丢到环境对象中
            environment.getPropertySources().addFirst(composite);
        }
    }

    void initializeSystemProperty(/**当前应用的配置环境**/ConfigurableEnvironment environment) {
        String[] arr$ = APOLLO_SYSTEM_PROPERTIES;
        int len$ = arr$.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            String propertyName = arr$[i$];
            this.fillSystemPropertyFromEnvironment(environment, propertyName);
        }

    }

    private void fillSystemPropertyFromEnvironment(/**当前应用的配置环境**/ConfigurableEnvironment environment, 
                                                   String propertyName) {
        if (System.getProperty(propertyName) == null) {
            String propertyValue = environment.getProperty(propertyName);
            if (!Strings.isNullOrEmpty(propertyValue)) {
                System.setProperty(propertyName, propertyValue);
            }
        }
    }

    // 在postProcessEnvironment方法中对环境进行处理
    // 执行时间节点:高于initialize( /**当前应用上下文**/ConfigurableApplicationContext context)
    @Override
    public void postProcessEnvironment(/**当前应用的配置环境**/ConfigurableEnvironment configurableEnvironment, 
                                       SpringApplication springApplication) {
        // 初始化系统参数
        this.initializeSystemProperty(configurableEnvironment);
        // # 表示启用急切加载。
        // 急切加载意味着在应用启动时,Apollo 客户端会尽快加载配置,而不是在首次访问配置时才加载。这可以确保应用在启动过程中尽早获取到所需的配置信息,减少潜在的延迟和不确定性。
        Boolean eagerLoadEnabled = (Boolean)configurableEnvironment.getProperty("apollo.bootstrap.eagerLoad.enabled", Boolean.class, false);
        if (eagerLoadEnabled) {
            //  # 启用 Apollo 的引导程序,这是使 Apollo 客户端在应用启动时生效的关键设置。
            Boolean bootstrapEnabled = (Boolean)configurableEnvironment.getProperty("apollo.bootstrap.enabled", Boolean.class, false);
            if (bootstrapEnabled) {
                // 只有配置了对应的参数,才会执行初始化逻辑
                this.initialize(configurableEnvironment);
            }

        }
    }

    // 通过getOrder方法返回一个执行顺序值
    @Override
    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}

触发顺序:

EnvironmentPostProcessor

  1. postProcessEnvironment(**) [ApolloApplicationContextInitializer.java]
  2. this.initialize(configurableEnvironment) [ApolloApplicationContextInitializer.java]
  3. Config config = ConfigService.getConfig(namespace) [ApolloApplicationContextInitializer.java]
  4. Config getConfig(String namespace) [ConfigService.java]
  5. getConfig(String namespace) [DefaultConfigManager.java]
    • factory.create(namespace)
  1. Config create(String namespace) [DefaultConfigFactory.java]
    • new DefaultConfig(namespace, this.createLocalConfigRepository(namespace)) :
      • this.createLocalConfigRepository(namespace) :
        • new LocalFileConfigRepository(*,this.createRemoteConfigRepository(namespace));
          • createRemoteConfigRepository(String namespace)
            • 创建RemoteConfigRepository对象,启动各种触发机制
              1. RemoteConfigRepository(String namespace) [RemoteConfigRepository.java]
                • this.trySync();
                • this.scheduleLongPollingRefresh();
    • 最后DefaultConfig 类型包装一下
  1. 数据刷新到环境中
    • composite.addPropertySource(this.configPropertySourceFactory.getConfigPropertySource(namespace, config));

environment.getPropertySources().addFirst(composite);

public RemoteConfigRepository(String namespace) {
        // 命名空间
        this.m_namespace = namespace;
        //
        this.m_configCache = new AtomicReference();
        // 
        this.m_configUtil = (ConfigUtil)ApolloInjector.getInstance(ConfigUtil.class);
        this.m_httpUtil = (HttpUtil)ApolloInjector.getInstance(HttpUtil.class);
        this.m_serviceLocator = (ConfigServiceLocator)ApolloInjector.getInstance(ConfigServiceLocator.class);
        // 获取远程配置数据:LongPoll实时通讯交互服务,apollo动态刷新就靠它了~(定义一个服务,后面有逻辑触发)
        this.remoteConfigLongPollService = (RemoteConfigLongPollService)ApolloInjector.getInstance(RemoteConfigLongPollService.class);
        this.m_longPollServiceDto = new AtomicReference();
        this.m_remoteMessages = new AtomicReference();
        // 限流
        this.m_loadConfigRateLimiter = RateLimiter.create((double)this.m_configUtil.getLoadConfigQPS());
        this.m_configNeedForceRefresh = new AtomicBoolean(true);
        this.m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(this.m_configUtil.getOnErrorRetryInterval(), this.m_configUtil.getOnErrorRetryInterval() * 8L);
        this.gson = new Gson();
        // 触发一次同步操作:拉取一次数据(apollo客户端首次拉取配置数据)
        this.trySync();
        // 刷新任务:主动拉取数据
        this.schedulePeriodicRefresh();
        // 实时刷新任务:启动longpolling机制
        this.scheduleLongPollingRefresh();
    }
    private void schedulePeriodicRefresh() {
        logger.debug("Schedule periodic refresh with interval: {} {}", this.m_configUtil.getRefreshInterval(), this.m_configUtil.getRefreshIntervalTimeUnit());
        m_executorService.scheduleAtFixedRate(
            // 定时任务逻辑
            new Runnable() {
                    public void run() {
                        Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", RemoteConfigRepository.this.m_namespace));
                        RemoteConfigRepository.logger.debug("refresh config for namespace: {}", RemoteConfigRepository.this.m_namespace);
                        // 主动获取配置数据
                        // 使用抽象父类中的trySync(),触发当前类的:this.sync(); (后文会详细说明这块)
                        RemoteConfigRepository.this.trySync();
                        Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
                    }
            },
            (long)this.m_configUtil.getRefreshInterval(),  // 查看配置为5s,可以通过:apollo.refreshInterval配置调整,首次延迟时间
            (long)this.m_configUtil.getRefreshInterval(),  // 同上 ,以后执行间隔时间频率
            this.m_configUtil.getRefreshIntervalTimeUnit()   // 单位:分钟,固定 TimeUnit.MINUTES;
        );
    }
private void scheduleLongPollingRefresh() {
    // 触发LongPolling实时通讯机制
    this.remoteConfigLongPollService.submit(this.m_namespace, this);
}
public class RemoteConfigLongPollService {
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLongPollService.class);
    private static final Joiner STRING_JOINER = Joiner.on("+");
    private static final MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("=");
    private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();
    private static final long INIT_NOTIFICATION_ID = -1L;
    // 读超时
    private static final int LONG_POLLING_READ_TIMEOUT = 90000;
    // 一个单线程的执行器
    private final ExecutorService m_longPollingService = Executors.newSingleThreadExecutor(ApolloThreadFactory.create("RemoteConfigLongPollService", true));
    private final AtomicBoolean m_longPollingStopped = new AtomicBoolean(false);
    // 策略
    private SchedulePolicy m_longPollFailSchedulePolicyInSecond = new ExponentialSchedulePolicy(1L, 120L);
    // 限流
    private RateLimiter m_longPollRateLimiter;
    private final AtomicBoolean m_longPollStarted = new AtomicBoolean(false);
    // 从apollo服务端拿到的不同namespace的结果数据集合:线程安全
    private final Multimap<String, RemoteConfigRepository> m_longPollNamespaces = Multimaps.synchronizedSetMultimap(HashMultimap.create());
    // 通知信息:<{namespaceName},{notificationId}>
    private final ConcurrentMap<String, Long> m_notifications = Maps.newConcurrentMap();
    // 通知内容
    private final Map<String, ApolloNotificationMessages> m_remoteNotificationMessages = Maps.newConcurrentMap();
    // apollo服务端请求数据应答类型
    private Type m_responseType = (new TypeToken<List<ApolloConfigNotification>>() {}).getType();
    private Gson gson = new Gson();
    // 
    private ConfigUtil m_configUtil = (ConfigUtil)ApolloInjector.getInstance(ConfigUtil.class);
    private HttpUtil m_httpUtil = (HttpUtil)ApolloInjector.getInstance(HttpUtil.class);
    // 注册中心服务:获取可用的apollo服务侧的机器信息,用于http通信时使用
    // ConfigServiceLocator内置机制:
    // (1)schedulePeriodicRefresh():每隔5min刷新一次,注册中心服务列表的数据,可以通过:apollo.refreshInterval配置调整时间间隔和频率
    private ConfigServiceLocator m_serviceLocator = (ConfigServiceLocator)ApolloInjector.getInstance(ConfigServiceLocator.class);

    // 构造器
    public RemoteConfigLongPollService() {
        // 搞一个限流器
        // 默认QPS=2
        this.m_longPollRateLimiter = RateLimiter.create((double)this.m_configUtil.getLongPollQPS());
    }
    
    // 提交的任务从这里开始
    public boolean submit(String namespace, 	// 命名空间
                          RemoteConfigRepository remoteConfigRepository		// 上文传递过来的操作对象:this
     ) {
        // 保存当前要查询的namespace数据,主要是有上下文数据,为了数据安全一致,不会出现错乱问题
        boolean added = this.m_longPollNamespaces.put(namespace, remoteConfigRepository);
        this.m_notifications.putIfAbsent(namespace, -1L);
        // 如果尚未启动任务,则立即启动
        if (!this.m_longPollStarted.get()) {
            // 任务启动
            this.startLongPolling();
        }
        return added;
    }

    // 任务核心逻辑
    private void startLongPolling() {
        // 原子操作:更新任务状态:true
        if (this.m_longPollStarted.compareAndSet(false, true)) {
            try {
                // 从配置对象中拿到相关apollo配置参数,可以从ConfigUtil.java找到控制参数
                final String appId = this.m_configUtil.getAppId();
                final String cluster = this.m_configUtil.getCluster();
                final String dataCenter = this.m_configUtil.getDataCenter();
                final String secret = this.m_configUtil.getAccessKeySecret();
                // 参数:apollo.longPollingInitialDelayInMills,默认2000L
                final long longPollingInitialDelayInMills = this.m_configUtil.getLongPollingInitialDelayInMills();
                // 一个单线程的执行器
                this.m_longPollingService.submit(new Runnable() {
                    public void run() {
                        if (longPollingInitialDelayInMills > 0L) {
                            try {
                                RemoteConfigLongPollService.logger.debug("Long polling will start in {} ms.", longPollingInitialDelayInMills);
                                // sleep休眠的方式,控制刷新频率
                                TimeUnit.MILLISECONDS.sleep(longPollingInitialDelayInMills);
                            } catch (InterruptedException var2) {
                            }
                        }
                        
                        // 开始从apollo获取配置数据
                        RemoteConfigLongPollService.this.doLongPollingRefresh(appId, cluster, dataCenter, secret);
                    }
                });
            } catch (Throwable var7) {
                this.m_longPollStarted.set(false);
                ApolloConfigException exception = new ApolloConfigException("Schedule long polling refresh failed", var7);
                Tracer.logError(exception);
                logger.warn(ExceptionUtil.getDetailMessage(exception));
            }

        }
    }

    // 任务关停
    void stopLongPollingRefresh() {
        this.m_longPollingStopped.compareAndSet(false, true);
    }

    // 从apollo获取配置数据
    private void doLongPollingRefresh(String appId, String cluster, String dataCenter, String secret) {
        Random random = new Random();
        // 通信目标服务信息
        ServiceDTO lastServiceDto = null;

        // 非停止状态,当前线程正常运行的情况下
        while(!this.m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
            // 限流机制(默认2次)
            if (!this.m_longPollRateLimiter.tryAcquire(5L, TimeUnit.SECONDS)) {
                try {
                    TimeUnit.SECONDS.sleep(5L);
                } catch (InterruptedException var21) {
                }
            }

            // 事务控制(Tracer追踪工具)
            Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "pollNotification");
            String url = null;

            try {
                if (lastServiceDto == null) {
                    // 从注册中心拿到机器注册列表
                    List<ServiceDTO> configServices = this.getConfigServices();
                    // 随便选举一个
                    lastServiceDto = (ServiceDTO)configServices.get(random.nextInt(configServices.size()));
                }

                // 组装目标服务的url
                url = this.assembleLongPollRefreshUrl(lastServiceDto.getHomepageUrl(), appId, cluster, dataCenter, this.m_notifications);
                logger.debug("Long polling from {}", url);
                // 组装request
                HttpRequest request = new HttpRequest(url);
                request.setReadTimeout(90000);
                // 密钥数据配置
                if (!StringUtils.isBlank(secret)) {
                    Map<String, String> headers = Signature.buildHttpHeaders(url, appId, secret);
                    request.setHeaders(headers);
                }

                transaction.addData("Url", url);
                // 主动发起GET请求,获取通知url:notifications/v2/***
                HttpResponse<List<ApolloConfigNotification>> response = this.m_httpUtil.doGet(request, this.m_responseType);
                logger.debug("Long polling response: {}, url: {}", response.getStatusCode(), url);
                // 结果数据检测&处理
                // 成功
                if (response.getStatusCode() == 200 && response.getBody() != null) {
                    // 收集通知目标数据·1
                    this.updateNotifications((List)response.getBody());
                    // 收集通知目标数据·2
                    this.updateRemoteNotifications((List)response.getBody());
                    transaction.addData("Result", ((List)response.getBody()).toString());
                    // 收集完成后,开始通知操作
                    this.notify(lastServiceDto, (List)response.getBody());
                }

                // 异常
                if (response.getStatusCode() == 304 && random.nextBoolean()) {
                    lastServiceDto = null;
                }
                
                // 一次longpoll完成
                this.m_longPollFailSchedulePolicyInSecond.success();
                transaction.addData("StatusCode", response.getStatusCode());
                transaction.setStatus("0");
            } catch (Throwable var19) {
                lastServiceDto = null;
                Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(var19));
                transaction.setStatus(var19);
                // 失败时,有自己的休眠时间
                long sleepTimeInSecond = this.m_longPollFailSchedulePolicyInSecond.fail();
                logger.warn("Long polling failed, will retry in {} seconds. appId: {}, cluster: {}, namespaces: {}, long polling url: {}, reason: {}", new Object[]{sleepTimeInSecond, appId, cluster, this.assembleNamespaces(), url, ExceptionUtil.getDetailMessage(var19)});

                try {
                    // 休息一会
                    TimeUnit.SECONDS.sleep(sleepTimeInSecond);
                } catch (InterruptedException var18) {
                }
            } finally {
                // 事务完成
                transaction.complete();
            }
        }

    }

    // 收集完成后,开始通知操作
    private void notify(ServiceDTO lastServiceDto, 		// 注册中心,续接前文中使用的目标机器的信息
                        List<ApolloConfigNotification> notifications   // 消息通知数据
    ) {
        if (notifications != null && !notifications.isEmpty()) {
            Iterator i$ = notifications.iterator();

            while(i$.hasNext()) {
                ApolloConfigNotification notification = (ApolloConfigNotification)i$.next();
                String namespaceName = notification.getNamespaceName();
                // 拿到需要被通知的ID
                List<RemoteConfigRepository> toBeNotified = Lists.newArrayList(this.m_longPollNamespaces.get(namespaceName));
                // 拿到需要被通知的Messages
                ApolloNotificationMessages originalMessages = (ApolloNotificationMessages)this.m_remoteNotificationMessages.get(namespaceName);
                // 克隆一份
                ApolloNotificationMessages remoteMessages = originalMessages == null ? null : originalMessages.clone();
                // 追加一种扩展数据:比如:application.properties,有则加入,没有
                toBeNotified.addAll(this.m_longPollNamespaces.get(String.format("%s.%s", namespaceName, ConfigFileFormat.Properties.getValue())));
                Iterator i$ = toBeNotified.iterator();
                // 迭代
                while(i$.hasNext()) {
                    // 拿到当前namespaceName在前文装配好的RemoteConfigRepository数据
                    RemoteConfigRepository remoteConfigRepository = (RemoteConfigRepository)i$.next();

                    try {
                        // 调用前文操作对象,启动通知逻辑(详细后续逻辑移步:RemoteConfigRepository.java文件)
                        // 通知内容:举例:remoteMessages的格式:123+default+java-project -> {Long@6682} 16
                        remoteConfigRepository.onLongPollNotified(lastServiceDto, remoteMessages);
                    } catch (Throwable var12) {
                        Tracer.logError(var12);
                    }
                }
            }

        }
    }

    // 收集通知目标数据·1
    private void updateNotifications(List<ApolloConfigNotification> deltaNotifications) {
        Iterator i$ = deltaNotifications.iterator();

        while(i$.hasNext()) {
            ApolloConfigNotification notification = (ApolloConfigNotification)i$.next();
            if (!Strings.isNullOrEmpty(notification.getNamespaceName())) {
                String namespaceName = notification.getNamespaceName();
                if (this.m_notifications.containsKey(namespaceName)) {
                    this.m_notifications.put(namespaceName, notification.getNotificationId());
                }
                // 待前缀的
                // 比如:java-project.properties 或者 application.properties
                String namespaceNameWithPropertiesSuffix = String.format("%s.%s", namespaceName, ConfigFileFormat.Properties.getValue());
                if (this.m_notifications.containsKey(namespaceNameWithPropertiesSuffix)) {
                    this.m_notifications.put(namespaceNameWithPropertiesSuffix, notification.getNotificationId());
                }
            }
        }

    }

    // 收集通知目标数据·2
    private void updateRemoteNotifications(List<ApolloConfigNotification> deltaNotifications) {
        Iterator i$ = deltaNotifications.iterator();

        while(i$.hasNext()) {
            ApolloConfigNotification notification = (ApolloConfigNotification)i$.next();
            if (!Strings.isNullOrEmpty(notification.getNamespaceName()) && notification.getMessages() != null && !notification.getMessages().isEmpty()) {
                ApolloNotificationMessages localRemoteMessages = (ApolloNotificationMessages)this.m_remoteNotificationMessages.get(notification.getNamespaceName());
                if (localRemoteMessages == null) {
                    localRemoteMessages = new ApolloNotificationMessages();
                    this.m_remoteNotificationMessages.put(notification.getNamespaceName(), localRemoteMessages);
                }
                // 收集数据:Messages:123+default+application -> {Long@6671} 19
                // 数据装填
                localRemoteMessages.mergeFrom(notification.getMessages());
            }
        }

    }

    private String assembleNamespaces() {
        return STRING_JOINER.join(this.m_longPollNamespaces.keySet());
    }

    // 组装目标服务的url
    String assembleLongPollRefreshUrl(String uri, String appId, String cluster, String dataCenter, Map<String, Long> notificationsMap) {
        Map<String, String> queryParams = Maps.newHashMap();
        queryParams.put("appId", queryParamEscaper.escape(appId));
        queryParams.put("cluster", queryParamEscaper.escape(cluster));
        queryParams.put("notifications", queryParamEscaper.escape(this.assembleNotifications(notificationsMap)));
        if (!Strings.isNullOrEmpty(dataCenter)) {
            queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter));
        }

        String localIp = this.m_configUtil.getLocalIp();
        if (!Strings.isNullOrEmpty(localIp)) {
            queryParams.put("ip", queryParamEscaper.escape(localIp));
        }

        String params = MAP_JOINER.join(queryParams);
        if (!uri.endsWith("/")) {
            uri = uri + "/";
        }

        return uri + "notifications/v2?" + params;
    }

    String assembleNotifications(Map<String, Long> notificationsMap) {
        List<ApolloConfigNotification> notifications = Lists.newArrayList();
        Iterator i$ = notificationsMap.entrySet().iterator();

        while(i$.hasNext()) {
            Entry<String, Long> entry = (Entry)i$.next();
            ApolloConfigNotification notification = new ApolloConfigNotification((String)entry.getKey(), (Long)entry.getValue());
            notifications.add(notification);
        }

        return this.gson.toJson(notifications);
    }

    private List<ServiceDTO> getConfigServices() {
        List<ServiceDTO> services = this.m_serviceLocator.getConfigServices();
        if (services.size() == 0) {
            throw new ApolloConfigException("No available config service");
        } else {
            return services;
        }
    }
}
......
// longpoll的注册中心选举出的目标服务信息(具有原子性)
private final AtomicReference<ServiceDTO> m_longPollServiceDto;
// 当前namespaceName对应的通知remoteMessages信息(具有原子性)
private final AtomicReference<ApolloNotificationMessages> m_remoteMessages;
// 单线程任务执行器
private static final ScheduledExecutorService m_executorService = 
            Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("RemoteConfigRepository", true));
......
// 来之RemoteConfigLongPollService.java触发的方法
public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, // 注册中心,续接前文中使用的目标机器的信息
                               // 通知内容:举例:remoteMessages的格式:123+default+java-project -> {Long@6682} 16
                               ApolloNotificationMessages remoteMessages 
  ) {
    // longpoll的注册中心选举出的目标服务信息(具有原子性)
    this.m_longPollServiceDto.set(longPollNotifiedServiceDto);
    // 当前namespaceName对应的通知remoteMessages信息(具有原子性)
    this.m_remoteMessages.set(remoteMessages);
    // 单线程任务执行器
    m_executorService.submit(new Runnable() {
        public void run() {
            // 强制刷新标记:true
            RemoteConfigRepository.this.m_configNeedForceRefresh.set(true);
            // 使用抽象父类中的trySync(),触发当前类的:this.sync(); (后文会详细说明这块)
            // 主动触发一次同步操作(和服务启动时,主动触发的方式一致,只不过这里在longpoll机制中添加了触发锚点)
            RemoteConfigRepository.this.trySync();
        }
    });
}

......

续接前文,针对RemoteConfigRepository.java 分析一下:

......
public interface ConfigRepository {
    Properties getConfig();

    void setUpstreamRepository(ConfigRepository var1);

    void addChangeListener(RepositoryChangeListener var1);

    void removeChangeListener(RepositoryChangeListener var1);

    ConfigSourceType getSourceType();
}

......
public class DefaultPropertiesFactory implements PropertiesFactory {
    // 获取配置工具类
    private ConfigUtil m_configUtil = (ConfigUtil)ApolloInjector.getInstance(ConfigUtil.class);

    public DefaultPropertiesFactory() {
    }

    // 根据参数配置:pollo.property.order.enable=false(默认),获取配置实例
    public Properties getPropertiesInstance() {
        return (Properties)(this.m_configUtil.isPropertiesOrderEnabled() ? new OrderedProperties() : new Properties());
    }
}

......
public abstract class AbstractConfigRepository implements ConfigRepository {
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);
    // Change监听器
    private List<RepositoryChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
    // 拿到默认的:DefaultPropertiesFactory.java实例对象
    protected PropertiesFactory propertiesFactory = (PropertiesFactory)ApolloInjector.getInstance(PropertiesFactory.class);

    public AbstractConfigRepository() {
    }

    // 就是包装了一层
    protected boolean trySync() {
        try {
            this.sync();
            return true;
        } catch (Throwable var2) {
            Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(var2));
            logger.warn("Sync config failed, will retry. Repository {}, reason: {}", this.getClass(), ExceptionUtil.getDetailMessage(var2));
            return false;
        }
    }

    // 抽象方法:子类负责实现具体的逻辑
    protected abstract void sync();

    // 添加监听器
    public void addChangeListener(RepositoryChangeListener listener) {
        if (!this.m_listeners.contains(listener)) {
            this.m_listeners.add(listener);
        }

    }

    // 移除监听器
    public void removeChangeListener(RepositoryChangeListener listener) {
        this.m_listeners.remove(listener);
    }

    // 监听器开始生效
    protected void fireRepositoryChange(String namespace, Properties newProperties) {
        Iterator i$ = this.m_listeners.iterator();

        while(i$.hasNext()) {
            // 拿到每一个监听器
            RepositoryChangeListener listener = (RepositoryChangeListener)i$.next();

            try {
                // 开始生效了(稍后分析一下具体的生效逻辑:RepositoryChangeListener.java)
                listener.onRepositoryChange(namespace, newProperties);
            } catch (Throwable var6) {
                Tracer.logError(var6);
                logger.error("Failed to invoke repository change listener {}", listener.getClass(), var6);
            }
        }

    }
}
......
public class RemoteConfigRepository extends AbstractConfigRepository {
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class);
    private static final Joiner STRING_JOINER = Joiner.on("+");
    private static final MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("=");
    private static final Escaper pathEscaper = UrlEscapers.urlPathSegmentEscaper();
    private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();
    // 注册中心服务:获取可用的apollo服务侧的机器信息,用于http通信时使用
    // ConfigServiceLocator内置机制:
    // (1)schedulePeriodicRefresh():每隔5min刷新一次,注册中心服务列表的数据,可以通过:apollo.refreshInterval配置调整时间间隔和频率
    private final ConfigServiceLocator m_serviceLocator;
    // http工具
    private final HttpUtil m_httpUtil;
    // 配置工具类
    private final ConfigUtil m_configUtil;
    // longpoll 机制服务
    private final RemoteConfigLongPollService remoteConfigLongPollService;
    // 每次从apollo服务端拿的的配置数据保存在缓存中
    private volatile AtomicReference<ApolloConfig> m_configCache;
    // 命名空间,核心key
    private final String m_namespace;
    // 单线程任务执行器
    private static final ScheduledExecutorService m_executorService = Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("RemoteConfigRepository", true));
    // longpoll的注册中心选举出的目标服务信息(具有原子性)
    private final AtomicReference<ServiceDTO> m_longPollServiceDto;
    // 当前namespaceName对应的通知remoteMessages信息(具有原子性)
    private final AtomicReference<ApolloNotificationMessages> m_remoteMessages;
    // 限流器
    private final RateLimiter m_loadConfigRateLimiter;
    // 是否强制刷新
    private final AtomicBoolean m_configNeedForceRefresh;
    // 如果加载失败时启用的失败策略
    private final SchedulePolicy m_loadConfigFailSchedulePolicy;
    private final Gson gson;

    // 构造注入
    public RemoteConfigRepository(String namespace) {
        // 命名空间,核心key
        this.m_namespace = namespace;
        //
        this.m_configCache = new AtomicReference();
        // 配置工具类
        this.m_configUtil = (ConfigUtil)ApolloInjector.getInstance(ConfigUtil.class);
        this.m_httpUtil = (HttpUtil)ApolloInjector.getInstance(HttpUtil.class);
        // 注册中心服务:获取可用的apollo服务侧的机器信息,用于http通信时使用
        // ConfigServiceLocator内置机制:
        // (1)schedulePeriodicRefresh():每隔5min刷新一次,注册中心服务列表的数据,可以通过:apollo.refreshInterval配置调整时间间隔和频率
        this.m_serviceLocator = (ConfigServiceLocator)ApolloInjector.getInstance(ConfigServiceLocator.class);
        // 获取远程配置数据:LongPoll实时通讯交互服务,apollo动态刷新就靠它了~(定义一个服务,后面有逻辑触发)
        this.remoteConfigLongPollService = (RemoteConfigLongPollService)ApolloInjector.getInstance(RemoteConfigLongPollService.class);
        this.m_longPollServiceDto = new AtomicReference();
        this.m_remoteMessages = new AtomicReference();
        // 限流:默认QPS=2
        this.m_loadConfigRateLimiter = RateLimiter.create((double)this.m_configUtil.getLoadConfigQPS());
        this.m_configNeedForceRefresh = new AtomicBoolean(true);
        this.m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(this.m_configUtil.getOnErrorRetryInterval(), this.m_configUtil.getOnErrorRetryInterval() * 8L);
        this.gson = new Gson();
        // 触发一次同步操作:拉取一次数据(apollo客户端首次拉取配置数据)
        this.trySync();
        // 刷新任务:主动拉取数据
        this.schedulePeriodicRefresh();
        // 实时刷新任务:启动longpolling机制
        this.scheduleLongPollingRefresh();
    }

    // 获取配置:也会主动触发一次apollo数据拉取
    public Properties getConfig() {
        if (this.m_configCache.get() == null) {
            this.sync();
        }

        // 数据格式转换
        return this.transformApolloConfigToProperties((ApolloConfig)this.m_configCache.get());
    }

    public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
    }

    public ConfigSourceType getSourceType() {
        return ConfigSourceType.REMOTE;
    }

    // 刷新任务:主动拉取数据
    private void schedulePeriodicRefresh() {
        logger.debug("Schedule periodic refresh with interval: {} {}", this.m_configUtil.getRefreshInterval(), this.m_configUtil.getRefreshIntervalTimeUnit());
        m_executorService.scheduleAtFixedRate(
            // 定时任务逻辑
            new Runnable() {
                    public void run() {
                        Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", RemoteConfigRepository.this.m_namespace));
                        RemoteConfigRepository.logger.debug("refresh config for namespace: {}", RemoteConfigRepository.this.m_namespace);
                        // 主动获取配置数据
                        // 使用抽象父类中的trySync(),触发当前类的:this.sync(); (后文会详细说明这块)
                        RemoteConfigRepository.this.trySync();
                        Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
                    }
            },
            (long)this.m_configUtil.getRefreshInterval(),  // 查看配置为5s,可以通过:apollo.refreshInterval配置调整,首次延迟时间
            (long)this.m_configUtil.getRefreshInterval(),  // 同上 ,以后执行间隔时间频率
            this.m_configUtil.getRefreshIntervalTimeUnit()   // 单位:分钟,固定 TimeUnit.MINUTES;
        );
    }

    // 主动拉取的(核心逻辑)
    protected synchronized void sync() {
        Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

        try {
            // 先从缓存中拿数据
            ApolloConfig previous = (ApolloConfig)this.m_configCache.get();
            // 加载所有数据(核心逻辑)
            ApolloConfig current = this.loadApolloConfig();
            // 检测数据是否一致
            if (previous != current) {
                // 不一致,说明远程数据被刷新了
                logger.debug("Remote Config refreshed!");
                // 更新缓存数据
                this.m_configCache.set(current);
                // 主动触发变更(监听器去触发:listener.onRepositoryChange(namespace, newProperties);)
                this.fireRepositoryChange(this.m_namespace, this.getConfig());
            }

            if (current != null) {
                Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()), current.getReleaseKey());
            }
            
            transaction.setStatus("0");
        } catch (Throwable var7) {
            transaction.setStatus(var7);
            throw var7;
        } finally {
            transaction.complete();
        }

    }

    // 数据格式转换
    private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) {
        Properties result = this.propertiesFactory.getPropertiesInstance();
        result.putAll(apolloConfig.getConfigurations());
        return result;
    }

    // 加载所有数据(核心逻辑)
    private ApolloConfig loadApolloConfig() {
        // 限流器
        if (!this.m_loadConfigRateLimiter.tryAcquire(5L, TimeUnit.SECONDS)) {
            try {
                TimeUnit.SECONDS.sleep(5L);
            } catch (InterruptedException var27) {
            }
        }

        // 拿到配置参数
        String appId = this.m_configUtil.getAppId();
        String cluster = this.m_configUtil.getCluster();
        String dataCenter = this.m_configUtil.getDataCenter();
        String secret = this.m_configUtil.getAccessKeySecret();
        Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, new Object[]{this.m_namespace}));
        // 重试次数
        int maxRetries = this.m_configNeedForceRefresh.get() ? 2 : 1;
        // 失败休眠时间
        long onErrorSleepTime = 0L;
        Throwable exception = null;
        // 拿到注册中心中的目标服务列表
        List<ServiceDTO> configServices = this.getConfigServices();
        String url = null;
        int i = 0;

        // 标签通常用于在特定的代码块中进行跳转操作,比如在循环或条件语句中使用break或continue结合标签来控制程序的流程
        label194:
        while(true) {
            if (i < maxRetries) {
                // 目标服务列表
                List<ServiceDTO> randomConfigServices = Lists.newLinkedList(configServices);
                // 打乱顺序
                Collections.shuffle(randomConfigServices);
                if (this.m_longPollServiceDto.get() != null) {                    
                    // 如果当前检测到longpoll机制中存在可用目标服务,则添加进来,放在首位,并清空longpoll的服务数据
                    // 如果此处存在数据,则后面使用的就是这个目标服务的数据
                    randomConfigServices.add(0, this.m_longPollServiceDto.getAndSet((Object)null));
                }
                
                Iterator i$ = randomConfigServices.iterator();

                while(true) {
                    // 劝退机制
                    if (!i$.hasNext()) {
                        ++i;
                        continue label194;
                    }

                    // 因为被随机打乱了顺序,次数为随机一个目标服务(如果有longpoll的情况下,则延续使用一次)
                    ServiceDTO configService = (ServiceDTO)i$.next();
                    if (onErrorSleepTime > 0L) {
                        logger.warn("Load config failed, will retry in {} {}. appId: {}, cluster: {}, namespaces: {}", new Object[]{onErrorSleepTime, this.m_configUtil.getOnErrorRetryIntervalTimeUnit(), appId, cluster, this.m_namespace});

                        try {
                            // 休眠
                            this.m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(onErrorSleepTime);
                        } catch (InterruptedException var26) {
                        }
                    }

                    // 组装url
                    url = this.assembleQueryConfigUrl(configService.getHomepageUrl(),
                                                      appId, 
                                                      cluster, 
                                                      this.m_namespace, 
                                                      dataCenter, 
                                                      (ApolloNotificationMessages)this.m_remoteMessages.get(), 
                                                      (ApolloConfig)this.m_configCache.get()
                    );
                    logger.debug("Loading config from {}", url);
                    // 装配request
                    HttpRequest request = new HttpRequest(url);
                    // 装配密钥
                    if (!StringUtils.isBlank(secret)) {
                        // 设置头部
                        Map<String, String> headers = Signature.buildHttpHeaders(url, appId, secret);
                        request.setHeaders(headers);
                    }

                    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "queryConfig");
                    transaction.addData("Url", url);

                    try {
                        // 主动请求,向后台拉取apollo配置数据
                        HttpResponse<ApolloConfig> response = this.m_httpUtil.doGet(request, ApolloConfig.class);
                        // 非强制刷新
                        this.m_configNeedForceRefresh.set(false);
                        // 执行成功了
                        this.m_loadConfigFailSchedulePolicy.success();
                        transaction.addData("StatusCode", response.getStatusCode());
                        transaction.setStatus("0");
                        ApolloConfig result;
                        // 304异常
                        if (response.getStatusCode() == 304) {
                            logger.debug("Config server responds with 304 HTTP status code.");
                            result = (ApolloConfig)this.m_configCache.get();
                            return result;
                        }
                        // 成功,直接返回
                        result = (ApolloConfig)response.getBody();
                        logger.debug("Loaded config for {}: {}", this.m_namespace, result);
                        ApolloConfig var34 = result;
                        return var34;
                    } catch (ApolloConfigStatusCodeException var28) {
                        ApolloConfigStatusCodeException statusCodeException = var28;
                        if (var28.getStatusCode() == 404) {
                            String message = String.format("Could not find config for namespace - appId: %s, cluster: %s, namespace: %s, please check whether the configs are released in Apollo!", appId, cluster, this.m_namespace);
                            statusCodeException = new ApolloConfigStatusCodeException(var28.getStatusCode(), message);
                        }

                        Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(statusCodeException));
                        transaction.setStatus(statusCodeException);
                        exception = statusCodeException;
                        if (var28.getStatusCode() == 404) {
                            break;
                        }
                    } catch (Throwable var29) {
                        Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(var29));
                        transaction.setStatus(var29);
                        exception = var29;
                    } finally {
                        transaction.complete();
                    }

                    onErrorSleepTime = this.m_configNeedForceRefresh.get() ? this.m_configUtil.getOnErrorRetryInterval() : this.m_loadConfigFailSchedulePolicy.fail();
                }
            }

            String message = String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s, url: %s", appId, cluster, this.m_namespace, url);
            throw new ApolloConfigException(message, (Throwable)exception);
        }
    }

    // 组装查询url
    String assembleQueryConfigUrl(String uri, String appId, String cluster, String namespace, String dataCenter, ApolloNotificationMessages remoteMessages, ApolloConfig previousConfig) {
        String path = "configs/%s/%s/%s";
        List<String> pathParams = Lists.newArrayList(new String[]{pathEscaper.escape(appId), pathEscaper.escape(cluster), pathEscaper.escape(namespace)});
        Map<String, String> queryParams = Maps.newHashMap();
        if (previousConfig != null) {
            queryParams.put("releaseKey", queryParamEscaper.escape(previousConfig.getReleaseKey()));
        }

        if (!Strings.isNullOrEmpty(dataCenter)) {
            queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter));
        }

        String localIp = this.m_configUtil.getLocalIp();
        if (!Strings.isNullOrEmpty(localIp)) {
            queryParams.put("ip", queryParamEscaper.escape(localIp));
        }

        if (remoteMessages != null) {
            queryParams.put("messages", queryParamEscaper.escape(this.gson.toJson(remoteMessages)));
        }

        String pathExpanded = String.format(path, pathParams.toArray());
        if (!queryParams.isEmpty()) {
            pathExpanded = pathExpanded + "?" + MAP_JOINER.join(queryParams);
        }

        if (!uri.endsWith("/")) {
            uri = uri + "/";
        }

        return uri + pathExpanded;
    }

    private void scheduleLongPollingRefresh() {
        // 触发LongPolling实时通讯机制
        this.remoteConfigLongPollService.submit(this.m_namespace, this);
    }

    // 来之RemoteConfigLongPollService.java触发的方法
    public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, // 注册中心,续接前文中使用的目标机器的信息
                                   // 通知内容:举例:remoteMessages的格式:123+default+java-project -> {Long@6682} 16
                                   ApolloNotificationMessages remoteMessages 
      ) {
        // longpoll的注册中心选举出的目标服务信息(具有原子性)
        this.m_longPollServiceDto.set(longPollNotifiedServiceDto);
        // 当前namespaceName对应的通知remoteMessages信息(具有原子性)
        this.m_remoteMessages.set(remoteMessages);
        // 单线程任务执行器
        m_executorService.submit(new Runnable() {
            public void run() {
                // 强制刷新标记:true
                RemoteConfigRepository.this.m_configNeedForceRefresh.set(true);
                // 使用抽象父类中的trySync(),触发当前类的:this.sync(); (后文会详细说明这块)
                // 主动触发一次同步操作(和服务启动时,主动触发的方式一致,只不过这里在longpoll机制中添加了触发锚点)
                RemoteConfigRepository.this.trySync();
            }
        });
    }

    // 获取注册中心中的目标服务的数据列表
    private List<ServiceDTO> getConfigServices() {
        // 关键逻辑在:ConfigServiceLocator.java文件中
        List<ServiceDTO> services = this.m_serviceLocator.getConfigServices();
        if (services.size() == 0) {
            throw new ApolloConfigException("No available config service");
        } else {
            return services;
        }
    }
}

至此,可以看到,从 apollo 服务端侧拿到了配置数据,接下来要看一下,拿到的数据时如何更新到客户端参数的

还需要续接前文的:ApolloApplicationContextInitializer.java

继续分析!!!

一、 更新过程

  1. 创建CompositePropertySource
    • 当 Apollo 客户端从服务端获取到新的配置数据后,会根据这些数据创建一个新的CompositePropertySource对象。这个复合属性源包含了从不同命名空间获取的配置信息,每个命名空间对应一个ConfigPropertySource
  1. 添加到环境属性源:
    • 如果环境中已经存在 “ApolloBootstrapPropertySources” 属性源,会确保其优先级最高。然后将新创建的复合属性源添加到环境属性源中,使其在 “ApolloBootstrapPropertySources” 之后。具体实现是通过操作ConfigurableEnvironment对象的MutablePropertySources来完成的。
    • 如果没有 “ApolloBootstrapPropertySources”,则将复合属性源添加到环境属性源的首位。

二、生效机制

  1. Spring 的属性源查找机制:
    • Spring 在运行时,当需要获取某个属性值时,会从Environment对象中查找属性源。Environment对象维护了一个有序的PropertySources集合,它会按照特定的顺序遍历这个集合来查找属性值。
    • CompositePropertySource被添加到这个集合中后,Spring 在查找属性时就有机会访问到这个新添加的属性源。
  1. 依赖注入与属性解析:
    • 在 Spring 容器初始化过程中,对于使用了@Value注解或者通过Environment对象获取属性值的 Bean,Spring 会在依赖注入阶段解析这些属性值。
    • 当属性源发生变化(即新的CompositePropertySource被添加)后,Spring 在解析属性值时会从更新后的属性源中获取最新的值。
    • 例如,如果一个 Bean 中有一个属性使用了@Value("${some.property.key}")注解,Spring 会在属性注入时从环境属性源中查找对应的值。如果新的配置更新了这个属性的值,那么在下次解析这个注解时,就会获取到新的值,从而实现了配置的动态更新。
  1. 自动更新机制(如果启用):
    • 如果配置了自动更新属性的功能,当配置发生变化时,注册的监听器(如AutoUpdateConfigChangeListener)会被触发。这些监听器会检测到配置的变化,并更新相应的属性源。
    • 这样,即使在应用运行过程中配置发生了变化,也能及时更新到环境中,并通过上述机制使应用中的 Bean 能够获取到最新的属性值。

综上所述,更新后的CompositePropertySource通过被添加到 Spring 的环境属性源集合中,利用 Spring 的属性查找和依赖注入机制,以及自动更新机制(如果启用),使得新的配置能够在应用中生效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成熟的小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值