123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- //
- // QNAutoZone.m
- // QiniuSDK
- //
- // Created by yangsen on 2020/4/16.
- // Copyright © 2020 Qiniu. All rights reserved.
- //
- #import "QNDefine.h"
- #import "QNAutoZone.h"
- #import "QNConfig.h"
- #import "QNRequestTransaction.h"
- #import "QNZoneInfo.h"
- #import "QNUpToken.h"
- #import "QNResponseInfo.h"
- #import "QNFixedZone.h"
- #import "QNSingleFlight.h"
- #import "QNUploadRequestMetrics.h"
- @interface QNAutoZoneCache : NSObject
- @property(nonatomic, strong)NSMutableDictionary *cache;
- @end
- @implementation QNAutoZoneCache
- + (instancetype)share {
- static QNAutoZoneCache *cache = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- cache = [[QNAutoZoneCache alloc] init];
- [cache setupData];
- });
- return cache;
- }
- - (void)setupData{
- self.cache = [NSMutableDictionary dictionary];
- }
- - (void)cache:(QNZonesInfo *)zonesInfo forKey:(NSString *)cacheKey{
-
- if (!cacheKey || [cacheKey isEqualToString:@""] || zonesInfo == nil) {
- return;
- }
-
- @synchronized (self) {
- self.cache[cacheKey] = zonesInfo;
- }
- }
- - (QNZonesInfo *)cacheForKey:(NSString *)cacheKey{
-
- if (!cacheKey || [cacheKey isEqualToString:@""]) {
- return nil;
- }
-
- @synchronized (self) {
- return self.cache[cacheKey];
- }
- }
- - (QNZonesInfo *)zonesInfoForKey:(NSString *)cacheKey{
-
- if (!cacheKey || [cacheKey isEqualToString:@""]) {
- return nil;
- }
-
- QNZonesInfo *zonesInfo = nil;
- @synchronized (self) {
- zonesInfo = self.cache[cacheKey];
- }
-
- return zonesInfo;
- }
- - (void)clearCache {
- @synchronized (self) {
- for (NSString *key in self.cache.allKeys) {
- QNZonesInfo *info = self.cache[key];
- [info toTemporary];
- }
- }
- }
- @end
- @interface QNUCQuerySingleFlightValue : NSObject
- @property(nonatomic, strong)QNResponseInfo *responseInfo;
- @property(nonatomic, strong)NSDictionary *response;
- @property(nonatomic, strong)QNUploadRegionRequestMetrics *metrics;
- @end
- @implementation QNUCQuerySingleFlightValue
- @end
- @interface QNAutoZone()
- @property(nonatomic, strong)NSArray *ucHosts;
- @property(nonatomic, strong)QNFixedZone *defaultZone;
- @property(nonatomic, strong)NSMutableArray <QNRequestTransaction *> *transactions;
- @end
- @implementation QNAutoZone
- + (QNSingleFlight *)UCQuerySingleFlight {
- static QNSingleFlight *singleFlight = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- singleFlight = [[QNSingleFlight alloc] init];
- });
- return singleFlight;
- }
- + (instancetype)zoneWithUcHosts:(NSArray *)ucHosts {
- QNAutoZone *zone = [[self alloc] init];
- zone.ucHosts = [ucHosts copy];
- return zone;
- }
- + (void)clearCache {
- [[QNAutoZoneCache share] clearCache];
- }
- - (instancetype)init{
- if (self = [super init]) {
- _transactions = [NSMutableArray array];
- }
- return self;
- }
- - (void)setDefaultZones:(NSArray <QNFixedZone *> *)zones {
- self.defaultZone = [QNFixedZone combineZones:zones];
- }
- - (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken * _Nullable)token {
-
- if (token == nil) return nil;
- NSString *cacheKey = [NSString stringWithFormat:@"%@", token.index] ;
- QNZonesInfo *zonesInfo = [[QNAutoZoneCache share] cacheForKey:cacheKey];
- zonesInfo = [zonesInfo copy];
- return zonesInfo;
- }
- - (void)preQuery:(QNUpToken *)token on:(QNPrequeryReturn)ret {
- if (token == nil || ![token isValid]) {
- ret(-1, [QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], nil);
- return;
- }
-
- QNUploadRegionRequestMetrics *cacheMetrics = [QNUploadRegionRequestMetrics emptyMetrics];
- [cacheMetrics start];
-
- NSString *cacheKey = [NSString stringWithFormat:@"%@", token.index] ;
- QNZonesInfo *zonesInfo = [[QNAutoZoneCache share] zonesInfoForKey:cacheKey];
-
- // 临时的 zonesInfo 仅能使用一次
- if (zonesInfo != nil && zonesInfo.isValid && !zonesInfo.isTemporary) {
- [cacheMetrics end];
- ret(0, [QNResponseInfo successResponse], cacheMetrics);
- return;
- }
-
- kQNWeakSelf;
- QNSingleFlight *singleFlight = [QNAutoZone UCQuerySingleFlight];
- [singleFlight perform:token.index action:^(QNSingleFlightComplete _Nonnull complete) {
- kQNStrongSelf;
- QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
-
- kQNWeakSelf;
- kQNWeakObj(transaction);
- [transaction queryUploadHosts:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
- kQNStrongSelf;
- kQNStrongObj(transaction);
-
- QNUCQuerySingleFlightValue *value = [[QNUCQuerySingleFlightValue alloc] init];
- value.responseInfo = responseInfo;
- value.response = response;
- value.metrics = metrics;
- complete(value, nil);
-
- [self destroyUploadRequestTransaction:transaction];
- }];
-
- } complete:^(id _Nullable value, NSError * _Nullable error) {
- QNResponseInfo *responseInfo = [(QNUCQuerySingleFlightValue *)value responseInfo];
- NSDictionary *response = [(QNUCQuerySingleFlightValue *)value response];
- QNUploadRegionRequestMetrics *metrics = [(QNUCQuerySingleFlightValue *)value metrics];
- if (responseInfo && responseInfo.isOK) {
- QNZonesInfo *zonesInfo = [QNZonesInfo infoWithDictionary:response];
- if ([zonesInfo isValid]) {
- [[QNAutoZoneCache share] cache:zonesInfo forKey:cacheKey];
- ret(0, responseInfo, metrics);
- } else {
- ret(-1, responseInfo, metrics);
- }
- } else {
- if (responseInfo.isConnectionBroken) {
- ret(kQNNetworkError, responseInfo, metrics);
- } else {
- QNZonesInfo *info = nil;
- if (self.defaultZone) {
- QNZonesInfo * infoP = [self.defaultZone getZonesInfoWithToken:token];
- if (infoP && [infoP isValid]) {
- [infoP toTemporary];
- info = infoP;
- }
- }
-
- if (info) {
- [[QNAutoZoneCache share] cache:info forKey:cacheKey];
- ret(0, responseInfo, metrics);
- } else {
- ret(-1, responseInfo, metrics);
- }
- }
- }
- }];
- }
- - (QNRequestTransaction *)createUploadRequestTransaction:(QNUpToken *)token{
- NSArray *hosts = nil;
- if (self.ucHosts && self.ucHosts.count > 0) {
- hosts = [self.ucHosts copy];
- } else {
- hosts = kQNPreQueryHosts;
- }
- QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithHosts:hosts
- regionId:QNZoneInfoEmptyRegionId
- token:token];
- @synchronized (self) {
- [self.transactions addObject:transaction];
- }
- return transaction;
- }
- - (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction{
- if (transaction) {
- @synchronized (self) {
- [self.transactions removeObject:transaction];
- }
- }
- }
- @end
|