QNUploadInfoCollector.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. //
  2. // QNUploadInfoCollector.m
  3. // QiniuSDK
  4. //
  5. // Created by WorkSpace_Sun on 2020/4/15.
  6. // Copyright © 2020 Qiniu. All rights reserved.
  7. //
  8. #import "QNUploadInfoCollector.h"
  9. #import "QNUploadInfoReporter.h"
  10. #import "QNHttpResponseInfo.h"
  11. #import "QNResponseInfo.h"
  12. QNCollectKey *const CK_bucket = @"bucket";
  13. QNCollectKey *const CK_key = @"key";
  14. QNCollectKey *const CK_targetRegionId = @"targetRegionId";
  15. QNCollectKey *const CK_currentRegionId = @"currentRegionId";
  16. QNCollectKey *const CK_result = @"result";
  17. QNCollectKey *const CK_blockBytesSent = @"blockBytesSent";
  18. QNCollectKey *const CK_recoveredFrom = @"recoveredFrom";
  19. QNCollectKey *const CK_totalBytesSent = @"totalBytesSent";
  20. QNCollectKey *const CK_fileSize = @"fileSize";
  21. QNCollectKey *const CK_blockApiVersion = @"blockApiVersion";
  22. int64_t QN_IntNotSet = -11111111;
  23. // Upload Result Type
  24. NSString *const upload_ok = @"ok";
  25. NSString *const zero_size_file = @"zero_size_file";
  26. NSString *const invalid_file = @"invalid_file";
  27. NSString *const invalid_args = @"invalid_args";
  28. NSString *const local_io_error = @"local_io_error";
  29. // Network Error Type
  30. NSString *const unknown_error = @"unknown_error";
  31. NSString *const network_error = @"network_error";
  32. NSString *const network_timeout = @"timeout";
  33. NSString *const unknown_host = @"unknown_host";
  34. NSString *const cannot_connect_to_host = @"cannot_connect_to_host";
  35. NSString *const transmission_error = @"transmission_error";
  36. NSString *const proxy_error = @"proxy_error";
  37. NSString *const ssl_error = @"ssl_error";
  38. NSString *const response_error = @"response_error";
  39. NSString *const parse_error = @"parse_error";
  40. NSString *const malicious_response = @"malicious_response";
  41. NSString *const user_canceled = @"user_canceled";
  42. NSString *const bad_request = @"bad_request";
  43. NSString *const protocol_error = @"protocol_error";
  44. static NSString *const requestTypes[] = {@"form", @"mkblk", @"bput", @"mkfile", @"put", @"init_parts", @"upload_part", @"complete_part", @"uc_query", @"httpdns_query"};
  45. @interface QNCollectItem : NSObject
  46. @property (nonatomic, copy) NSString *identifier;
  47. @property (nonatomic, copy) NSString *bucket;
  48. @property (nonatomic, copy) NSString *key;
  49. @property (nonatomic, copy) NSString *token;
  50. @property (nonatomic, copy) NSString *targetRegionId;
  51. @property (nonatomic, copy) NSString *currentRegionId;
  52. @property (nonatomic, copy) NSString *result;
  53. @property (nonatomic, assign) int64_t uploadStartTime;
  54. @property (nonatomic, assign) int64_t uploadEndTime;
  55. @property (nonatomic, assign) int64_t totalBytesSent;
  56. @property (nonatomic, assign) int64_t fileSize;
  57. @property (nonatomic, assign) int64_t recoveredFrom;
  58. @property (nonatomic, assign) int64_t blockApiVersion;
  59. @property (nonatomic, assign) int64_t blockBytesSent;
  60. @property (nonatomic, strong) NSMutableArray<QNHttpResponseInfo *> *httpRequestList;
  61. @end
  62. @implementation QNCollectItem
  63. - (instancetype)initWithIdentifier:(NSString *)identifier token:(NSString *)token
  64. {
  65. self = [super init];
  66. if (self) {
  67. _identifier = identifier;
  68. _token = token;
  69. _uploadStartTime = QN_IntNotSet;
  70. _uploadEndTime = QN_IntNotSet;
  71. _fileSize = QN_IntNotSet;
  72. _recoveredFrom = QN_IntNotSet;
  73. _blockApiVersion = QN_IntNotSet;
  74. _totalBytesSent = 0;
  75. _blockBytesSent = 0;
  76. _httpRequestList = [NSMutableArray array];
  77. }
  78. return self;
  79. }
  80. @end
  81. @interface QNUploadInfoCollector ()
  82. @property (nonatomic, strong) NSArray<QNCollectKey *> *updateKeysList;
  83. @property (nonatomic, strong) NSArray <QNCollectKey *>*appendKeysList;
  84. @property (nonatomic, strong) NSMutableArray<QNCollectItem *> *collectItemList;
  85. @property (nonatomic, strong) dispatch_queue_t collectQueue;
  86. @end
  87. @implementation QNUploadInfoCollector
  88. + (instancetype)sharedInstance {
  89. static QNUploadInfoCollector *sharedInstance = nil;
  90. static dispatch_once_t onceToken;
  91. dispatch_once(&onceToken, ^{
  92. sharedInstance = [[self alloc] init];
  93. sharedInstance.collectItemList = [NSMutableArray array];
  94. sharedInstance.collectQueue = dispatch_queue_create("com.qiniu.collector", DISPATCH_QUEUE_SERIAL);
  95. sharedInstance.updateKeysList = @[
  96. CK_bucket,
  97. CK_key,
  98. CK_targetRegionId,
  99. CK_currentRegionId,
  100. CK_result,
  101. CK_recoveredFrom,
  102. CK_fileSize,
  103. CK_blockApiVersion];
  104. sharedInstance.appendKeysList = @[
  105. CK_blockBytesSent,
  106. CK_totalBytesSent];
  107. });
  108. return sharedInstance;
  109. }
  110. - (void)registerWithIdentifier:(NSString *)identifier token:(NSString *)token {
  111. if (!identifier || !token) return;
  112. dispatch_async(_collectQueue, ^{
  113. QNCollectItem *item = [[QNCollectItem alloc] initWithIdentifier:identifier token:token];
  114. item.uploadStartTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  115. [self.collectItemList addObject:item];
  116. });
  117. }
  118. - (void)update:(QNCollectKey *)key value:(id)value identifier:(NSString *)identifier {
  119. if (!identifier || !key || ![self.updateKeysList containsObject:key]) return;
  120. dispatch_async(_collectQueue, ^{
  121. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  122. if (currentItem) {
  123. [currentItem setValue:value forKey:key];
  124. }
  125. });
  126. }
  127. - (void)append:(QNCollectKey *)key value:(id)value identifier:(NSString *)identifier {
  128. if (!identifier || !key || ![self.appendKeysList containsObject:key]) return;
  129. dispatch_async(_collectQueue, ^{
  130. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  131. if (currentItem) {
  132. // append NSNumber value
  133. NSNumber *formalValue = [currentItem valueForKey:key];
  134. NSNumber *appendValue = (NSNumber *)value;
  135. int64_t newValue = formalValue.longValue + appendValue.longValue;
  136. [currentItem setValue:@(newValue) forKey:key];
  137. }
  138. });
  139. }
  140. - (void)addRequestWithType:(QNRequestType)upType httpResponseInfo:(QNHttpResponseInfo *)httpResponseInfo fileOffset:(int64_t)fileOffset targetRegionId:(NSString *)targetRegionId currentRegionId:(NSString *)currentRegionId identifier:(NSString *)identifier {
  141. if (!identifier || !httpResponseInfo) return;
  142. dispatch_async(_collectQueue, ^{
  143. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  144. if (currentItem) {
  145. [currentItem.httpRequestList addObject:httpResponseInfo];
  146. if ([QNReportConfig sharedInstance].isReportEnable) {
  147. // 分块上传bytesSent字段有误差 这里分开处理
  148. int64_t bytesSent;
  149. if (upType == QNRequestType_mkblk || upType == QNRequestType_bput) {
  150. if (httpResponseInfo.hasHttpResponse) {
  151. bytesSent = httpResponseInfo.bytesTotal;
  152. } else {
  153. bytesSent = 0;
  154. }
  155. } else {
  156. bytesSent = httpResponseInfo.bytesSent;
  157. }
  158. QNReportRequestItem *item = [QNReportRequestItem buildWithUpType:requestTypes[upType]
  159. TargetBucket:currentItem.bucket
  160. targetKey:currentItem.key
  161. fileOffset:fileOffset
  162. targetRegionId:targetRegionId
  163. currentRegionId:currentRegionId
  164. prefetchedIpCount:QN_IntNotSet
  165. pid:httpResponseInfo.pid
  166. tid:httpResponseInfo.tid
  167. statusCode:httpResponseInfo.statusCode
  168. reqId:httpResponseInfo.reqId
  169. host:httpResponseInfo.host
  170. remoteIp:httpResponseInfo.remoteIp
  171. port:httpResponseInfo.port totalElapsedTime:httpResponseInfo.totalElapsedTime dnsElapsedTime:httpResponseInfo.dnsElapsedTime connectElapsedTime:httpResponseInfo.connectElapsedTime tlsConnectElapsedTime:httpResponseInfo.tlsConnectElapsedTime requestElapsedTime:httpResponseInfo.requestElapsedTime waitElapsedTime:httpResponseInfo.waitElapsedTime responseElapsedTime:httpResponseInfo.responseElapsedTime bytesSent:bytesSent bytesTotal:httpResponseInfo.bytesTotal errorType:httpResponseInfo.errorType errorDescription:httpResponseInfo.errorDescription networkType:httpResponseInfo.networkType signalStrength:httpResponseInfo.signalStrength];
  172. [Reporter report:[item toJson] token:currentItem.token];
  173. }
  174. }
  175. });
  176. }
  177. - (QNResponseInfo *)completeWithHttpResponseInfo:(QNHttpResponseInfo *)lastHttpResponseInfo identifier:(NSString *)identifier {
  178. __block QNResponseInfo *info;
  179. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  180. dispatch_async(_collectQueue, ^{
  181. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  182. if (lastHttpResponseInfo.isOK) {
  183. currentItem.result = upload_ok;
  184. } else {
  185. currentItem.result = lastHttpResponseInfo.errorType;
  186. }
  187. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  188. info = [QNResponseInfo responseInfoWithHttpResponseInfo:lastHttpResponseInfo duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  189. dispatch_semaphore_signal(signal);
  190. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  191. [self.collectItemList removeObject:currentItem];
  192. });
  193. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  194. return info;
  195. }
  196. - (QNResponseInfo *)completeWithInvalidArgument:(NSString *)text identifier:(NSString *)identifier {
  197. __block QNResponseInfo *info;
  198. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  199. dispatch_async(_collectQueue, ^{
  200. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  201. currentItem.result = invalid_args;
  202. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  203. info = [QNResponseInfo responseInfoWithInvalidArgument:text duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  204. dispatch_semaphore_signal(signal);
  205. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  206. [self.collectItemList removeObject:currentItem];
  207. });
  208. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  209. return info;
  210. }
  211. - (QNResponseInfo *)completeWithInvalidToken:(NSString *)text identifier:(NSString *)identifier {
  212. __block QNResponseInfo *info;
  213. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  214. dispatch_async(_collectQueue, ^{
  215. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  216. currentItem.result = invalid_args;
  217. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  218. info = [QNResponseInfo responseInfoWithInvalidToken:text duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  219. dispatch_semaphore_signal(signal);
  220. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  221. [self.collectItemList removeObject:currentItem];
  222. });
  223. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  224. return info;
  225. }
  226. - (QNResponseInfo *)completeWithFileError:(NSError *)error identifier:(NSString *)identifier {
  227. __block QNResponseInfo *info;
  228. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  229. dispatch_async(_collectQueue, ^{
  230. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  231. currentItem.result = invalid_file;
  232. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  233. info = [QNResponseInfo responseInfoWithFileError:error duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  234. dispatch_semaphore_signal(signal);
  235. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  236. [self.collectItemList removeObject:currentItem];
  237. });
  238. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  239. return info;
  240. }
  241. - (QNResponseInfo *)completeWithLocalIOError:(NSError *)error identifier:(NSString *)identifier {
  242. __block QNResponseInfo *info;
  243. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  244. dispatch_async(_collectQueue, ^{
  245. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  246. currentItem.result = local_io_error;
  247. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  248. info = [QNResponseInfo responseInfoWithFileError:error duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  249. dispatch_semaphore_signal(signal);
  250. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  251. [self.collectItemList removeObject:currentItem];
  252. });
  253. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  254. return info;
  255. }
  256. - (QNResponseInfo *)completeWithZeroData:(NSString *)path identifier:(NSString *)identifier {
  257. __block QNResponseInfo *info;
  258. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  259. dispatch_async(_collectQueue, ^{
  260. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  261. currentItem.result = zero_size_file;
  262. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  263. info = [QNResponseInfo responseInfoOfZeroData:path duration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  264. dispatch_semaphore_signal(signal);
  265. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  266. [self.collectItemList removeObject:currentItem];
  267. });
  268. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  269. return info;
  270. }
  271. - (QNResponseInfo *)userCancel:(NSString *)identifier {
  272. __block QNResponseInfo *info;
  273. dispatch_semaphore_t signal = dispatch_semaphore_create(0);
  274. dispatch_async(_collectQueue, ^{
  275. QNCollectItem *currentItem = [self getCurrentItemWithIdentifier:identifier];
  276. currentItem.result = user_canceled;
  277. currentItem.uploadEndTime = [[NSDate dateWithTimeIntervalSinceNow:0] timeIntervalSince1970] * 1000;
  278. info = [QNResponseInfo cancelWithDuration:(currentItem.uploadEndTime - currentItem.uploadStartTime) / 1000.0];
  279. dispatch_semaphore_signal(signal);
  280. if ([QNReportConfig sharedInstance].isReportEnable) [self reportResult:currentItem];
  281. [self.collectItemList removeObject:currentItem];
  282. });
  283. dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
  284. return info;
  285. }
  286. - (void)reportResult:(QNCollectItem *)currentItem {
  287. int64_t regionsCount = !currentItem.targetRegionId || !currentItem.currentRegionId || [currentItem.targetRegionId isEqualToString:currentItem.currentRegionId] ? 1 : 2;
  288. int64_t totalElapsedTime = currentItem.uploadEndTime - currentItem.uploadStartTime;
  289. if (currentItem.blockApiVersion != QN_IntNotSet) {
  290. QNReportBlockItem *item = [QNReportBlockItem buildWithTargetRegionId:currentItem.targetRegionId currentRegionId:currentItem.currentRegionId totalElapsedTime:totalElapsedTime bytesSent:currentItem.blockBytesSent recoveredFrom:currentItem.recoveredFrom fileSize:currentItem.fileSize pid:QN_IntNotSet tid:QN_IntNotSet upApiVersion:currentItem.blockApiVersion];
  291. [Reporter report:[item toJson] token:currentItem.token];
  292. }
  293. QNReportQualityItem *item = [QNReportQualityItem buildWithResult:currentItem.result totalElapsedTime:totalElapsedTime requestsCount:currentItem.httpRequestList.count regionsCount:regionsCount bytesSent:currentItem.totalBytesSent];
  294. [Reporter report:[item toJson] token:currentItem.token];
  295. }
  296. - (QNCollectItem *)getCurrentItemWithIdentifier:(NSString *)identifier {
  297. QNCollectItem *item = nil;
  298. for (NSInteger i = 0; i < self.collectItemList.count; i++) {
  299. QNCollectItem *currentItem = self.collectItemList[i];
  300. if ([currentItem.identifier isEqualToString:identifier]) {
  301. item = currentItem;
  302. break;
  303. }
  304. }
  305. return item;
  306. }
  307. @end