406 lines
16 KiB
Objective-C
406 lines
16 KiB
Objective-C
/*
|
||
* Licensed to the Apache Software Foundation (ASF) under one
|
||
* or more contributor license agreements. See the NOTICE file
|
||
* distributed with this work for additional information
|
||
* regarding copyright ownership. The ASF licenses this file
|
||
* to you under the Apache License, Version 2.0 (the
|
||
* "License"); you may not use this file except in compliance
|
||
* with the License. You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing,
|
||
* software distributed under the License is distributed on an
|
||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
* KIND, either express or implied. See the License for the
|
||
* specific language governing permissions and limitations
|
||
* under the License.
|
||
*/
|
||
|
||
#import "HttpdnsScheduleCenter.h"
|
||
#import "HttpdnsPersistenceUtils.h"
|
||
#import "HttpdnsLog_Internal.h"
|
||
#import "HttpdnsInternalConstant.h"
|
||
#import "HttpdnsRequestManager.h"
|
||
#import "HttpdnsService_Internal.h"
|
||
#import "HttpdnsScheduleExecutor.h"
|
||
#import "HttpdnsRemoteResolver.h"
|
||
#import "HttpdnsUtil.h"
|
||
#import "HttpdnsPublicConstant.h"
|
||
#import "HttpdnsRegionConfigLoader.h"
|
||
#import "HttpdnsIpStackDetector.h"
|
||
|
||
static NSString *const kLastUpdateUnixTimestampKey = @"last_update_unix_timestamp";
|
||
static NSString *const kScheduleRegionConfigLocalCacheFileName = @"schedule_center_result";
|
||
|
||
static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||
|
||
@interface HttpdnsScheduleCenter ()
|
||
|
||
// 为了简å<E282AC>•ï¼Œæ— è®ºv4还是v6,都å<C2BD>ªå…±å<C2B1>Œç»´æŠ?个下æ ?
|
||
// ä¸€èˆ¬è€Œè¨€ï¼Œä¸‹æ ‡å½“å‰<C3A5>在哪里并ä¸<C3A4>是那么é‡<C3A9>è¦<C3A8>,é‡<C3A9>è¦<C3A8>的是轮询的能åŠ?
|
||
@property (nonatomic, assign) int currentActiveServiceHostIndex;
|
||
@property (nonatomic, assign) int currentActiveUpdateHostIndex;
|
||
|
||
@property (nonatomic, copy) NSArray<NSString *> *ipv4ServiceServerHostList;
|
||
@property (nonatomic, copy) NSArray<NSString *> *ipv6ServiceServerHostList;
|
||
|
||
@property (nonatomic, copy) NSArray<NSString *> *ipv4UpdateServerHostList;
|
||
@property (nonatomic, copy) NSArray<NSString *> *ipv6UpdateServerHostList;
|
||
|
||
@property (nonatomic, strong) dispatch_queue_t scheduleFetchConfigAsyncQueue;
|
||
@property (nonatomic, strong) dispatch_queue_t scheduleConfigLocalOperationQueue;
|
||
|
||
@property (nonatomic, copy) NSString *scheduleCenterResultPath;
|
||
@property (nonatomic, copy) NSDate *lastScheduleCenterConnectDate;
|
||
|
||
@property (nonatomic, copy) NSString *currentRegion;
|
||
|
||
@property (nonatomic, assign) NSInteger accountId;
|
||
|
||
@end
|
||
|
||
@implementation HttpdnsScheduleCenter
|
||
|
||
- (instancetype)initWithAccountId:(NSInteger)accountId {
|
||
if (self = [self init]) {
|
||
_accountId = accountId;
|
||
// 针对多账å<C2A6>·éš”ç¦»ï¼šæ ¹æ<C2B9>®è´¦å<C2A6>·ID切æ<E280A1>¢æœ¬åœ°è°ƒåº¦ç»“æžœå˜å‚¨è·¯å¾„
|
||
NSString *dir = [HttpdnsPersistenceUtils scheduleCenterResultDirectoryForAccount:accountId];
|
||
_scheduleCenterResultPath = [dir stringByAppendingPathComponent:kScheduleRegionConfigLocalCacheFileName];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (instancetype)init {
|
||
if (self = [super init]) {
|
||
_scheduleFetchConfigAsyncQueue = dispatch_queue_create("com.new.httpdns.scheduleFetchConfigAsyncQueue", DISPATCH_QUEUE_CONCURRENT);
|
||
_scheduleConfigLocalOperationQueue = dispatch_queue_create("com.new.httpdns.scheduleConfigLocalOperationQueue", DISPATCH_QUEUE_SERIAL);
|
||
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
self->_currentActiveUpdateHostIndex = 0;
|
||
self->_currentActiveServiceHostIndex = 0;
|
||
});
|
||
|
||
_scheduleCenterResultPath = [[HttpdnsPersistenceUtils scheduleCenterResultDirectory]
|
||
stringByAppendingPathComponent:kScheduleRegionConfigLocalCacheFileName];
|
||
|
||
// 上次更新日期默认设置ä¸?天å‰<C3A5>ï¼Œè¿™æ ·å¦‚æžœç¼“å˜æ²¡æœ‰è®°å½•,就会立å<E280B9>³æ›´æ–°
|
||
_lastScheduleCenterConnectDate = [NSDate dateWithTimeIntervalSinceNow:(- 24 * 60 * 60)];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)initRegion:(NSString *)region {
|
||
if (![[HttpdnsRegionConfigLoader getAvailableRegionList] containsObject:region]) {
|
||
region = NEW_HTTPDNS_DEFAULT_REGION_KEY;
|
||
}
|
||
|
||
// 先用默认regionåˆ<C3A5>å§‹åŒ?
|
||
// 如果用户主动调用了设置region接å<C2A5>£ï¼Œä¼šæŒ‰ç…§ç”¨æˆ·è®¾ç½®çš„å†<C3A5>æ<EFBFBD>¥ä¸€æ¬?
|
||
[self initServerListByRegion:region];
|
||
|
||
// å†<C3A5>从本地缓å˜è¯»å<C2BB>–之å‰<C3A5>缓å˜è¿‡çš„é…<C3A9>ç½®
|
||
[self loadRegionConfigFromLocalCache];
|
||
}
|
||
|
||
- (void)resetRegion:(NSString *)region {
|
||
[self initServerListByRegion:region];
|
||
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
self.currentActiveServiceHostIndex = 0;
|
||
self.currentActiveUpdateHostIndex = 0;
|
||
});
|
||
|
||
// é‡<C3A9>ç½®region之å<E280B9>Žé©¬ä¸Šå<C5A0>‘起一次更æ–?
|
||
[self asyncUpdateRegionScheduleConfig];
|
||
}
|
||
|
||
- (void)loadRegionConfigFromLocalCache {
|
||
dispatch_async(self.scheduleFetchConfigAsyncQueue, ^{
|
||
id obj = [HttpdnsPersistenceUtils getJSONFromPath:self.scheduleCenterResultPath];
|
||
if (![obj isKindOfClass:[NSDictionary class]]) {
|
||
// 本地缓å˜å<CB9C>¯èƒ½è¢«æ—§ç‰ˆæœ¬æˆ–其他组件写入é<C2A5>žå—典类型,直接忽略以é<C2A5>¿å…<C3A5>é<EFBFBD>žæ³•对象释放
|
||
return;
|
||
}
|
||
NSDictionary *scheduleCenterResult = (NSDictionary *)obj;
|
||
|
||
// 兼容时间戳为NSNumber/NSString,å±<C3A5>蔽NSNullç‰å¼‚常输å…?
|
||
id ts = [scheduleCenterResult objectForKey:kLastUpdateUnixTimestampKey];
|
||
if ([ts respondsToSelector:@selector(doubleValue)]) {
|
||
NSDate *lastUpdateDate = [NSDate dateWithTimeIntervalSince1970:[ts doubleValue]];
|
||
self->_lastScheduleCenterConnectDate = lastUpdateDate;
|
||
}
|
||
[self updateRegionConfig:scheduleCenterResult];
|
||
});
|
||
}
|
||
|
||
// æ ¹æ<C2B9>®æŒ‡å®šçš„æ—¶é—´é—´é𔿣€æŸ¥æ˜¯å<C2AF>¦éœ€è¦<C3A8>æ›´æ–?
|
||
- (void)asyncUpdateRegionConfigAfterAtLeast:(NSTimeInterval)interval {
|
||
__block BOOL shouldUpdate = NO;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
NSDate *now = [NSDate date];
|
||
if ([now timeIntervalSinceDate:self->_lastScheduleCenterConnectDate] > interval) {
|
||
self->_lastScheduleCenterConnectDate = now;
|
||
shouldUpdate = YES;
|
||
}
|
||
});
|
||
|
||
if (shouldUpdate) {
|
||
[self asyncUpdateRegionScheduleConfig];
|
||
}
|
||
}
|
||
|
||
- (void)asyncUpdateRegionScheduleConfig {
|
||
[self asyncUpdateRegionScheduleConfigAtRetry:0];
|
||
}
|
||
|
||
- (void)asyncUpdateRegionScheduleConfigAtRetry:(int)retryCount {
|
||
if (retryCount > MAX_UPDATE_RETRY_COUNT) {
|
||
return;
|
||
}
|
||
|
||
dispatch_async(_scheduleFetchConfigAsyncQueue, ^(void) {
|
||
NSTimeInterval timeout = [HttpDnsService getInstanceByAccountId:self.accountId].timeoutInterval;
|
||
HttpdnsScheduleExecutor *scheduleCenterExecutor = [[HttpdnsScheduleExecutor alloc] initWithAccountId:self.accountId timeout:timeout];
|
||
|
||
NSError *error = nil;
|
||
NSString *updateHost = [self getActiveUpdateServerHost];
|
||
NSDictionary *scheduleCenterResult = [scheduleCenterExecutor fetchRegionConfigFromServer:updateHost error:&error];
|
||
if (error || !scheduleCenterResult) {
|
||
HttpdnsLogDebug("Update region config failed, error: %@", error);
|
||
|
||
// å<>ªæœ‰æŠ¥é”™äº†å°±å°<C3A5>试选择新的调度æœ<C3A6>务å™?
|
||
[self rotateUpdateServerHost];
|
||
|
||
// 3秒之å<E280B9>Žé‡<C3A9>è¯?
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((retryCount + 1) * NSEC_PER_SEC)), self->_scheduleFetchConfigAsyncQueue, ^{
|
||
[self asyncUpdateRegionScheduleConfigAtRetry:retryCount + 1];
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
NSMutableDictionary *toSave = [scheduleCenterResult mutableCopy];
|
||
toSave[kLastUpdateUnixTimestampKey] = @([[NSDate date] timeIntervalSince1970]);
|
||
|
||
BOOL saveSuccess = [HttpdnsPersistenceUtils saveJSON:toSave toPath:self.scheduleCenterResultPath];
|
||
HttpdnsLogDebug("Save region config to local cache %@", saveSuccess ? @"successfully" : @"failed");
|
||
|
||
[self updateRegionConfig:scheduleCenterResult];
|
||
});
|
||
}
|
||
|
||
- (void)updateRegionConfig:(NSDictionary *)scheduleCenterResult {
|
||
NSArray *v4Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV4HostKey];
|
||
NSArray *v6Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV6HostKey];
|
||
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
HttpdnsRegionConfigLoader *regionConfigLoader = [HttpdnsRegionConfigLoader sharedInstance];
|
||
|
||
if ([HttpdnsUtil isNotEmptyArray:v4Result]) {
|
||
self->_ipv4ServiceServerHostList = [v4Result copy];
|
||
|
||
// 调度server列表总是æœ<C3A6>务serveråˆ—è¡¨åŠ ä¸Šå…œåº•åŸŸå<C5B8><C3A5>
|
||
self->_ipv4UpdateServerHostList = [HttpdnsUtil joinArrays:v4Result
|
||
withArray:[regionConfigLoader getUpdateV4FallbackHostList:self->_currentRegion]];
|
||
}
|
||
|
||
if ([HttpdnsUtil isNotEmptyArray:v6Result]) {
|
||
self->_ipv6ServiceServerHostList = [v6Result copy];
|
||
|
||
// 调度server列表总是æœ<C3A6>务serveråˆ—è¡¨åŠ ä¸Šå…œåº•åŸŸå<C5B8><C3A5>
|
||
self->_ipv6UpdateServerHostList = [HttpdnsUtil joinArrays:v6Result
|
||
withArray:[regionConfigLoader getUpdateV6FallbackHostList:self->_currentRegion]];
|
||
}
|
||
|
||
self->_currentActiveUpdateHostIndex = 0;
|
||
self->_currentActiveServiceHostIndex = 0;
|
||
});
|
||
}
|
||
|
||
- (NSString *)getActiveUpdateServerHost {
|
||
HttpdnsIPStackType currentStack = [[HttpdnsIpStackDetector sharedInstance] currentIpStack];
|
||
if (currentStack == kHttpdnsIpv6Only) {
|
||
NSString *v6Host = [self currentActiveUpdateServerV6Host];
|
||
if ([HttpdnsUtil isIPv6Address:v6Host]) {
|
||
return [NSString stringWithFormat:@"[%@]", v6Host];
|
||
}
|
||
return v6Host;
|
||
}
|
||
|
||
return [self currentActiveUpdateServerV4Host];
|
||
}
|
||
|
||
- (void)initServerListByRegion:(NSString *)region {
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
self->_currentRegion = region;
|
||
|
||
HttpdnsRegionConfigLoader *regionConfigLoader = [HttpdnsRegionConfigLoader sharedInstance];
|
||
|
||
self.ipv4ServiceServerHostList = [regionConfigLoader getSeriveV4HostList:region];
|
||
self.ipv4UpdateServerHostList = [HttpdnsUtil joinArrays:[regionConfigLoader getSeriveV4HostList:region]
|
||
withArray:[regionConfigLoader getUpdateV4FallbackHostList:region]];
|
||
|
||
self.ipv6ServiceServerHostList = [regionConfigLoader getSeriveV6HostList:region];
|
||
self.ipv6UpdateServerHostList = [HttpdnsUtil joinArrays:[regionConfigLoader getSeriveV6HostList:region]
|
||
withArray:[regionConfigLoader getUpdateV6FallbackHostList:region]];
|
||
});
|
||
}
|
||
|
||
- (void)rotateServiceServerHost {
|
||
__block int timeToUpdate = NO;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
self.currentActiveServiceHostIndex++;
|
||
|
||
int total = (int)self.ipv4ServiceServerHostList.count + (int)self.ipv6ServiceServerHostList.count;
|
||
if (self.currentActiveServiceHostIndex % total == 0) {
|
||
timeToUpdate = YES;
|
||
}
|
||
});
|
||
|
||
if (timeToUpdate) {
|
||
// æ¯<C3A6>次æœ<C3A6>务server列表轮转之å<E280B9>Žï¼Œå°<C3A5>è¯?个至少间éš?0秒的更新
|
||
[self asyncUpdateRegionConfigAfterAtLeast:30];
|
||
}
|
||
}
|
||
|
||
- (void)rotateUpdateServerHost {
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
self.currentActiveUpdateHostIndex++;
|
||
});
|
||
}
|
||
|
||
- (NSString *)currentActiveUpdateServerV4Host {
|
||
__block NSString *host = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
int count = (int)self.ipv4UpdateServerHostList.count;
|
||
if (count == 0) {
|
||
HttpdnsLogDebug("Severe error: update v4 ip list is empty, it should never happen");
|
||
return;
|
||
}
|
||
int index = self.currentActiveUpdateHostIndex % count;
|
||
host = self.ipv4UpdateServerHostList[index];
|
||
});
|
||
return host;
|
||
}
|
||
|
||
- (NSString *)currentActiveServiceServerV4Host {
|
||
// æ¯<C3A6>次读å<C2BB>–时都检查是å<C2AF>¦éœ€è¦<C3A8>æ›´æ–°ï¼Œç›¸å½“äºŽå®žçŽ°ä¸€ä¸ªæ‡’åŠ è½½çš„æœºåˆ?
|
||
// å› ä¸ºå½“å‰<C3A5>httpdnsçš„åˆ<C3A5>始化方å¼<C3A5>,没有一个统一的åˆ<C3A5>始化入å<C2A5>£ï¼Œæ‰€ä»¥éœ€è¦<C3A8>è¿™æ ·å¤„ç<E2809E>?
|
||
[self asyncUpdateRegionConfigAfterAtLeast:(24 * 60 * 60)];
|
||
|
||
// 检查是å<C2AF>¦å˜åœ¨HTTPDNS_DEBUG_V4_SERVICE_IP环境å<C692>˜é‡<C3A9>
|
||
NSString *debugV4ServiceIP = [[[NSProcessInfo processInfo] environment] objectForKey:@"HTTPDNS_DEBUG_V4_SERVICE_IP"];
|
||
if ([HttpdnsUtil isNotEmptyString:debugV4ServiceIP]) {
|
||
HttpdnsLogDebug("Using debug v4 service IP from environment: %@", debugV4ServiceIP);
|
||
return debugV4ServiceIP;
|
||
}
|
||
|
||
__block NSString *host = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
int count = (int)self.ipv4ServiceServerHostList.count;
|
||
if (count == 0) {
|
||
HttpdnsLogDebug("Severe error: service v4 ip list is empty, it should never happen");
|
||
return;
|
||
}
|
||
int index = self.currentActiveServiceHostIndex % count;
|
||
host = self.ipv4ServiceServerHostList[index];
|
||
});
|
||
return host;
|
||
}
|
||
|
||
- (NSString *)currentActiveUpdateServerV6Host {
|
||
__block NSString *host = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
int count = (int)self.ipv6UpdateServerHostList.count;
|
||
if (count == 0) {
|
||
HttpdnsLogDebug("Severe error: update v6 ip list is empty, it should never happen");
|
||
return;
|
||
}
|
||
int index = self.currentActiveUpdateHostIndex % count;
|
||
host = self.ipv6UpdateServerHostList[index];
|
||
});
|
||
return host;
|
||
}
|
||
|
||
- (NSString *)currentActiveServiceServerV6Host {
|
||
// å<>Œä¸Š
|
||
[self asyncUpdateRegionConfigAfterAtLeast:(24 * 60 * 60)];
|
||
|
||
// 检查是å<C2AF>¦å˜åœ¨HTTPDNS_DEBUG_V6_SERVICE_IP环境å<C692>˜é‡<C3A9>
|
||
NSString *debugV6ServiceIP = [[[NSProcessInfo processInfo] environment] objectForKey:@"HTTPDNS_DEBUG_V6_SERVICE_IP"];
|
||
if ([HttpdnsUtil isNotEmptyString:debugV6ServiceIP]) {
|
||
HttpdnsLogDebug("Using debug v6 service IP from environment: %@", debugV6ServiceIP);
|
||
return debugV6ServiceIP;
|
||
}
|
||
|
||
__block NSString *host = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
int count = (int)self.ipv6ServiceServerHostList.count;
|
||
if (count == 0) {
|
||
HttpdnsLogDebug("Severe error: service v6 ip list is empty, it should never happen");
|
||
return;
|
||
}
|
||
int index = self.currentActiveServiceHostIndex % count;
|
||
host = self.ipv6ServiceServerHostList[index];
|
||
});
|
||
|
||
if ([HttpdnsUtil isIPv6Address:host]) {
|
||
host = [NSString stringWithFormat:@"[%@]", host];
|
||
}
|
||
return host;
|
||
}
|
||
|
||
#pragma mark - For Test Only
|
||
|
||
- (NSArray<NSString *> *)currentUpdateServerV4HostList {
|
||
__block NSArray<NSString *> *list = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
list = [self.ipv4UpdateServerHostList copy];
|
||
});
|
||
return list;
|
||
}
|
||
|
||
- (NSArray<NSString *> *)currentServiceServerV4HostList {
|
||
__block NSArray<NSString *> *list = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
list = [self.ipv4ServiceServerHostList copy];
|
||
});
|
||
return list;
|
||
}
|
||
|
||
- (NSArray<NSString *> *)currentUpdateServerV6HostList {
|
||
__block NSArray<NSString *> *list = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
list = [self.ipv6UpdateServerHostList copy];
|
||
});
|
||
return list;
|
||
}
|
||
|
||
- (NSArray<NSString *> *)currentServiceServerV6HostList {
|
||
__block NSArray<NSString *> *list = nil;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
list = [self.ipv6ServiceServerHostList copy];
|
||
});
|
||
return list;
|
||
}
|
||
|
||
- (int)currentActiveUpdateServerHostIndex {
|
||
__block int index = 0;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
index = self.currentActiveUpdateHostIndex;
|
||
});
|
||
return index;
|
||
}
|
||
|
||
- (int)currentActiveServiceServerHostIndex {
|
||
__block int index = 0;
|
||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||
index = self.currentActiveServiceHostIndex;
|
||
});
|
||
return index;
|
||
}
|
||
|
||
@end
|