apollo客户端如何实时刷新配置数据?

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.this.trySync();”请查看前文中对应的逻辑

此处为语雀内容卡片,点击链接查看:https://www.yuque.com/xiaofengweiling/fkrpy5/ltzdv77vbvrzdi06

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

成熟的小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值