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