QNAutoZone.m 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. #import "QNUploadRequestMetrics.h"
  18. @interface QNAutoZoneCache : NSObject
  19. @property(nonatomic, strong)NSMutableDictionary *cache;
  20. @end
  21. @implementation QNAutoZoneCache
  22. + (instancetype)share {
  23. static QNAutoZoneCache *cache = nil;
  24. static dispatch_once_t onceToken;
  25. dispatch_once(&onceToken, ^{
  26. cache = [[QNAutoZoneCache alloc] init];
  27. [cache setupData];
  28. });
  29. return cache;
  30. }
  31. - (void)setupData{
  32. self.cache = [NSMutableDictionary dictionary];
  33. }
  34. - (void)cache:(QNZonesInfo *)zonesInfo forKey:(NSString *)cacheKey{
  35. if (!cacheKey || [cacheKey isEqualToString:@""] || zonesInfo == nil) {
  36. return;
  37. }
  38. @synchronized (self) {
  39. self.cache[cacheKey] = zonesInfo;
  40. }
  41. }
  42. - (QNZonesInfo *)cacheForKey:(NSString *)cacheKey{
  43. if (!cacheKey || [cacheKey isEqualToString:@""]) {
  44. return nil;
  45. }
  46. @synchronized (self) {
  47. return self.cache[cacheKey];
  48. }
  49. }
  50. - (QNZonesInfo *)zonesInfoForKey:(NSString *)cacheKey{
  51. if (!cacheKey || [cacheKey isEqualToString:@""]) {
  52. return nil;
  53. }
  54. QNZonesInfo *zonesInfo = nil;
  55. @synchronized (self) {
  56. zonesInfo = self.cache[cacheKey];
  57. }
  58. return zonesInfo;
  59. }
  60. - (void)clearCache {
  61. @synchronized (self) {
  62. for (NSString *key in self.cache.allKeys) {
  63. QNZonesInfo *info = self.cache[key];
  64. [info toTemporary];
  65. }
  66. }
  67. }
  68. @end
  69. @interface QNUCQuerySingleFlightValue : NSObject
  70. @property(nonatomic, strong)QNResponseInfo *responseInfo;
  71. @property(nonatomic, strong)NSDictionary *response;
  72. @property(nonatomic, strong)QNUploadRegionRequestMetrics *metrics;
  73. @end
  74. @implementation QNUCQuerySingleFlightValue
  75. @end
  76. @interface QNAutoZone()
  77. @property(nonatomic, strong)NSArray *ucHosts;
  78. @property(nonatomic, strong)QNFixedZone *defaultZone;
  79. @property(nonatomic, strong)NSMutableArray <QNRequestTransaction *> *transactions;
  80. @end
  81. @implementation QNAutoZone
  82. + (QNSingleFlight *)UCQuerySingleFlight {
  83. static QNSingleFlight *singleFlight = nil;
  84. static dispatch_once_t onceToken;
  85. dispatch_once(&onceToken, ^{
  86. singleFlight = [[QNSingleFlight alloc] init];
  87. });
  88. return singleFlight;
  89. }
  90. + (instancetype)zoneWithUcHosts:(NSArray *)ucHosts {
  91. QNAutoZone *zone = [[self alloc] init];
  92. zone.ucHosts = [ucHosts copy];
  93. return zone;
  94. }
  95. + (void)clearCache {
  96. [[QNAutoZoneCache share] clearCache];
  97. }
  98. - (instancetype)init{
  99. if (self = [super init]) {
  100. _transactions = [NSMutableArray array];
  101. }
  102. return self;
  103. }
  104. - (void)setDefaultZones:(NSArray <QNFixedZone *> *)zones {
  105. self.defaultZone = [QNFixedZone combineZones:zones];
  106. }
  107. - (QNZonesInfo *)getZonesInfoWithToken:(QNUpToken * _Nullable)token {
  108. if (token == nil) return nil;
  109. NSString *cacheKey = [NSString stringWithFormat:@"%@", token.index] ;
  110. QNZonesInfo *zonesInfo = [[QNAutoZoneCache share] cacheForKey:cacheKey];
  111. zonesInfo = [zonesInfo copy];
  112. return zonesInfo;
  113. }
  114. - (void)preQuery:(QNUpToken *)token on:(QNPrequeryReturn)ret {
  115. if (token == nil || ![token isValid]) {
  116. ret(-1, [QNResponseInfo responseInfoWithInvalidToken:@"invalid token"], nil);
  117. return;
  118. }
  119. QNUploadRegionRequestMetrics *cacheMetrics = [QNUploadRegionRequestMetrics emptyMetrics];
  120. [cacheMetrics start];
  121. NSString *cacheKey = [NSString stringWithFormat:@"%@", token.index] ;
  122. QNZonesInfo *zonesInfo = [[QNAutoZoneCache share] zonesInfoForKey:cacheKey];
  123. // 临时的 zonesInfo 仅能使用一次
  124. if (zonesInfo != nil && zonesInfo.isValid && !zonesInfo.isTemporary) {
  125. [cacheMetrics end];
  126. ret(0, [QNResponseInfo successResponse], cacheMetrics);
  127. return;
  128. }
  129. kQNWeakSelf;
  130. QNSingleFlight *singleFlight = [QNAutoZone UCQuerySingleFlight];
  131. [singleFlight perform:token.index action:^(QNSingleFlightComplete _Nonnull complete) {
  132. kQNStrongSelf;
  133. QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
  134. kQNWeakSelf;
  135. kQNWeakObj(transaction);
  136. [transaction queryUploadHosts:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
  137. kQNStrongSelf;
  138. kQNStrongObj(transaction);
  139. QNUCQuerySingleFlightValue *value = [[QNUCQuerySingleFlightValue alloc] init];
  140. value.responseInfo = responseInfo;
  141. value.response = response;
  142. value.metrics = metrics;
  143. complete(value, nil);
  144. [self destroyUploadRequestTransaction:transaction];
  145. }];
  146. } complete:^(id _Nullable value, NSError * _Nullable error) {
  147. QNResponseInfo *responseInfo = [(QNUCQuerySingleFlightValue *)value responseInfo];
  148. NSDictionary *response = [(QNUCQuerySingleFlightValue *)value response];
  149. QNUploadRegionRequestMetrics *metrics = [(QNUCQuerySingleFlightValue *)value metrics];
  150. if (responseInfo && responseInfo.isOK) {
  151. QNZonesInfo *zonesInfo = [QNZonesInfo infoWithDictionary:response];
  152. if ([zonesInfo isValid]) {
  153. [[QNAutoZoneCache share] cache:zonesInfo forKey:cacheKey];
  154. ret(0, responseInfo, metrics);
  155. } else {
  156. ret(-1, responseInfo, metrics);
  157. }
  158. } else {
  159. if (responseInfo.isConnectionBroken) {
  160. ret(kQNNetworkError, responseInfo, metrics);
  161. } else {
  162. QNZonesInfo *info = nil;
  163. if (self.defaultZone) {
  164. QNZonesInfo * infoP = [self.defaultZone getZonesInfoWithToken:token];
  165. if (infoP && [infoP isValid]) {
  166. [infoP toTemporary];
  167. info = infoP;
  168. }
  169. }
  170. if (info) {
  171. [[QNAutoZoneCache share] cache:info forKey:cacheKey];
  172. ret(0, responseInfo, metrics);
  173. } else {
  174. ret(-1, responseInfo, metrics);
  175. }
  176. }
  177. }
  178. }];
  179. }
  180. - (QNRequestTransaction *)createUploadRequestTransaction:(QNUpToken *)token{
  181. NSArray *hosts = nil;
  182. if (self.ucHosts && self.ucHosts.count > 0) {
  183. hosts = [self.ucHosts copy];
  184. } else {
  185. hosts = kQNPreQueryHosts;
  186. }
  187. QNRequestTransaction *transaction = [[QNRequestTransaction alloc] initWithHosts:hosts
  188. regionId:QNZoneInfoEmptyRegionId
  189. token:token];
  190. @synchronized (self) {
  191. [self.transactions addObject:transaction];
  192. }
  193. return transaction;
  194. }
  195. - (void)destroyUploadRequestTransaction:(QNRequestTransaction *)transaction{
  196. if (transaction) {
  197. @synchronized (self) {
  198. [self.transactions removeObject:transaction];
  199. }
  200. }
  201. }
  202. @end