QNDnsPrefetcher.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. //
  2. // QNDnsPrefetcher.m
  3. // QnDNS
  4. //
  5. // Created by yangsen on 2020/3/26.
  6. // Copyright © 2020 com.qiniu. All rights reserved.
  7. //
  8. #import "QNDnsPrefetcher.h"
  9. #import "QNInetAddress.h"
  10. #import "QNDnsCacheInfo.h"
  11. #import "QNConfig.h"
  12. #import "QNDnsCacheFile.h"
  13. #import "QNUpToken.h"
  14. #import "QNUtils.h"
  15. #import "QNAsyncRun.h"
  16. #import "QNFixedZone.h"
  17. #import "QNAutoZone.h"
  18. #import <HappyDNS/HappyDNS.h>
  19. //MARK: -- HappyDNS 适配
  20. @interface QNRecord(DNS)<QNInetAddressDelegate>
  21. @end
  22. @implementation QNRecord(DNS)
  23. - (NSString *)hostValue{
  24. return nil;
  25. }
  26. - (NSString *)ipValue{
  27. return self.value;
  28. }
  29. - (NSNumber *)ttlValue{
  30. return @(self.ttl);
  31. }
  32. - (NSNumber *)timestampValue{
  33. return @(self.timeStamp);
  34. }
  35. @end
  36. @interface QNDnsManager(DNS)<QNDnsDelegate>
  37. @end
  38. @implementation QNDnsManager(DNS)
  39. - (NSArray<id<QNInetAddressDelegate>> *)lookup:(NSString *)host{
  40. return [self queryRecords:host];
  41. }
  42. @end
  43. //MARK: -- DNS Prefetcher
  44. @interface QNDnsPrefetcher()
  45. /// 是否正在预取,正在预取会直接取消新的预取操作请求
  46. @property(atomic, assign)BOOL isPrefetching;
  47. /// 获取AutoZone时的同步锁
  48. @property(nonatomic, strong)dispatch_semaphore_t getAutoZoneSemaphore;
  49. /// DNS信息本地缓存key
  50. @property(nonatomic, strong)QNDnsCacheInfo *dnsCacheInfo;
  51. /// happy的dns解析对象列表,会使用多个dns解析对象 包括系统解析
  52. @property(nonatomic, strong)QNDnsManager * httpDns;
  53. /// 缓存DNS解析结果
  54. @property(nonatomic, strong)NSMutableDictionary <NSString *, NSArray<QNInetAddress *>*> *addressDictionary;
  55. @end
  56. @implementation QNDnsPrefetcher
  57. + (instancetype)shared{
  58. static QNDnsPrefetcher *prefetcher = nil;
  59. static dispatch_once_t onceToken;
  60. dispatch_once(&onceToken, ^{
  61. prefetcher = [[QNDnsPrefetcher alloc] init];
  62. });
  63. return prefetcher;
  64. }
  65. - (instancetype)init{
  66. if (self = [super init]) {
  67. _isPrefetching = NO;
  68. }
  69. return self;
  70. }
  71. //MARK: -- uploadManager初始化时,加载本地缓存到内存
  72. /// 同步本地预取缓存 如果存在且满足使用返回false,反之为true
  73. - (BOOL)recoverCache{
  74. NSLog(@"== recoverCache");
  75. id <QNRecorderDelegate> recorder = nil;
  76. NSError *error;
  77. recorder = [QNDnsCacheFile dnsCacheFile:kQNGlobalConfiguration.dnscacheDir
  78. error:&error];
  79. if (error) {
  80. return YES;
  81. }
  82. NSData *data = [recorder get:[QNIP local]];
  83. if (!data) {
  84. return YES;
  85. }
  86. QNDnsCacheInfo *cacheInfo = [QNDnsCacheInfo dnsCacheInfo:data];
  87. if (!cacheInfo) {
  88. return YES;
  89. }
  90. NSString *localIp = [QNIP local];
  91. if (!localIp || localIp.length == 0) {
  92. return YES;
  93. }
  94. if (![cacheInfo.localIp isEqualToString:localIp]) {
  95. return YES;
  96. }
  97. [self setDnsCacheInfo:cacheInfo];
  98. return [self recoverDnsCache:cacheInfo.info];
  99. }
  100. /// 本地缓存读取失败后,加载本地域名,预取DNS解析信息
  101. - (void)localFetch{
  102. if ([self prepareToPreFetch] == NO) {
  103. return;
  104. }
  105. NSLog(@"== localFetch");
  106. [self preFetchHosts:[self getLocalPreHost]];
  107. [self recorderDnsCache];
  108. [self endPreFetch];
  109. }
  110. //MARK: -- 检测并预取
  111. /// 根据token检测Dns缓存信息时效,无效则预取。 完成预取操作返回YES,反之返回NO
  112. - (void)checkAndPrefetchDnsIfNeed:(QNZone *)currentZone token:(NSString *)token{
  113. if ([self prepareToPreFetch] == NO) {
  114. return;
  115. }
  116. [self preFetchHosts:[self getCurrentZoneHosts:currentZone token:token]];
  117. [self recorderDnsCache];
  118. [self endPreFetch];
  119. }
  120. /// 检测已预取dns是否还有效,无效则重新预取
  121. - (void)checkWhetherCachedDnsValid{
  122. if ([self prepareToPreFetch] == NO) {
  123. return;
  124. }
  125. [self preFetchHosts:[self.addressDictionary allKeys]];
  126. [self recorderDnsCache];
  127. [self endPreFetch];
  128. }
  129. //MARK: -- 强制无效缓存
  130. // 无效缓存,会根据inetAddress的host,无效host对应的ip缓存
  131. - (void)invalidInetAdress:(id <QNInetAddressDelegate>)inetAddress{
  132. NSArray *inetAddressList = self.addressDictionary[inetAddress.hostValue];
  133. NSMutableArray *inetAddressListNew = [NSMutableArray array];
  134. for (id <QNInetAddressDelegate> inetAddressP in inetAddressList) {
  135. if (![inetAddress.ipValue isEqualToString:inetAddressP.ipValue]) {
  136. [inetAddressListNew addObject:inetAddressP];
  137. }
  138. }
  139. [self.addressDictionary setObject:[inetAddressListNew copy] forKey:inetAddress.hostValue];
  140. }
  141. //MARK: -- 读取缓存的DNS信息
  142. /// 根据host从缓存中读取DNS信息
  143. - (NSArray <id <QNInetAddressDelegate> > *)getInetAddressByHost:(NSString *)host{
  144. if ([self isDnsOpen] == NO) {
  145. return nil;
  146. }
  147. NSArray <QNInetAddress *> *addressList = self.addressDictionary[host];
  148. if (![addressList.firstObject isValid]) {
  149. QNAsyncRun(^{
  150. [[QNTransactionManager shared] setDnsCheckWhetherCachedValidTransactionAction];
  151. });
  152. }
  153. return addressList;
  154. }
  155. //MARK: --
  156. //MARK: -- 根据dns预取
  157. - (BOOL)prepareToPreFetch{
  158. if ([self isDnsOpen] == NO) {
  159. return NO;
  160. }
  161. if (self.isPrefetching == YES) {
  162. return NO;
  163. }
  164. NSString *localIp = [QNIP local];
  165. if (localIp == nil ||
  166. (self.dnsCacheInfo && ![localIp isEqualToString:self.dnsCacheInfo.localIp])) {
  167. [self clearPreHosts];
  168. }
  169. self.isPrefetching = YES;
  170. return YES;
  171. }
  172. - (void)endPreFetch{
  173. self.isPrefetching = NO;
  174. }
  175. - (void)preFetchHosts:(NSArray <NSString *> *)fetchHosts{
  176. self.httpDns.defaultTtl = kQNGlobalConfiguration.dnsCacheTime;
  177. NSArray *nextFetchHosts = fetchHosts;
  178. nextFetchHosts = [self preFetchHosts:nextFetchHosts
  179. dns:kQNGlobalConfiguration.dns];
  180. nextFetchHosts = [self preFetchHosts:nextFetchHosts
  181. dns:self.httpDns];
  182. }
  183. - (NSArray *)preFetchHosts:(NSArray <NSString *> *)preHosts
  184. dns:(id <QNDnsDelegate>)dns{
  185. if (!preHosts || preHosts.count == 0) {
  186. return nil;
  187. }
  188. if (!dns) {
  189. return [preHosts copy];
  190. }
  191. NSMutableArray *failHosts = [NSMutableArray array];
  192. for (NSString *host in preHosts) {
  193. int rePreNum = 0;
  194. BOOL isSuccess = NO;
  195. while (rePreNum < kQNGlobalConfiguration.dnsRepreHostNum) {
  196. if ([self preFetchHost:host dns:dns]) {
  197. isSuccess = YES;
  198. break;
  199. }
  200. }
  201. if (!isSuccess) {
  202. [failHosts addObject:host];
  203. }
  204. }
  205. return [failHosts copy];
  206. }
  207. - (BOOL)preFetchHost:(NSString *)preHost
  208. dns:(id <QNDnsDelegate>)dns{
  209. if (!preHost || preHost.length == 0) {
  210. return NO;
  211. }
  212. NSArray<QNInetAddress *>* preAddressList = self.addressDictionary[preHost];
  213. if (preAddressList && [preAddressList.firstObject isValid]) {
  214. return YES;
  215. }
  216. NSArray <id <QNInetAddressDelegate> > * addressList = [dns lookup:preHost];
  217. if (addressList && addressList.count > 0) {
  218. NSMutableArray *addressListP = [NSMutableArray array];
  219. for (id <QNInetAddressDelegate>inetAddress in addressList) {
  220. QNInetAddress *address = [QNInetAddress inetAddress:inetAddress];
  221. if (address) {
  222. address.hostValue = preHost;
  223. if (!address.ttlValue) {
  224. address.ttlValue = @(kQNDefaultDnsCacheTime);
  225. }
  226. if (!address.timestampValue) {
  227. address.timestampValue = @([[NSDate date] timeIntervalSince1970]);
  228. }
  229. [addressListP addObject:address];
  230. }
  231. }
  232. self.addressDictionary[preHost] = [addressListP copy];
  233. return YES;
  234. } else {
  235. return NO;
  236. }
  237. }
  238. //MARK: -- 加载和存储缓存信息
  239. - (BOOL)recoverDnsCache:(NSDictionary *)dataDic{
  240. if (dataDic == nil) {
  241. return NO;
  242. }
  243. NSMutableDictionary *newAddressDictionary = [NSMutableDictionary dictionary];
  244. for (NSString *key in dataDic.allKeys) {
  245. NSArray *ips = dataDic[key];
  246. if ([ips isKindOfClass:[NSArray class]]) {
  247. NSMutableArray <QNInetAddress *> * addressList = [NSMutableArray array];
  248. for (NSDictionary *ipInfo in ips) {
  249. if ([ipInfo isKindOfClass:[NSDictionary class]]) {
  250. QNInetAddress *address = [QNInetAddress inetAddress:ipInfo];
  251. if (address) {
  252. [addressList addObject:address];
  253. }
  254. }
  255. }
  256. if (addressList.count > 0) {
  257. newAddressDictionary[key] = [addressList copy];
  258. }
  259. }
  260. }
  261. self.addressDictionary = newAddressDictionary;
  262. NSLog(@"== recoverDnsCache");
  263. return NO;
  264. }
  265. - (BOOL)recorderDnsCache{
  266. NSTimeInterval currentTime = [QNUtils currentTimestamp];
  267. NSString *localIp = [QNIP local];
  268. if (localIp == nil || localIp.length == 0) {
  269. return NO;
  270. }
  271. NSError *error;
  272. id <QNRecorderDelegate> recorder = [QNDnsCacheFile dnsCacheFile:kQNGlobalConfiguration.dnscacheDir
  273. error:&error];
  274. if (error) {
  275. return NO;
  276. }
  277. NSMutableDictionary *addressInfo = [NSMutableDictionary dictionary];
  278. for (NSString *key in self.addressDictionary.allKeys) {
  279. NSArray *addressModelList = self.addressDictionary[key];
  280. NSMutableArray * addressDicList = [NSMutableArray array];
  281. for (QNInetAddress *ipInfo in addressModelList) {
  282. NSDictionary *addressDic = [ipInfo toDictionary];
  283. if (addressDic) {
  284. [addressDicList addObject:addressDic];
  285. }
  286. }
  287. if (addressDicList.count > 0) {
  288. addressInfo[key] = addressDicList;
  289. }
  290. }
  291. QNDnsCacheInfo *cacheInfo = [QNDnsCacheInfo dnsCacheInfo:[NSString stringWithFormat:@"%.0lf",currentTime]
  292. localIp:localIp
  293. info:addressInfo];
  294. NSData *cacheData = [cacheInfo jsonData];
  295. if (!cacheData) {
  296. return NO;
  297. }
  298. [self setDnsCacheInfo:cacheInfo];
  299. [recorder set:localIp data:cacheData];
  300. return true;
  301. }
  302. - (void)clearPreHosts{
  303. [self.addressDictionary removeAllObjects];
  304. }
  305. //MARK: -- 获取预取hosts
  306. - (NSArray <NSString *> *)getLocalPreHost{
  307. NSMutableArray *localHosts = [NSMutableArray array];
  308. NSArray *fixedHosts = [self getFixedZoneHosts];
  309. [localHosts addObjectsFromArray:fixedHosts];
  310. NSString *ucHost = kQNPreQueryHost;
  311. [localHosts addObject:ucHost];
  312. return [localHosts copy];
  313. }
  314. - (NSArray <NSString *> *)getAllPreHost:(QNZone *)currentZone
  315. token:(NSString *)token{
  316. NSMutableSet *set = [NSMutableSet set];
  317. NSMutableArray *fetchHosts = [NSMutableArray array];
  318. NSArray *fixedHosts = [self getFixedZoneHosts];
  319. [fetchHosts addObjectsFromArray:fixedHosts];
  320. NSArray *autoHosts = [self getCurrentZoneHosts:currentZone token:token];
  321. [fetchHosts addObjectsFromArray:autoHosts];
  322. NSString *ucHost = kQNPreQueryHost;
  323. [fetchHosts addObject:ucHost];
  324. NSArray *cacheHost = [self getCacheHosts];
  325. [fetchHosts addObjectsFromArray:cacheHost];
  326. NSMutableArray *fetchHostsFiltered = [NSMutableArray array];
  327. for (NSString *host in fetchHosts) {
  328. NSInteger countBeforeAdd = set.count;
  329. [set addObject:host];
  330. NSInteger countAfterAdd = set.count;
  331. if (countBeforeAdd < countAfterAdd) {
  332. [fetchHostsFiltered addObject:host];
  333. }
  334. }
  335. return [fetchHostsFiltered copy];
  336. }
  337. - (NSArray <NSString *> *)getCurrentZoneHosts:(QNZone *)currentZone
  338. token:(NSString *)token{
  339. if (!currentZone || !token) {
  340. return nil;
  341. }
  342. [currentZone preQuery:[QNUpToken parse:token] on:^(int code, QNHttpResponseInfo *httpResponseInfo) {
  343. dispatch_semaphore_signal(self.semaphore);
  344. }];
  345. dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
  346. QNZonesInfo *autoZonesInfo = [currentZone getZonesInfoWithToken:[QNUpToken parse:token]];
  347. NSMutableArray *autoHosts = [NSMutableArray array];
  348. NSArray *zoneInfoList = autoZonesInfo.zonesInfo;
  349. for (QNZoneInfo *info in zoneInfoList) {
  350. for (NSString *host in info.upDomainsList) {
  351. [autoHosts addObject:host];
  352. }
  353. }
  354. return [autoHosts copy];
  355. }
  356. - (NSArray <NSString *> *)getFixedZoneHosts{
  357. NSArray <QNFixedZone *> *fixedZones = [QNFixedZone localsZoneInfo];
  358. NSMutableArray *localHosts = [NSMutableArray array];
  359. for (QNFixedZone *fixZone in fixedZones) {
  360. QNZonesInfo *zonesInfo = [fixZone getZonesInfoWithToken:nil];
  361. for (QNZoneInfo *zoneInfo in zonesInfo.zonesInfo) {
  362. for (NSString *host in zoneInfo.upDomainsList) {
  363. [localHosts addObject:host];
  364. }
  365. }
  366. }
  367. return [localHosts copy];
  368. }
  369. - (NSArray <NSString *> *)getCacheHosts{
  370. return self.addressDictionary.allKeys;
  371. }
  372. //MARK: --
  373. - (BOOL)isDnsOpen{
  374. return [kQNGlobalConfiguration isDnsOpen];
  375. }
  376. - (NSMutableDictionary<NSString *,NSArray<QNInetAddress *> *> *)addressDictionary{
  377. if (_addressDictionary == nil) {
  378. _addressDictionary = [NSMutableDictionary dictionary];
  379. }
  380. return _addressDictionary;
  381. }
  382. - (dispatch_semaphore_t)semaphore{
  383. if (_getAutoZoneSemaphore == NULL) {
  384. _getAutoZoneSemaphore = dispatch_semaphore_create(0);
  385. }
  386. return _getAutoZoneSemaphore;
  387. }
  388. - (QNDnsManager *)httpDns{
  389. if (_httpDns == nil) {
  390. QNResolver *systemDnsresolver = [QNResolver systemResolver];
  391. QNDnspodFree *dnspodFree = [[QNDnspodFree alloc] init];
  392. QNDnsManager *httpDns = [[QNDnsManager alloc] init:@[systemDnsresolver, dnspodFree]
  393. networkInfo:nil];
  394. _httpDns = httpDns;
  395. }
  396. return _httpDns;
  397. }
  398. @end
  399. //MARK: -- DNS 事务
  400. @implementation QNTransactionManager(Dns)
  401. #define kQNLoadLocalDnstransactionName @"QNLoadLocalDnstransaction"
  402. #define kQNDnsCheckAndPrefetchtransactionName @"QNDnsCheckAndPrefetchtransactionName"
  403. - (void)addDnsLocalLoadTransaction{
  404. if ([kQNDnsPrefetcher isDnsOpen] == NO) {
  405. return;
  406. }
  407. static dispatch_once_t onceToken;
  408. dispatch_once(&onceToken, ^{
  409. QNTransaction *transaction = [QNTransaction transaction:kQNLoadLocalDnstransactionName after:0 action:^{
  410. [kQNDnsPrefetcher recoverCache];
  411. [kQNDnsPrefetcher localFetch];
  412. }];
  413. [[QNTransactionManager shared] addTransaction:transaction];
  414. });
  415. }
  416. - (BOOL)addDnsCheckAndPrefetchTransaction:(QNZone *)currentZone token:(NSString *)token{
  417. if (!token) {
  418. return NO;
  419. }
  420. if ([kQNDnsPrefetcher isDnsOpen] == NO) {
  421. return NO;
  422. }
  423. BOOL ret = NO;
  424. @synchronized (kQNDnsPrefetcher) {
  425. QNTransactionManager *transactionManager = [QNTransactionManager shared];
  426. if (![transactionManager existtransactionsForName:token]) {
  427. QNTransaction *transaction = [QNTransaction transaction:token after:0 action:^{
  428. [kQNDnsPrefetcher checkAndPrefetchDnsIfNeed:currentZone token:token];
  429. }];
  430. [transactionManager addTransaction:transaction];
  431. ret = YES;
  432. }
  433. }
  434. return ret;
  435. }
  436. - (void)setDnsCheckWhetherCachedValidTransactionAction{
  437. if ([kQNDnsPrefetcher isDnsOpen] == NO) {
  438. return;
  439. }
  440. @synchronized (kQNDnsPrefetcher) {
  441. QNTransactionManager *transactionManager = [QNTransactionManager shared];
  442. QNTransaction *transaction = [transactionManager transactionsForName:kQNDnsCheckAndPrefetchtransactionName].firstObject;
  443. if (!transaction) {
  444. QNTransaction *transaction = [QNTransaction timeTransaction:kQNDnsCheckAndPrefetchtransactionName
  445. after:10
  446. interval:120
  447. action:^{
  448. [kQNDnsPrefetcher checkWhetherCachedDnsValid];
  449. }];
  450. [transactionManager addTransaction:transaction];
  451. } else {
  452. [transactionManager performTransaction:transaction];
  453. }
  454. }
  455. }
  456. @end