Skip to content

Commit 9e8157a

Browse files
authored
Merge pull request #384 from YangSen-qn/develop
UC Query Add SingleFlight & connect check
2 parents f8d1a29 + df8344c commit 9e8157a

40 files changed

+796
-114
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
#Changelog
2+
## 8.1.2(2021-01-18)
3+
## 增加
4+
- 区域查询采用SingleFlight模式
5+
- 增加网络链接状态检测
6+
27
## 8.1.1(2021-01-06)
38
## 优化
49
- 优化日志统计
@@ -8,6 +13,7 @@
813
- 支持分片V2
914
- 支持自定义meta
1015
- 优化并发上传
16+
- 解决一些并发分片上传的野指针问题
1117

1218
## 8.0.5(2020-11-24)
1319
## 优化

Qiniu.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'Qiniu'
3-
s.version = '8.1.1'
3+
s.version = '8.1.2'
44
s.summary = 'Qiniu Resource Storage SDK for iOS and Mac'
55
s.homepage = 'https://github.com/qiniu/objc-sdk'
66
s.social_media_url = 'http://weibo.com/qiniutek'

QiniuSDK.xcodeproj/project.pbxproj

Lines changed: 54 additions & 10 deletions
Large diffs are not rendered by default.

QiniuSDK/Common/QNAutoZone.m

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#import "QNUpToken.h"
1515
#import "QNResponseInfo.h"
1616
#import "QNFixedZone.h"
17+
#import "QNSingleFlight.h"
18+
1719

