Files
waf-platform/HttpDNSSDK/sdk/ios/NewHttpDNS/Scheduler/HttpdnsScheduleCenter.m

406 lines
16 KiB
Objective-C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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