QNAutoZone.m 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. //
  2. // QNAutoZone.m
  3. // QiniuSDK
  4. //
  5. // Created by yangsen on 2020/4/16.
  6. // Copyright © 2020 Qiniu. All rights reserved.
  7. //
  8. #import "QNDefine.h"
  9. #import "QNAutoZone.h"
  10. #import "QNConfig.h"
  11. #import "QNRequestTransaction.h"
  12. #import "QNZoneInfo.h"
  13. #import "QNUpToken.h"
  14. #import "QNResponseInfo.h"
  15. #import "QNFixedZone.h"
  16. #import "QNSingleFlight.h"
  17. @interface QNAutoZoneCache : NSObject
  18. @property(nonatomic, strong)NSMutableDictionary *cache;
  19. @end
  20. @implementation QNAutoZoneCache
  21. + (instancetype)share{
  22. static QNAutoZoneCache *cache = nil;
  23. static dispatch_once_t onceToken;
  24. dispatch_once(&onceToken, ^{
  25. cache = [[QNAutoZoneCache alloc] init];
  26. [cache setupData];
  27. });
  28. return cache;
  29. }
  30. - (void)setupData{
  31. self.cache = [NSMutableDictionary dictionary];
  32. }
  33. - (void)cache:(NSDictionary *)zonesInfo forKey:(NSString *)cacheKey{
  34. if (!cacheKey || [cacheKey isEqualToString:@""]) {
  35. return;
  36. }
  37. @synchronized (self) {
  38. if (zonesInfo) {
  39. self.cache[cacheKey] = zonesInfo;
  40. } else {
  41. [self.cache removeObjectForKey:cacheKey];
  42. }
  43. }
  44. }
  45. - (QNZonesInfo *)zonesInfoForKey:(NSString *)cacheKey{
  46. if (!cacheKey || [cacheKey isEqualToString:@""]) {
  47. return nil;
  48. }
  49. NSDictionary *zonesInfoDic = nil;
  50. @synchronized (self) {
  51. zonesInfoDic = self.cache[cacheKey];
  52. }
  53. if (zonesInfoDic == nil) {
  54. return nil;
  55. }
  56. return [QNZonesInfo infoWithDictionary:zonesInfoDic];
  57. }
  58. @end
  59. @interface QNUCQuerySingleFlightValue : NSObject
  60. @property(nonatomic, strong)QNResponseInfo *responseInfo;
  61. @property(nonatomic, strong)NSDictionary *response;
  62. @property(nonatomic, strong)QNUploadRegionRequestMetrics *metrics;
  63. @end
  64. @implementation QNUCQuerySingleFlightValue
  65. @end
  66. @interface QNAutoZone()
  67. @property(nonatomic, strong)NSMutableDictionary *cache;
  68. @property(nonatomic, strong)NSLock *lock;
  69. @property(nonatomic, strong)NSMutableArray <QNRequestTransaction *> *transactions;
  70. @end
  71. @implementation QNAutoZone
  72. + (QNSingleFlight *)UCQuerySingleFlight {
  73. static QNSingleFlight *singleFlight = nil;
  74. static dispatch_once_t onceToken;
  75. dispatch_once(&onceToken, ^{
  76. singleFlight = [[QNSingleFlight alloc] init];
  77. });
  78. return singleFlight;
  79. }
  80. - (instancetype)init{
  81. if (self = [super init]) {
  82. _cache = [NSMutableDictionary new];
  83. _lock = [NSLock new];
  84. _transactions = [NSMutableArray array];
  85. }
  86. return self;
  87. }
  88. - (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken *)token {
  89. if (token == nil) return nil;
  90. [self.lock lock];
  91. QNZonesInfo *zonesInfo = [_cache objectForKey:[token index]];
  92. [self.lock unlock];
  93. return zonesInfo;
  94. }
  95. - (void)preQuery:(QNUpToken *)token
  96. on:(QNPrequeryReturn)ret {
  97. if (token == nil || ![token isValid]) {
  98. ret(-1, [QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], nil);
  99. return;
  100. }
  101. NSString *cacheKey = token.index;
  102. [_lock lock];
  103. QNZonesInfo *zonesInfo = [_cache objectForKey:cacheKey];
  104. [_lock unlock];
  105. if (zonesInfo == nil) {
  106. zonesInfo = [[QNAutoZoneCache share] zonesInfoForKey:cacheKey];
  107. if (zonesInfo && zonesInfo.isValid) {
  108. [self.lock lock];
  109. [self.cache setValue:zonesInfo forKey:cacheKey];
  110. [self.lock unlock];
  111. }
  112. }
  113. if (zonesInfo != nil && zonesInfo.isValid) {
  114. ret(0, [QNResponseInfo successResponse], nil);
  115. return;
  116. }
  117. kQNWeakSelf;
  118. QNSingleFlight *singleFlight = [QNAutoZone UCQuerySingleFlight];
  119. [singleFlight perform:token.index action:^(QNSingleFlightComplete _Nonnull complete) {
  120. kQNStrongSelf;
  121. QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
  122. kQNWeakSelf;
  123. kQNWeakObj(transaction);
  124. [transaction queryUploadHosts:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
  125. kQNStrongSelf;
  126. kQNStrongObj(transaction);
  127. QNUCQuerySingleFlightValue *value = [[QNUCQuerySingleFlightValue alloc] init];
  128. value.responseInfo = responseInfo;
  129. value.response = response;
  130. value.metrics = metrics;
  131. complete(value, nil);
  132. [self destroyUploadRequestTransaction:transaction];
  133. }];
  134. } complete:^(id _Nullable value, NSError * _Nullable error) {
  135. kQNStrongSelf;
  136. QNResponseInfo *responseInfo = [(QNUCQuerySingleFlightValue *)value responseInfo];
  137. NSDictionary *response = [(QNUCQuerySingleFlightValue *)value response];
  138. QNUploadRegionRequestMetrics *metrics = [(QNUCQuerySingleFlightValue *)value metrics];
  139. if (responseInfo && responseInfo.isOK) {
  140. QNZonesInfo *zonesInfo = [QNZonesInfo infoWithDictionary:response];
  141. [self.lock lock];
  142. [self.cache setValue:zonesInfo forKey:cacheKey];
  143. [self.lock unlock];
  144. [[QNAutoZoneCache share] cache:response forKey:cacheKey];
  145. ret(0, responseInfo, metrics);
  146. } else {
  147. if (responseInfo.isConnectionBroken) {
  148. ret(kQNNetworkError, responseInfo, metrics);
  149. } else {
  150. QNZonesInfo *zonesInfo = [[QNFixedZone localsZoneInfo] getZonesInfoWithToken:token];
  151. [self.lock lock];
  152. [self.cache setValue:zonesInfo forKey:cacheKey];
  153. [self.lock unlock];
  154. ret(0, responseInfo, metrics);
  155. }
  156. }
  157. }];
  158. }
  159. - (QNRequestTransaction *)createUploadRequestTransaction:(QNUpToken *)token{
  160. QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithHosts:@[kQNPreQueryHost00, kQNPreQueryHost01]
  161. regionId:QNZoneInfoEmptyRegionId
  162. token:token];
  163. [self.lock lock];
  164. [self.transactions addObject:transaction];
  165. [self.lock unlock];
  166. return transaction;
  167. }
  168. - (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction{
  169. if (transaction) {
  170. [self.lock lock];
  171. [self.transactions removeObject:transaction];
  172. [self.lock unlock];
  173. }
  174. }
  175. @end