1820
@interface QNAutoZoneCache : NSObject
1921
@property(nonatomic, strong)NSMutableDictionary *cache;
@@ -69,6 +71,16 @@ - (QNZonesInfo *)zonesInfoForKey:(NSString *)cacheKey{
6971

7072
@end
7173

74+
@interface QNUCQuerySingleFlightValue : NSObject
75+
76+
@property(nonatomic, strong)QNResponseInfo *responseInfo;
77+
@property(nonatomic, strong)NSDictionary *response;
78+
@property(nonatomic, strong)QNUploadRegionRequestMetrics *metrics;
79+
80+
@end
81+
@implementation QNUCQuerySingleFlightValue
82+
@end
83+
7284
@interface QNAutoZone()
7385

7486
@property(nonatomic, strong)NSMutableDictionary *cache;
@@ -78,6 +90,15 @@ @interface QNAutoZone()
7890
@end
7991
@implementation QNAutoZone
8092

93+
+ (QNSingleFlight *)UCQuerySingleFlight {
94+
static QNSingleFlight *singleFlight = nil;
95+
static dispatch_once_t onceToken;
96+
dispatch_once(&onceToken, ^{
97+
singleFlight = [[QNSingleFlight alloc] init];
98+
});
99+
return singleFlight;
100+
}
101+
81102
- (instancetype)init{
82103
if (self = [super init]) {
83104
_cache = [NSMutableDictionary new];
@@ -122,24 +143,43 @@ - (void)preQuery:(QNUpToken *)token
122143
ret(0, [QNResponseInfo successResponse], nil);
123144
return;
124145
}
125-
126-
QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
127146

128147
kQNWeakSelf;
129-
kQNWeakObj(transaction);
130-
[transaction queryUploadHosts:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
148+
QNSingleFlight *singleFlight = [QNAutoZone UCQuerySingleFlight];
149+
[singleFlight perform:token.index action:^(QNSingleFlightComplete _Nonnull complete) {
131150
kQNStrongSelf;
132-
kQNStrongObj(transaction);
151+
QNRequestTransaction *transaction = [self createUploadRequestTransaction:token];
152+
153+
kQNWeakSelf;
154+
kQNWeakObj(transaction);
155+
[transaction queryUploadHosts:^(QNResponseInfo * _Nullable responseInfo, QNUploadRegionRequestMetrics * _Nullable metrics, NSDictionary * _Nullable response) {
156+
kQNStrongSelf;
157+
kQNStrongObj(transaction);
158+
159+
QNUCQuerySingleFlightValue *value = [[QNUCQuerySingleFlightValue alloc] init];
160+
value.responseInfo = responseInfo;
161+
value.response = response;
162+
value.metrics = metrics;
163+
complete(value, nil);
164+
165+
[self destroyUploadRequestTransaction:transaction];
166+
}];
133167

134-
if (responseInfo.isOK) {
168+
} complete:^(id _Nullable value, NSError * _Nullable error) {
169+
kQNStrongSelf;
170+
171+
QNResponseInfo *responseInfo = [(QNUCQuerySingleFlightValue *)value responseInfo];
172+
NSDictionary *response = [(QNUCQuerySingleFlightValue *)value response];
173+
QNUploadRegionRequestMetrics *metrics = [(QNUCQuerySingleFlightValue *)value metrics];
174+
175+
if (responseInfo && responseInfo.isOK) {
135176
QNZonesInfo *zonesInfo = [QNZonesInfo infoWithDictionary:response];
136177
[self.lock lock];
137178
[self.cache setValue:zonesInfo forKey:cacheKey];
138179
[self.lock unlock];
139180
[[QNAutoZoneCache share] cache:response forKey:cacheKey];
140181
ret(0, responseInfo, metrics);
141182
} else {
142-
143183
if (responseInfo.isConnectionBroken) {
144184
ret(kQNNetworkError, responseInfo, metrics);
145185
} else {
@@ -150,7 +190,6 @@ - (void)preQuery:(QNUpToken *)token
150190
ret(0, responseInfo, metrics);
151191
}
152192
}
153-
[self destroyUploadRequestTransaction:transaction];
154193
}];
155194
}
156195

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// QNConnectChecker.h
3+
// QiniuSDK_Mac
4+
//
5+
// Created by yangsen on 2021/1/8.
6+
// Copyright © 2021 Qiniu. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface QNConnectChecker : NSObject
14+
15+
+ (BOOL)check;
16+
17+
+ (void)check:(void(^)(BOOL isConnected))complete;
18+
19+
@end
20+
21+
NS_ASSUME_NONNULL_END
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// QNConnectChecker.m
3+
// QiniuSDK_Mac
4+
//
5+
// Created by yangsen on 2021/1/8.
6+
// Copyright © 2021 Qiniu. All rights reserved.
7+
//
8+
9+
#import "QNDefine.h"
10+
#import "QNLogUtil.h"
11+
#import "QNConfiguration.h"
12+
#import "QNSingleFlight.h"
13+
#import "QNConnectChecker.h"
14+
#import "QNUploadSystemClient.h"
15+
16+
@interface QNConnectChecker()
17+
18+
@end
19+
@implementation QNConnectChecker
20+
21+
+ (QNSingleFlight *)singleFlight {
22+
static QNSingleFlight *singleFlight = nil;
23+
static dispatch_once_t onceToken;
24+
dispatch_once(&onceToken, ^{
25+
singleFlight = [[QNSingleFlight alloc] init];
26+
});
27+
return singleFlight;
28+
}
29+
30+
+ (BOOL)check {
31+
__block BOOL isConnected = false;
32+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
33+
[self check:^(BOOL isConnectedP) {
34+
isConnected = isConnectedP;
35+
dispatch_semaphore_signal(semaphore);
36+
}];
37+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
38+
return isConnected;
39+
}
40+
41+
+ (void)check:(void (^)(BOOL isConnected))complete {
42+
QNSingleFlight *singleFlight = [self singleFlight];
43+
44+
kQNWeakSelf;
45+
[singleFlight perform:@"connect_check" action:^(QNSingleFlightComplete _Nonnull singleFlightComplete) {
46+
kQNStrongSelf;
47+
48+
[self checkAllHosts:^(BOOL isConnected) {
49+
singleFlightComplete(@(isConnected), nil);
50+
}];
51+
52+
} complete:^(id _Nullable value, NSError * _Nullable error) {
53+
if (complete) {
54+
complete([(NSNumber *)value boolValue]);
55+
}
56+
}];
57+
}
58+
59+
60+
+ (void)checkAllHosts:(void (^)(BOOL isConnected))complete {
61+
62+
__block int completeCount = 0;
63+
__block BOOL isCompleted = false;
64+
__block BOOL isConnected = false;
65+
kQNWeakSelf;
66+
NSArray *allHosts = [kQNGlobalConfiguration.connectCheckURLStrings copy];
67+
for (NSString *host in allHosts) {
68+
[self checkHost:host complete:^(BOOL isHostConnected) {
69+
kQNStrongSelf;
70+
71+
@synchronized (self) {
72+
completeCount += 1;
73+
}
74+
if (isHostConnected) {
75+
isConnected = YES;
76+
}
77+
if (isHostConnected || completeCount == allHosts.count) {
78+
@synchronized (self) {
79+
if (isCompleted) {
80+
QNLogInfo(@"== check all hosts has completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
81+
return;
82+
} else {
83+
QNLogInfo(@"== check all hosts completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
84+
isCompleted = true;
85+
}
86+
}
87+
complete(isConnected);
88+
} else {
89+
QNLogInfo(@"== check all hosts not completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
90+
}
91+
}];
92+
}
93+
}
94+
95+
+ (void)checkHost:(NSString *)host complete:(void (^)(BOOL isConnected))complete {
96+
97+
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
98+
request.URL = [NSURL URLWithString:host];
99+
request.HTTPMethod = @"HEAD";
100+
request.timeoutInterval = kQNGlobalConfiguration.connectCheckTimeout;
101+
102+
QNUploadSystemClient *client = [[QNUploadSystemClient alloc] init];
103+
[client request:request connectionProxy:nil progress:nil complete:^(NSURLResponse * response, QNUploadSingleRequestMetrics * metrics, NSData * _Nullable data, NSError * error) {
104+
if (response && [(NSHTTPURLResponse *)response statusCode] > 99) {
105+
QNLogInfo(@"== checkHost:%@ result: true", host);
106+
complete(true);
107+
} else {
108+
QNLogInfo(@"== checkHost:%@ result: false", host);
109+
complete(false);
110+
}
111+
}];
112+
}
113+
114+
@end

QiniuSDK/Http/QNResponseInfo.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,7 @@ - (BOOL)canConnectToHost{
262262

263263
- (BOOL)isHostUnavailable{
264264
// 基本不可恢复,注:会影响下次请求,范围太大可能会造成大量的timeout
265-
if ((_statusCode >= -2000 && _statusCode <= -1200)
266-
|| _statusCode == 502 || _statusCode == 503 || _statusCode == 504 || _statusCode == 599) {
265+
if (_statusCode == 502 || _statusCode == 503 || _statusCode == 504 || _statusCode == 599) {
267266
return true;
268267
} else {
269268
return false;

QiniuSDK/Http/QNUploadRequestMetrics.m

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,10 @@ - (instancetype)init{
194194
}
195195

196196
- (NSNumber *)totalElapsedTime{
197-
if (self.metricsInfo) {
197+
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
198+
if (metricsInfo) {
198199
double time = 0;
199-
for (QNUploadRegionRequestMetrics *metrics in self.metricsInfo.allValues) {
200+
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
200201
time += metrics.totalElapsedTime.doubleValue;
201202
}
202203
return time > 0 ? @(time) : nil;
@@ -206,9 +207,10 @@ - (NSNumber *)totalElapsedTime{
206207
}
207208

208209
- (NSNumber *)requestCount{
209-
if (self.metricsInfo) {
210+
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
211+
if (metricsInfo) {
210212
NSInteger count = 0;
211-
for (QNUploadRegionRequestMetrics *metrics in self.metricsInfo.allValues) {
213+
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
212214
count += metrics.requestCount.integerValue;
213215
}
214216
return @(count);
@@ -218,9 +220,10 @@ - (NSNumber *)requestCount{
218220
}
219221

220222
- (NSNumber *)bytesSend{
221-
if (self.metricsInfo) {
223+
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
224+
if (metricsInfo) {
222225
long long bytes = 0;
223-
for (QNUploadRegionRequestMetrics *metrics in self.metricsInfo.allValues) {
226+
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
224227
bytes += metrics.bytesSend.longLongValue;
225228
}
226229
return @(bytes);
@@ -230,9 +233,10 @@ - (NSNumber *)bytesSend{
230233
}
231234

232235
- (NSNumber *)regionCount{
233-
if (self.metricsInfo) {
236+
NSDictionary *metricsInfo = [self syncCopyMetricsInfo];
237+
if (metricsInfo) {
234238
int count = 0;
235-
for (QNUploadRegionRequestMetrics *metrics in self.metricsInfo.allValues) {
239+
for (QNUploadRegionRequestMetrics *metrics in metricsInfo.allValues) {
236240
if (![metrics.region.zoneInfo.regionId isEqualToString:QNZoneInfoEmptyRegionId]) {
237241
count += 1;
238242
}
@@ -258,5 +262,11 @@ - (void)addMetrics:(QNUploadRegionRequestMetrics *)metrics{
258262
}
259263
}
260264

265+
- (NSDictionary<NSString *, QNUploadRegionRequestMetrics *> *)syncCopyMetricsInfo {
266+
@synchronized (self) {
267+
return [_metricsInfo copy];
268+
}
269+
}
270+
261271

262272
@end

QiniuSDK/Http/Request/QNHttpSingleRequest.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#import "QNResponseInfo.h"
1919
#import "QNRequestClient.h"
2020

21+
#import "QNConnectChecker.h"
2122
#import "QNDnsPrefetch.h"
2223

2324
#import "QNReportItem.h"
@@ -145,6 +146,11 @@ - (void)retryRequest:(NSURLRequest *)request
145146
response:(NSHTTPURLResponse *)response
146147
body:responseData
147148
error:error];
149+
if ([self shouldCheckConnect:responseInfo] && ![QNConnectChecker check]) {
150+
NSString *message = [NSString stringWithFormat:@"check origin statusCode:%d error:%@", responseInfo.statusCode, responseInfo.error];
151+
responseInfo = [QNResponseInfo errorResponseInfo:NSURLErrorNotConnectedToInternet errorDesc:message];
152+
}
153+
148154
QNLogInfo(@"key:%@ response:%@", self.requestInfo.key, responseInfo);
149155
if (shouldRetry(responseInfo, responseDic)
150156
&& self.currentRetryTime < self.config.retryMax
@@ -160,6 +166,16 @@ - (void)retryRequest:(NSURLRequest *)request
160166

161167
}
162168

169+
- (BOOL)shouldCheckConnect:(QNResponseInfo *)responseInfo {
170+
return responseInfo.statusCode == kQNNetworkError ||
171+
responseInfo.statusCode == -1001 /* NSURLErrorTimedOut */ ||
172+
responseInfo.statusCode == -1003 /* NSURLErrorCannotFindHost */ ||
173+
responseInfo.statusCode == -1004 /* NSURLErrorCannotConnectToHost */ ||
174+
responseInfo.statusCode == -1005 /* NSURLErrorNetworkConnectionLost */ ||
175+
responseInfo.statusCode == -1006 /* NSURLErrorDNSLookupFailed */ ||
176+
responseInfo.statusCode == -1009 /* NSURLErrorNotConnectedToInternet */;
177+
}
178+
163179
- (void)complete:(QNResponseInfo *)responseInfo
164180
server:(id <QNUploadServer>)server
165181
response:(NSDictionary *)response

QiniuSDK/Http/Request/QNRequestClient.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ typedef void (^QNRequestClientCompleteHandler)(NSURLResponse * _Nullable, QNUplo
1616

1717
- (void)request:(NSURLRequest *)request
1818
connectionProxy:(NSDictionary * _Nullable)connectionProxy
19-
progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
19+
progress:(void(^ _Nullable)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
2020
complete:(QNRequestClientCompleteHandler)complete;
2121

2222
- (void)cancel;

0 commit comments

Comments
 (0)