Skip to content

Commit 1bcd4c3

Browse files
committed
修复与双活功能的互斥
1 parent a39bee0 commit 1bcd4c3

File tree

2 files changed

+117
-60
lines changed

2 files changed

+117
-60
lines changed

base/lib/src/storage/config/host.dart

Lines changed: 117 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -55,65 +55,93 @@ class DefaultHostProvider extends HostFreezer {
5555

5656
final _http = Dio();
5757
// 缓存的上传区域
58-
final _stashedUpDomains = <_Domain>[];
58+
final _stashedUpHosts = <_Host>[];
5959
// accessKey:bucket 用此 key 判断是否 up host 需要走缓存
6060
String? _cacheKey;
6161

62-
@override
63-
Future<String> getUpHost({
62+
Future<List<_Host>> getUpHostsFromV4Query({
6463
required String accessKey,
6564
required String bucket,
6665
bool accelerateUploading = false,
67-
bool transregional = false,
68-
int regionIndex = 0,
6966
}) async {
70-
_unfreezeUpDomains();
71-
final upDomains = <_Domain>[];
72-
final cacheKey =
73-
'$accessKey:$bucket:${accelerateUploading ? '1' : '0'}:${_getHostsMD5(bucketHosts)}';
74-
if (cacheKey == _cacheKey && _stashedUpDomains.isNotEmpty) {
75-
upDomains.addAll(_stashedUpDomains);
67+
final upHosts = <_Host>[];
68+
final cacheKey = '$accessKey:'
69+
'$bucket:'
70+
'${accelerateUploading ? '1' : '0'}:'
71+
'${_getHostsMD5(bucketHosts)}';
72+
if (cacheKey == _cacheKey && _stashedUpHosts.isNotEmpty) {
73+
upHosts.addAll(_stashedUpHosts);
7674
} else {
7775
final data = await _getUrl(
7876
bucketHosts,
7977
'v4/query?ak=$accessKey&bucket=$bucket',
8078
protocol,
8179
_http,
8280
);
83-
Iterable<_Host> hosts = data['hosts']
81+
final hosts = data['hosts']
8482
.map((dynamic json) => _Host.fromJson(json as Map))
8583
.cast<_Host>();
84+
if (hosts.isEmpty) _throwNoAvailableRegionError();
8685

87-
if (!transregional) {
88-
final host = hosts.elementAtOrNull(regionIndex);
89-
if (host == null) {
90-
_throwNoAvailableRegionError();
91-
}
92-
hosts = [host];
93-
}
86+
_cacheKey = cacheKey;
87+
_stashedUpHosts.addAll(hosts);
88+
}
89+
return upHosts;
90+
}
91+
92+
@override
93+
Future<String> getUpHost({
94+
required String accessKey,
95+
required String bucket,
96+
bool accelerateUploading = false,
97+
// 表单上传这个值为true,则始终在所有可用区域之间选择一个可用域名
98+
// 分片上传为false,则始终选择 regionIndex 指定的区域
99+
bool transregional = false,
100+
int regionIndex = 0,
101+
}) async {
102+
_unfreezeUpDomains();
103+
104+
final upHosts = await getUpHostsFromV4Query(
105+
accessKey: accessKey,
106+
bucket: bucket,
107+
accelerateUploading: accelerateUploading,
108+
);
94109

95-
for (final host in hosts) {
110+
if (transregional) {
111+
// 表单上传
112+
final upDomains = <_Domain>{};
113+
// 全都不可用了,随机选择一个域名返回
114+
for (final host in upHosts) {
96115
final domainList = host.up['domains'].cast<String>() as List<String>;
97116
final domains = domainList.map((domain) => _Domain(domain));
98117
upDomains.addAll(domains);
99118
}
100119

101-
_cacheKey = cacheKey;
102-
_stashedUpDomains.addAll(upDomains);
103-
}
104-
105-
// 每次都从头遍历一遍,最合适的 host 总是会排在最前面
106-
for (var index = 0; index < upDomains.length; index++) {
107-
final availableDomain = upDomains.elementAt(index);
108-
// 检查看起来可用的 host 是否之前被冻结过
109-
final frozen = isFrozen('$protocol://${availableDomain.value}');
120+
// 每次都从头遍历一遍,最合适的 host 总是会排在最前面
121+
for (var index = 0; index < upDomains.length; index++) {
122+
final availableDomain = upDomains.elementAt(index);
123+
// 检查看起来可用的 host 是否之前被冻结过
124+
final frozen = isFrozen('$protocol://${availableDomain.value}');
110125

111-
if (!frozen) {
112-
return '$protocol://${availableDomain.value}';
126+
if (!frozen) {
127+
return '$protocol://${availableDomain.value}';
128+
}
129+
}
130+
return '$protocol://${upDomains.toList().mustGetRandomElement()}';
131+
} else {
132+
// 分片上传
133+
if (regionIndex < upHosts.length) {
134+
// 分片上传不能随机选择一个域名返回,需要上层切换regionIndex
135+
_throwNoAvailableHostError();
136+
} else {
137+
// 已经至少把所有的可用region都尝试过了,直接继续轮转回开头的region
138+
final host = upHosts.elementAt(regionIndex % upHosts.length);
139+
// 在这个region里面随机选择一个域名返回
140+
final domainList = host.up['domains'].cast<String>() as List<String>;
141+
final domains = domainList.map((domain) => _Domain(domain));
142+
return '$protocol://${domains.toList().mustGetRandomElement()}';
113143
}
114144
}
115-
// 全都不可用了,随机选择一个域名返回
116-
return '$protocol://${upDomains.mustGetRandomElement()}';
117145
}
118146
}
119147

@@ -170,6 +198,8 @@ class DefaultHostProviderV2 extends HostFreezer {
170198
required String accessKey,
171199
required String bucket,
172200
bool accelerateUploading = false,
201+
// 表单上传这个值为true,则始终在所有可用区域之间选择一个可用域名
202+
// 分片上传为false,则始终选择 regionIndex 指定的区域
173203
bool transregional = false,
174204
int regionIndex = 0,
175205
}) async {
@@ -181,35 +211,58 @@ class DefaultHostProviderV2 extends HostFreezer {
181211
bucketName: bucket,
182212
accelerateUploading: accelerateUploading,
183213
);
184-
final regions = <Region>[];
214+
185215
if (transregional) {
186-
regions.addAll(regionsProvider.regions);
187-
} else {
188-
final region = regionsProvider.regions.elementAtOrNull(regionIndex);
189-
if (region == null) {
190-
_throwNoAvailableRegionError();
216+
// 表单上传
217+
final regions = regionsProvider.regions;
218+
if (regions.isEmpty) _throwNoAvailableRegionError();
219+
220+
for (final region in regions) {
221+
final unfrozenDomain = region.up
222+
.map((domain) => _makeHost(domain, useHttps: _useHttps))
223+
.firstWhere(
224+
(domain) => !isFrozen(domain),
225+
orElse: () => '',
226+
);
227+
if (unfrozenDomain != '') {
228+
return unfrozenDomain;
229+
}
191230
}
192-
regions.add(region);
193-
}
194-
for (final region in regions) {
195-
final unfrozenDomain = region.up
231+
// 全都不可用了,随机选择一个域名返回
232+
return regions
233+
.mustGetRandomElement()
234+
.up
196235
.map((domain) => _makeHost(domain, useHttps: _useHttps))
197-
.firstWhere(
198-
(domain) => !isFrozen(domain),
199-
orElse: () => '',
200-
);
201-
if (unfrozenDomain != '') {
202-
return unfrozenDomain;
236+
.toList()
237+
.mustGetRandomElement();
238+
} else {
239+
// 分片上传
240+
if (regionIndex < regionsProvider.regions.length) {
241+
final unfrozenDomain = regionsProvider.regions
242+
.elementAt(regionIndex)
243+
.up
244+
.map((domain) => _makeHost(domain, useHttps: _useHttps))
245+
.firstWhere(
246+
(domain) => !isFrozen(domain),
247+
orElse: () => '',
248+
);
249+
if (unfrozenDomain != '') {
250+
return unfrozenDomain;
251+
}
252+
// 分片上传不能随机选择一个域名返回,需要上层切换regionIndex
253+
_throwNoAvailableHostError();
254+
} else {
255+
// 已经至少把所有的可用region都尝试过了,直接继续轮转回开头的region
256+
final index = regionIndex % regionsProvider.regions.length;
257+
// 这里面的域名很可能都是仍然处于冻结状态的,这里随机选择一个返回
258+
return regionsProvider.regions
259+
.elementAt(index)
260+
.up
261+
.map((domain) => _makeHost(domain, useHttps: _useHttps))
262+
.toList()
263+
.mustGetRandomElement();
203264
}
204265
}
205-
206-
// 全都不可用了,随机选择一个域名返回
207-
return regions
208-
.mustGetRandomElement()
209-
.up
210-
.map((domain) => _makeHost(domain, useHttps: _useHttps))
211-
.toList()
212-
.mustGetRandomElement();
213266
}
214267

215268
Future<BucketRegionsQuery> get query => _getQuery();
@@ -284,6 +337,13 @@ void _checkResponse(Response response) {
284337
}
285338
}
286339

340+
Never _throwNoAvailableHostError() {
341+
throw StorageError(
342+
type: StorageErrorType.NO_AVAILABLE_HOST,
343+
message: '没有可用的上传域名',
344+
);
345+
}
346+
287347
Never _throwNoAvailableRegionError() {
288348
throw StorageError(
289349
type: StorageErrorType.NO_AVAILABLE_REGION,

flutter/example/lib/main.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,6 @@ class BaseState extends State<Base> with DisposableState {
155155
case StorageErrorType.UNKNOWN:
156156
printToConsole('发生错误: 未知错误');
157157
break;
158-
case StorageErrorType.NO_AVAILABLE_HOST:
159-
printToConsole('发生错误: 无可用 Host');
160-
break;
161158
case StorageErrorType.NO_AVAILABLE_REGION:
162159
printToConsole('发生错误: 无可用 Region');
163160
break;

0 commit comments

Comments
 (0)