diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cfea34c2..df6ad1390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ #Changelog +## 8.2.0(2021-02-20) +## 增加 +* 增加网络监控功能,选择最优 Host 进行上传 +* 优化日志统计 + ## 8.1.2(2021-01-18) * 区域查询采用SingleFlight模式 * 增加网络链接状态检测 diff --git a/README.md b/README.md index 66725c7fb..753d6b876 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ https://github.com/qiniudemo/qiniu-lab-android | Qiniu SDK 版本 | 最低 Android版本 | 依赖库版本 | |------------ |-----------------|------------------------| +| 8.2.x | Android 5.0+ | okhttp 4+ | | 8.1.x | Android 5.0+ | okhttp 4+ | | 8.0.x | Android 5.0+ | okhttp 4+ | | 7.7.x | Android 5.0+ | okhttp 4+ | @@ -28,7 +29,7 @@ https://github.com/qiniudemo/qiniu-lab-android | 7.0.7 | Android 2.2+ | android-async-http 1.4.8 | ### 注意 -* 推荐使用最新版:8.1.2 +* 推荐使用最新版:8.2.0 * AndroidNetwork.getMobileDbm()可以获取手机信号强度,需要如下权限(API>=18时生效) ``` diff --git a/library/src/androidTest/java/com/qiniu/android/ConnectCheckTest.java b/library/src/androidTest/java/com/qiniu/android/ConnectCheckTest.java index bc77e6698..8232647fa 100644 --- a/library/src/androidTest/java/com/qiniu/android/ConnectCheckTest.java +++ b/library/src/androidTest/java/com/qiniu/android/ConnectCheckTest.java @@ -10,7 +10,7 @@ public void testCheck() { int maxCount = 100; int successCount = 0; for (int i = 0; i < maxCount; i++) { - if (ConnectChecker.check()) { + if (ConnectChecker.isConnected(ConnectChecker.check())) { successCount += 1; } } @@ -23,7 +23,7 @@ public void testCustomCheckHosts() { int maxCount = 100; int successCount = 0; for (int i = 0; i < maxCount; i++) { - if (ConnectChecker.check()) { + if (ConnectChecker.isConnected(ConnectChecker.check())) { successCount += 1; } } @@ -36,7 +36,7 @@ public void testNotConnected() { int maxCount = 100; int successCount = 0; for (int i = 0; i < maxCount; i++) { - if (ConnectChecker.check()) { + if (ConnectChecker.isConnected(ConnectChecker.check())) { successCount += 1; } } diff --git a/library/src/androidTest/java/com/qiniu/android/DnsTransactionTest.java b/library/src/androidTest/java/com/qiniu/android/DnsTransactionTest.java index af15fd4a0..2afe03f34 100644 --- a/library/src/androidTest/java/com/qiniu/android/DnsTransactionTest.java +++ b/library/src/androidTest/java/com/qiniu/android/DnsTransactionTest.java @@ -61,10 +61,12 @@ public void test_CheckAndPrefetch(){ public void run() { boolean isSuccess = DnsPrefetchTransaction.addDnsCheckAndPrefetchTransaction(zone, UpToken.parse(TestConfig.token_z0)); - if (isSuccess){ - successCount += 1; + synchronized (this) { + if (isSuccess) { + successCount += 1; + } + completeCount += 1; } - completeCount += 1; } }).start(); } @@ -80,7 +82,7 @@ public boolean shouldWait() { } }, 60); - assertTrue("successCount:" + successCount, successCount < 2); + assertTrue("successCount:" + successCount, successCount < 3); } diff --git a/library/src/androidTest/java/com/qiniu/android/TestConfig.java b/library/src/androidTest/java/com/qiniu/android/TestConfig.java index 4d909b9f0..1072f6756 100644 --- a/library/src/androidTest/java/com/qiniu/android/TestConfig.java +++ b/library/src/androidTest/java/com/qiniu/android/TestConfig.java @@ -10,20 +10,20 @@ public final class TestConfig { // TODO: 2020-05-09 bad token for testPutBytesWithFixedZoneUseBackupDomains // 华东上传凭证 public static final String bucket_z0 = "kodo-phone-zone0-space"; - public static final String token_z0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:Tq8MZRIC2hc1hxWxH3RqLcdFuQw=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTAtc3BhY2UiLCJkZWFkbGluZSI6MTYxMzE4NDg0OCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; + public static final String token_z0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:m1kHxpdaFH3NK120iAkHlSwBpio=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTAtc3BhY2UiLCJkZWFkbGluZSI6MTYxODgxNDM5MCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; // 华北上传凭证 public static final String bucket_z1 = "kodo-phone-zone1-space"; - public static final String token_z1 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:FZiNMb3SKDFpdat2q9wpG6zbn_w=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTEtc3BhY2UiLCJkZWFkbGluZSI6MTYxMzE4NDg0OCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; + public static final String token_z1 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:1vkQkb72ANFiAftABJAF2dhbXd0=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTEtc3BhY2UiLCJkZWFkbGluZSI6MTYxODgxNDM5MCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; // 华南上传凭证 public static final String bucket_z2 = "kodo-phone-zone2-space"; - public static final String token_z2 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:cP9NhEWUWhPrxlfAxaemVj6uIKI=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTItc3BhY2UiLCJkZWFkbGluZSI6MTYxMzE4NDg0OCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; + public static final String token_z2 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:ZTqDdbvHJuP3hJFckpadCyW08Cs=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZTItc3BhY2UiLCJkZWFkbGluZSI6MTYxODgxNDM5MCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; // 北美上传凭证 public static final String bucket_na0 = "kodo-phone-zone-na0-space"; - public static final String token_na0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:V9zBCBaNWrKZvqLu4b5Dem0hOrQ=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZS1uYTAtc3BhY2UiLCJkZWFkbGluZSI6MTYxMzE4NDg0OCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; + public static final String token_na0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:I8Q0E32hEelHH4xWBH2p17SxhdA=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZS1uYTAtc3BhY2UiLCJkZWFkbGluZSI6MTYxODgxNDM5MCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; // 东南亚上传凭证 public static final String bucket_as0 = "kodo-phone-zone-as0-space"; - public static final String token_as0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:PaaKXdrwQgI4J7Fw5nhtbwQ_bu8=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZS1hczAtc3BhY2UiLCJkZWFkbGluZSI6MTYxMzE4NDg0OCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; - public static final String invalidBucketToken = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:k9olgJ5rgs-SvzirMtdU_ASmArY=:eyJzY29wZSI6InpvbmVfaW52YWxpZCIsImRlYWRsaW5lIjoxNjEzMTg0ODQ4LCAicmV0dXJuQm9keSI6IntcImNhbGxiYWNrVXJsXCI6XCJodHRwOlwvXC9jYWxsYmFjay5kZXYucWluaXUuaW9cIiwgXCJmb29cIjokKHg6Zm9vKSwgXCJiYXJcIjokKHg6YmFyKSwgXCJtaW1lVHlwZVwiOiQobWltZVR5cGUpLCBcImhhc2hcIjokKGV0YWcpLCBcImtleVwiOiQoa2V5KSwgXCJmbmFtZVwiOiQoZm5hbWUpfSJ9"; + public static final String token_as0 = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:DDXIo7KzUj3ceh5LveRXyNfsiZU=:eyJzY29wZSI6ImtvZG8tcGhvbmUtem9uZS1hczAtc3BhY2UiLCJkZWFkbGluZSI6MTYxODgxNDM5MCwgInJldHVybkJvZHkiOiJ7XCJjYWxsYmFja1VybFwiOlwiaHR0cDpcL1wvY2FsbGJhY2suZGV2LnFpbml1LmlvXCIsIFwiZm9vXCI6JCh4OmZvbyksIFwiYmFyXCI6JCh4OmJhciksIFwibWltZVR5cGVcIjokKG1pbWVUeXBlKSwgXCJoYXNoXCI6JChldGFnKSwgXCJrZXlcIjokKGtleSksIFwiZm5hbWVcIjokKGZuYW1lKX0ifQ=="; + public static final String invalidBucketToken = "dxVQk8gyk3WswArbNhdKIwmwibJ9nFsQhMNUmtIM:Kpi_0B6gZSf7nF5UgdZtvHx0h8M=:eyJzY29wZSI6InpvbmVfaW52YWxpZCIsImRlYWRsaW5lIjoxNjE4ODE0MzkwLCAicmV0dXJuQm9keSI6IntcImNhbGxiYWNrVXJsXCI6XCJodHRwOlwvXC9jYWxsYmFjay5kZXYucWluaXUuaW9cIiwgXCJmb29cIjokKHg6Zm9vKSwgXCJiYXJcIjokKHg6YmFyKSwgXCJtaW1lVHlwZVwiOiQobWltZVR5cGUpLCBcImhhc2hcIjokKGV0YWcpLCBcImtleVwiOiQoa2V5KSwgXCJmbmFtZVwiOiQoZm5hbWUpfSJ9"; // ----------- public static final String ak = "bjtWBQXrcxgo7HWwlC_bgHg81j352_GhgBGZPeOW"; diff --git a/library/src/androidTest/java/com/qiniu/android/UploadDomainRegionTest.java b/library/src/androidTest/java/com/qiniu/android/UploadDomainRegionTest.java index 2cb18cb03..148ffa610 100644 --- a/library/src/androidTest/java/com/qiniu/android/UploadDomainRegionTest.java +++ b/library/src/androidTest/java/com/qiniu/android/UploadDomainRegionTest.java @@ -2,6 +2,7 @@ import com.qiniu.android.common.FixedZone; import com.qiniu.android.http.request.IUploadServer; +import com.qiniu.android.http.request.UploadRequestState; import com.qiniu.android.http.serverRegion.UploadDomainRegion; import com.qiniu.android.http.serverRegion.UploadServerFreezeManager; import com.qiniu.android.utils.Utils; @@ -18,9 +19,11 @@ public void testGetOneServer(){ UploadDomainRegion region = new UploadDomainRegion(); region.setupRegionData(zone.getZonesInfo(null).zonesInfo.get(0)); - UploadServerFreezeManager.getInstance().freezeHost(host, type, 100); + UploadServerFreezeManager.getInstance().freezeType(type, 100); - IUploadServer server = region.getNextServer(false, null, null); + UploadRequestState state = new UploadRequestState(); + state.setUseOldServer(false); + IUploadServer server = region.getNextServer(state, null, null); assertNotNull(server); } diff --git a/library/src/androidTest/java/com/qiniu/android/UploadServerFreezeManagerTest.java b/library/src/androidTest/java/com/qiniu/android/UploadServerFreezeManagerTest.java index 1500fa8dc..007907f91 100644 --- a/library/src/androidTest/java/com/qiniu/android/UploadServerFreezeManagerTest.java +++ b/library/src/androidTest/java/com/qiniu/android/UploadServerFreezeManagerTest.java @@ -8,9 +8,9 @@ public void testFreeze() { String host = "baidu.com"; String type = host; - UploadServerFreezeManager.getInstance().freezeHost(host, type, 10); + UploadServerFreezeManager.getInstance().freezeType(type, 10); - boolean isFrozen = UploadServerFreezeManager.getInstance().isFreezeHost(host, type); + boolean isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); assertTrue(isFrozen); } @@ -18,13 +18,13 @@ public void testUnfreeze() { String host = "baidu.com"; String type = host; - UploadServerFreezeManager.getInstance().freezeHost(host, type, 10); + UploadServerFreezeManager.getInstance().freezeType(type, 10); - boolean isFrozen = UploadServerFreezeManager.getInstance().isFreezeHost(host, type); + boolean isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); assertTrue(isFrozen); - UploadServerFreezeManager.getInstance().unfreezeHost(host, type); - isFrozen = UploadServerFreezeManager.getInstance().isFreezeHost(host, type); + UploadServerFreezeManager.getInstance().unfreezeType(type); + isFrozen = UploadServerFreezeManager.getInstance().isTypeFrozen(type); assertTrue(isFrozen == false); } diff --git a/library/src/main/java/com/qiniu/android/collect/ReportItem.java b/library/src/main/java/com/qiniu/android/collect/ReportItem.java index 0d3171185..285ec7183 100644 --- a/library/src/main/java/com/qiniu/android/collect/ReportItem.java +++ b/library/src/main/java/com/qiniu/android/collect/ReportItem.java @@ -47,6 +47,7 @@ public String toJson(){ public static final String RequestKeyStatusCode = "status_code"; public static final String RequestKeyRequestId = "req_id"; public static final String RequestKeyHost = "host"; + public static final String RequestKeyHttpVersion = "http_version"; public static final String RequestKeyRemoteIp = "remote_ip"; public static final String RequestKeyPort = "port"; public static final String RequestKeyTargetBucket = "target_bucket"; @@ -80,6 +81,7 @@ public String toJson(){ public static final String RequestKeyPrefetchedDnsSource = "prefetched_dns_source"; public static final String RequestKeyPrefetchedBefore = "prefetched_before"; public static final String RequestKeyPrefetchedErrorMessage = "prefetched_error_message"; + public static final String RequestKeyNetworkMeasuring = "network_measuring"; // 分块上传统计⽇志 public static final String BlockKeyLogType = "log_type"; diff --git a/library/src/main/java/com/qiniu/android/common/AutoZone.java b/library/src/main/java/com/qiniu/android/common/AutoZone.java index 020e40a11..d056fd89a 100644 --- a/library/src/main/java/com/qiniu/android/common/AutoZone.java +++ b/library/src/main/java/com/qiniu/android/common/AutoZone.java @@ -123,7 +123,7 @@ public void complete(Object value) { } catch (Exception e) { /// 此处永远不会执行,回调只为占位 - completeHandler.complete(ResponseInfo.NetworkError, ResponseInfo.localIOError("uc query"), null); + completeHandler.complete(ResponseInfo.NetworkError, ResponseInfo.localIOError(e.toString()), null); } } diff --git a/library/src/main/java/com/qiniu/android/common/Constants.java b/library/src/main/java/com/qiniu/android/common/Constants.java index a76e38c45..1e2652ce0 100644 --- a/library/src/main/java/com/qiniu/android/common/Constants.java +++ b/library/src/main/java/com/qiniu/android/common/Constants.java @@ -2,7 +2,7 @@ public final class Constants { - public static final String VERSION = "8.1.2"; + public static final String VERSION = "8.2.0"; public static final String UTF_8 = "utf-8"; } diff --git a/library/src/main/java/com/qiniu/android/common/ZoneInfo.java b/library/src/main/java/com/qiniu/android/common/ZoneInfo.java index 6d647d269..b6a880368 100644 --- a/library/src/main/java/com/qiniu/android/common/ZoneInfo.java +++ b/library/src/main/java/com/qiniu/android/common/ZoneInfo.java @@ -24,6 +24,7 @@ public class ZoneInfo { private static int DOMAIN_FROZEN_SECONDS = 10 * 60; public final int ttl; + public final boolean http3Enabled; public final List domains; public final List old_domains; @@ -70,10 +71,12 @@ public static ZoneInfo buildInfo(List mainHosts, } private ZoneInfo(int ttl, + boolean http3Enabled, String regionId, List domains, List old_domains) { this.ttl = ttl; + this.http3Enabled = http3Enabled; this.regionId = regionId; this.domains = domains; this.old_domains = old_domains; @@ -91,6 +94,14 @@ public static ZoneInfo buildFromJson(JSONObject obj) throws JSONException { } int ttl = obj.optInt("ttl"); + boolean http3Enabled = false; + try { + JSONObject features = obj.getJSONObject("features"); + JSONObject http3 = features.getJSONObject("http3"); + http3Enabled = http3.getBoolean("enabled"); + } catch (Exception ignored) { + } + String regionId = obj.optString("region"); if (regionId == null) { regionId = EmptyRegionId; @@ -130,7 +141,7 @@ public static ZoneInfo buildFromJson(JSONObject obj) throws JSONException { return null; } - ZoneInfo zoneInfo = new ZoneInfo(ttl, regionId, domains, old_domains); + ZoneInfo zoneInfo = new ZoneInfo(ttl, http3Enabled, regionId, domains, old_domains); zoneInfo.detailInfo = obj; zoneInfo.allHosts = allHosts; diff --git a/library/src/main/java/com/qiniu/android/http/connectCheck/ConnectChecker.java b/library/src/main/java/com/qiniu/android/http/connectCheck/ConnectChecker.java index 9c8acd995..8bd9e94a0 100644 --- a/library/src/main/java/com/qiniu/android/http/connectCheck/ConnectChecker.java +++ b/library/src/main/java/com/qiniu/android/http/connectCheck/ConnectChecker.java @@ -14,53 +14,57 @@ public class ConnectChecker { - private static SingleFlight singleFlight = new SingleFlight<>(); + private static SingleFlight singleFlight = new SingleFlight<>(); - public static boolean check() { + public static boolean isConnected(UploadSingleRequestMetrics metrics) { + return metrics != null && metrics.response != null && metrics.response.statusCode > 99; + } + + public static UploadSingleRequestMetrics check() { final CheckResult result = new CheckResult(); final Wait wait = new Wait(); check(new CheckCompleteHandler() { @Override - public void complete(boolean isConnected) { - result.isConnected = isConnected; + public void complete(UploadSingleRequestMetrics metrics) { + result.metrics = metrics; wait.stopWait(); } }); wait.startWait(); - return result.isConnected; + return result.metrics; } private static void check(final CheckCompleteHandler completeHandler) { try { - singleFlight.perform("connect_check", new SingleFlight.ActionHandler() { + singleFlight.perform("connect_check", new SingleFlight.ActionHandler() { @Override - public void action(final SingleFlight.CompleteHandler singleFlightComplete) throws Exception { + public void action(final SingleFlight.CompleteHandler singleFlightComplete) throws Exception { checkAllHosts(new CheckCompleteHandler() { @Override - public void complete(boolean isConnected) { - singleFlightComplete.complete(isConnected); + public void complete(UploadSingleRequestMetrics metrics) { + singleFlightComplete.complete(metrics); } }); } - }, new SingleFlight.CompleteHandler() { + }, new SingleFlight.CompleteHandler() { @Override - public void complete(Boolean value) { - completeHandler.complete(value); + public void complete(UploadSingleRequestMetrics metrics) { + completeHandler.complete(metrics); } }); } catch (Exception e) { - completeHandler.complete(true); + completeHandler.complete(null); } } private static void checkAllHosts(final CheckCompleteHandler completeHandler) { String[] allHosts = GlobalConfiguration.getInstance().connectCheckURLStrings; if (allHosts == null) { - completeHandler.complete(true); + completeHandler.complete(null); return; } @@ -72,8 +76,8 @@ private static void checkAllHosts(final CheckCompleteHandler completeHandler) { for (String host : allHosts) { checkHost(host, new CheckCompleteHandler() { @Override - public void complete(boolean isHostConnected) { - + public void complete(UploadSingleRequestMetrics metrics) { + boolean isHostConnected = isConnected(metrics); synchronized (checkStatus) { checkStatus.completeCount += 1; } @@ -90,7 +94,7 @@ public void complete(boolean isHostConnected) { checkStatus.isCompleted = true; } } - completeHandler.complete(checkStatus.isConnected); + completeHandler.complete(metrics); } else { LogUtil.i("== check all hosts not completed totalCount:" + checkStatus.totalCount + " completeCount:" + checkStatus.completeCount); } @@ -109,20 +113,15 @@ private static void checkHost(final String host, final CheckCompleteHandler comp client.request(request, true, null, null, new IRequestClient.RequestClientCompleteHandler() { @Override public void complete(ResponseInfo responseInfo, UploadSingleRequestMetrics metrics, JSONObject response) { - if (responseInfo.statusCode > 99) { - LogUtil.i("== checkHost:" + host + " result: true"); - completeHandler.complete(true); - } else { - LogUtil.i("== checkHost:" + host + " result: false"); - completeHandler.complete(false); - } + LogUtil.i("== checkHost:" + host + " responseInfo:" + responseInfo); + completeHandler.complete(metrics); } }); } private interface CheckCompleteHandler { - void complete(boolean isConnected); + void complete(UploadSingleRequestMetrics metrics); } private static class CheckStatus { @@ -133,6 +132,6 @@ private static class CheckStatus { } private static class CheckResult { - private boolean isConnected = false; + private UploadSingleRequestMetrics metrics; } } diff --git a/library/src/main/java/com/qiniu/android/http/metrics/UploadSingleRequestMetrics.java b/library/src/main/java/com/qiniu/android/http/metrics/UploadSingleRequestMetrics.java index 31c562f8f..788647162 100644 --- a/library/src/main/java/com/qiniu/android/http/metrics/UploadSingleRequestMetrics.java +++ b/library/src/main/java/com/qiniu/android/http/metrics/UploadSingleRequestMetrics.java @@ -9,6 +9,12 @@ public class UploadSingleRequestMetrics { + // 请求的 httpVersion + public String httpVersion; + + // 只有进行网络检测才会有 connectCheckMetrics + public UploadSingleRequestMetrics connectCheckMetrics; + public Request request; public ResponseInfo response; diff --git a/library/src/main/java/com/qiniu/android/http/networkStatus/NetworkStatusManager.java b/library/src/main/java/com/qiniu/android/http/networkStatus/NetworkStatusManager.java new file mode 100644 index 000000000..d4ee9c535 --- /dev/null +++ b/library/src/main/java/com/qiniu/android/http/networkStatus/NetworkStatusManager.java @@ -0,0 +1,194 @@ +package com.qiniu.android.http.networkStatus; + +import com.qiniu.android.storage.FileRecorder; +import com.qiniu.android.storage.Recorder; +import com.qiniu.android.utils.AsyncRun; +import com.qiniu.android.utils.Utils; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Date; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +public class NetworkStatusManager { + + private static String kNetworkStatusDiskKey = "NetworkStatus:v1.0.0"; + + private boolean hasInit = false; + private boolean isHandlingNetworkInfoOfDisk = false; + private Recorder recorder; + private ConcurrentHashMap networkStatusInfo; + private static NetworkStatusManager networkStatusManager = new NetworkStatusManager(); + + public static NetworkStatusManager getInstance() { + networkStatusManager.initData(); + return networkStatusManager; + } + + public synchronized void initData() { + if (hasInit){ + return; + } + networkStatusManager.networkStatusInfo = new ConcurrentHashMap<>(); + networkStatusManager.asyncRecoverNetworkStatusFromDisk(); + } + + public static String getNetworkStatusType(String host, String ip) { + return Utils.getIpType(ip, host); + } + + public NetworkStatus getNetworkStatus(String type) { + if (type == null || type.length() == 0) { + return null; + } + NetworkStatus status = networkStatusInfo.get(type); + if (status == null) { + status = new NetworkStatus(); + } + return status; + } + + public void updateNetworkStatus(String type, int speed) { + if (type == null || type.length() == 0) { + return; + } + NetworkStatus status = networkStatusInfo.get(type); + if (status == null) { + status = new NetworkStatus(); + networkStatusInfo.put(type, status); + } + status.setSpeed(speed); + + asyncRecordNetworkStatusInfo(); + } + + // ---------- 持久化 ----------- + private void asyncRecordNetworkStatusInfo() { + synchronized (this) { + if (isHandlingNetworkInfoOfDisk) { + return; + } + isHandlingNetworkInfoOfDisk = true; + } + AsyncRun.runInBack(new Runnable() { + @Override + public void run() { + recordNetworkStatusInfo(); + isHandlingNetworkInfoOfDisk = false; + } + }); + } + + private void asyncRecoverNetworkStatusFromDisk() { + synchronized (this) { + if (isHandlingNetworkInfoOfDisk) { + return; + } + isHandlingNetworkInfoOfDisk = true; + } + AsyncRun.runInBack(new Runnable() { + @Override + public void run() { + recoverNetworkStatusFromDisk(); + isHandlingNetworkInfoOfDisk = true; + } + }); + } + + private void recordNetworkStatusInfo() { + + setupRecorder(); + + if (recorder == null || networkStatusInfo == null) { + return; + } + + JSONObject networkStatusInfoJson = new JSONObject(); + for (String key : networkStatusInfo.keySet()) { + NetworkStatus status = networkStatusInfo.get(key); + if (status != null) { + try { + networkStatusInfoJson.put(key, status.toJson()); + } catch (Exception ignored) { + } + } + } + recorder.set(kNetworkStatusDiskKey, networkStatusInfoJson.toString().getBytes()); + } + + private void recoverNetworkStatusFromDisk() { + + setupRecorder(); + + if (recorder == null || networkStatusInfo == null) { + return; + } + + byte[] networkStatusInfoData = recorder.get(kNetworkStatusDiskKey); + JSONObject networkStatusInfoJSON = null; + try { + networkStatusInfoJSON = new JSONObject(new String(networkStatusInfoData)); + } catch (Exception ignored) { + return; + } + + for (Iterator it = networkStatusInfoJSON.keys(); it.hasNext(); ) { + String key = it.next(); + try { + JSONObject statusJson = networkStatusInfoJSON.getJSONObject(key); + NetworkStatus status = NetworkStatus.statusFromJson(statusJson); + if (status != null) { + networkStatusInfo.put(key, status); + } + } catch (JSONException ignored) { + } + } + } + + + private synchronized void setupRecorder() { + if (recorder == null) { + try { + recorder = new FileRecorder(Utils.sdkDirectory() + "/NetworkInfo"); + } catch (Exception ignored) { + } + } + } + + public static class NetworkStatus { + + private int speed; + + public int getSpeed() { + return speed; + } + + public void setSpeed(int speed) { + this.speed = speed; + } + + private JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.put("speed", speed); + } catch (Exception ignored) { + } + return jsonObject; + } + + private static NetworkStatus statusFromJson(JSONObject jsonObject) { + if (jsonObject == null) { + return null; + } + + NetworkStatus status = new NetworkStatus(); + try { + status.speed = jsonObject.getInt("speed"); + } catch (Exception ignored) { + } + return status; + } + } +} diff --git a/library/src/main/java/com/qiniu/android/http/networkStatus/UploadServerNetworkStatus.java b/library/src/main/java/com/qiniu/android/http/networkStatus/UploadServerNetworkStatus.java new file mode 100644 index 000000000..9429d327f --- /dev/null +++ b/library/src/main/java/com/qiniu/android/http/networkStatus/UploadServerNetworkStatus.java @@ -0,0 +1,32 @@ +package com.qiniu.android.http.networkStatus; + +import com.qiniu.android.http.request.IUploadServer; +import com.qiniu.android.utils.Utils; + +public class UploadServerNetworkStatus { + + public static IUploadServer getBetterNetworkServer(IUploadServer serverA, IUploadServer serverB) { + return isServerNetworkBetter(serverA, serverB) ? serverA : serverB; + } + + public static boolean isServerNetworkBetter(IUploadServer serverA, IUploadServer serverB) { + if (serverA == null) { + return false; + } else if (serverB == null) { + return true; + } + + String serverTypeA = NetworkStatusManager.getNetworkStatusType(serverA.getHost(), serverA.getIp()); + String serverTypeB = NetworkStatusManager.getNetworkStatusType(serverB.getHost(), serverB.getIp()); + if (serverTypeA == null) { + return false; + } else if (serverTypeB == null) { + return true; + } + + NetworkStatusManager.NetworkStatus serverStatusA = NetworkStatusManager.getInstance().getNetworkStatus(serverTypeA); + NetworkStatusManager.NetworkStatus serverStatusB = NetworkStatusManager.getInstance().getNetworkStatus(serverTypeB); + + return serverStatusB.getSpeed() < serverStatusA.getSpeed(); + } +} diff --git a/library/src/main/java/com/qiniu/android/http/request/HttpRegionRequest.java b/library/src/main/java/com/qiniu/android/http/request/HttpRegionRequest.java index 94d62d451..a7dc33d9f 100644 --- a/library/src/main/java/com/qiniu/android/http/request/HttpRegionRequest.java +++ b/library/src/main/java/com/qiniu/android/http/request/HttpRegionRequest.java @@ -24,7 +24,7 @@ class HttpRegionRequest { private final IUploadRegion region; private final UploadRequestInfo requestInfo; - private boolean isUseOldServer; + private UploadRequestState requestState; private HttpSingleRequest singleRequest; private IUploadServer currentServer; private UploadRegionRequestMetrics requestMetrics; @@ -40,6 +40,7 @@ class HttpRegionRequest { this.token = token; this.region = region; this.requestInfo = requestInfo; + this.requestState = requestState; singleRequest = new HttpSingleRequest(config, uploadOption, token, requestInfo, requestState); } @@ -97,30 +98,22 @@ private void performRequest(IUploadServer server, String serverIP = server.getIp(); if (config.urlConverter != null){ - serverHost = config.urlConverter.convert(serverHost); serverIP = null; - server = null; + serverHost = config.urlConverter.convert(serverHost); } - boolean toSkipDns; String scheme = config.useHttps ? "https://" : "http://"; String urlString = scheme + serverHost + (action != null ? action : ""); - if (serverIP != null && serverIP.length() > 0) { - toSkipDns = false; - } else { - toSkipDns = true; - } final Request request = new Request(urlString, method, header, data, config.connectTimeout); request.host = serverHost; request.ip = serverIP; - request.uploadServer = server; LogUtil.i("key:" + StringUtils.toNonnullString(requestInfo.key) + " url:" + StringUtils.toNonnullString(request.urlString)); LogUtil.i("key:" + StringUtils.toNonnullString(requestInfo.key) + " headers:" + StringUtils.toNonnullString(request.allHeaders)); - singleRequest.request(request, isAsync, toSkipDns, shouldRetryHandler, progressHandler, new HttpSingleRequest.RequestCompleteHandler() { + singleRequest.request(request, server, isAsync, shouldRetryHandler, progressHandler, new HttpSingleRequest.RequestCompleteHandler() { @Override public void complete(ResponseInfo responseInfo, ArrayList requestMetricsList, JSONObject response) { @@ -160,11 +153,11 @@ private void completeAction(ResponseInfo responseInfo, private IUploadServer getNextServer(ResponseInfo responseInfo){ - if (responseInfo != null && responseInfo.isTlsError()) { - isUseOldServer = true; + if (requestState != null && responseInfo != null && responseInfo.isTlsError()) { + requestState.setUseOldServer(true); } - return region.getNextServer(isUseOldServer, responseInfo, currentServer); + return region.getNextServer(requestState, responseInfo, currentServer); } diff --git a/library/src/main/java/com/qiniu/android/http/request/HttpSingleRequest.java b/library/src/main/java/com/qiniu/android/http/request/HttpSingleRequest.java index d970d6a67..e286e4d70 100644 --- a/library/src/main/java/com/qiniu/android/http/request/HttpSingleRequest.java +++ b/library/src/main/java/com/qiniu/android/http/request/HttpSingleRequest.java @@ -6,6 +6,7 @@ import com.qiniu.android.http.ResponseInfo; import com.qiniu.android.http.connectCheck.ConnectChecker; import com.qiniu.android.http.dns.DnsPrefetcher; +import com.qiniu.android.http.networkStatus.NetworkStatusManager; import com.qiniu.android.http.request.httpclient.SystemHttpClient; import com.qiniu.android.http.request.handler.CheckCancelHandler; import com.qiniu.android.http.request.handler.RequestProgressHandler; @@ -21,6 +22,7 @@ import org.json.JSONObject; import java.util.ArrayList; +import java.util.Locale; class HttpSingleRequest { @@ -32,7 +34,7 @@ class HttpSingleRequest { private final UploadRequestInfo requestInfo; private final UploadRequestState requestState; - private ArrayList requestMetricsList; + private ArrayList requestMetricsList; private IRequestClient client; @@ -50,24 +52,24 @@ class HttpSingleRequest { } void request(Request request, + IUploadServer server, boolean isAsync, - boolean toSkipDns, RequestShouldRetryHandler shouldRetryHandler, RequestProgressHandler progressHandler, - RequestCompleteHandler completeHandler){ + RequestCompleteHandler completeHandler) { currentRetryTime = 0; requestMetricsList = new ArrayList<>(); - retryRequest(request, isAsync, toSkipDns, shouldRetryHandler, progressHandler, completeHandler); + retryRequest(request, server, isAsync, shouldRetryHandler, progressHandler, completeHandler); } private void retryRequest(final Request request, + final IUploadServer server, final boolean isAsync, - final boolean toSkipDns, final RequestShouldRetryHandler shouldRetryHandler, final RequestProgressHandler progressHandler, - final RequestCompleteHandler completeHandler){ + final RequestCompleteHandler completeHandler) { - if (toSkipDns){ + if (server.isHttp3()) { client = new SystemHttpClient(); } else { client = new SystemHttpClient(); @@ -77,7 +79,7 @@ private void retryRequest(final Request request, @Override public boolean checkCancel() { boolean isCancelled = requestState.isUserCancel(); - if (! isCancelled && uploadOption.cancellationSignal != null) { + if (!isCancelled && uploadOption.cancellationSignal != null) { isCancelled = uploadOption.cancellationSignal.isCancelled(); } return isCancelled; @@ -94,39 +96,45 @@ public boolean checkCancel() { public void progress(long totalBytesWritten, long totalBytesExpectedToWrite) { if (checkCancelHandler.checkCancel()) { requestState.setUserCancel(true); - if (client != null){ + if (client != null) { client.cancel(); } - } else if (progressHandler != null){ + } else if (progressHandler != null) { progressHandler.progress(totalBytesWritten, totalBytesExpectedToWrite); } } }, new IRequestClient.RequestClientCompleteHandler() { @Override public void complete(ResponseInfo responseInfo, UploadSingleRequestMetrics metrics, JSONObject response) { - if (metrics != null){ + if (metrics != null) { requestMetricsList.add(metrics); } - if (shouldCheckConnect(responseInfo) && !ConnectChecker.check()) { - String message = "check origin statusCode:" + responseInfo.statusCode + " error:" + responseInfo.error; - responseInfo = ResponseInfo.errorInfo(ResponseInfo.NetworkSlow, message); + if (shouldCheckConnect(responseInfo)) { + UploadSingleRequestMetrics checkMetrics = ConnectChecker.check(); + if (metrics != null) { + metrics.connectCheckMetrics = checkMetrics; + } + if (!ConnectChecker.isConnected(checkMetrics)) { + String message = "check origin statusCode:" + responseInfo.statusCode + " error:" + responseInfo.error; + responseInfo = ResponseInfo.errorInfo(ResponseInfo.NetworkSlow, message); + } } LogUtil.i("key:" + StringUtils.toNonnullString(requestInfo.key) + " response:" + StringUtils.toNonnullString(responseInfo)); if (shouldRetryHandler != null && shouldRetryHandler.shouldRetry(responseInfo, response) - && currentRetryTime < config.retryMax - && responseInfo.couldHostRetry()){ + && currentRetryTime < config.retryMax + && responseInfo.couldHostRetry()) { currentRetryTime += 1; try { Thread.sleep(config.retryInterval); } catch (InterruptedException ignored) { } - retryRequest(request, isAsync, toSkipDns, shouldRetryHandler, progressHandler, completeHandler); + retryRequest(request, server, isAsync, shouldRetryHandler, progressHandler, completeHandler); } else { - completeAction(request, responseInfo, response, metrics, completeHandler); + completeAction(server, responseInfo, response, metrics, completeHandler); } } }); @@ -134,47 +142,63 @@ public void complete(ResponseInfo responseInfo, UploadSingleRequestMetrics metri } private boolean shouldCheckConnect(ResponseInfo responseInfo) { - return responseInfo != null && ( - responseInfo.statusCode == -1001 || /* timeout */ - responseInfo.statusCode == -1003 || /* unknown host */ - responseInfo.statusCode == -1004 || /* cannot connect to host */ - responseInfo.statusCode == -1005 || /* connection lost */ - responseInfo.statusCode == -1009 || /* not connected to host */ - responseInfo.isTlsError()); + return responseInfo != null && + (responseInfo.statusCode == ResponseInfo.NetworkError || /* network error */ + responseInfo.statusCode == -1001 || /* timeout */ + responseInfo.statusCode == -1003 || /* unknown host */ + responseInfo.statusCode == -1004 || /* cannot connect to host */ + responseInfo.statusCode == -1005 || /* connection lost */ + responseInfo.statusCode == -1009 || /* not connected to host */ + responseInfo.isTlsError()); } - private synchronized void completeAction(Request request, + private synchronized void completeAction(IUploadServer server, ResponseInfo responseInfo, JSONObject response, UploadSingleRequestMetrics requestMetrics, RequestCompleteHandler completeHandler) { - if (client == null){ + if (client == null) { return; } client = null; - if (completeHandler != null){ + updateHostNetworkStatus(responseInfo, server, requestMetrics); + reportRequest(responseInfo, server, requestMetrics); + + if (completeHandler != null) { completeHandler.complete(responseInfo, requestMetricsList, response); } - reportRequest(responseInfo, request, requestMetrics); + } + + private void updateHostNetworkStatus(ResponseInfo responseInfo, IUploadServer server, UploadSingleRequestMetrics requestMetrics) { + if (requestMetrics == null) { + return; + } + long byteCount = requestMetrics.bytesSend(); + long second = requestMetrics.totalElapsedTime(); + if (second > 0 && byteCount >= 1024 * 1024) { + int speed = (int) (byteCount * 1000 / second); + String type = NetworkStatusManager.getNetworkStatusType(server.getHost(), server.getIp()); + NetworkStatusManager.getInstance().updateNetworkStatus(type, speed); + } } private void reportRequest(ResponseInfo responseInfo, - Request request, - UploadSingleRequestMetrics requestMetrics){ + IUploadServer server, + UploadSingleRequestMetrics requestMetrics) { - if (token == null || !token.isValid() || requestInfo == null || !requestInfo.shouldReportRequestLog() || requestMetrics == null){ + if (token == null || !token.isValid() || requestInfo == null || !requestInfo.shouldReportRequestLog() || requestMetrics == null) { return; } long currentTimestamp = Utils.currentTimestamp(); ReportItem item = new ReportItem(); item.setReport(ReportItem.LogTypeRequest, ReportItem.RequestKeyLogType); - item.setReport((currentTimestamp/1000), ReportItem.RequestKeyUpTime); + item.setReport((currentTimestamp / 1000), ReportItem.RequestKeyUpTime); item.setReport(ReportItem.requestReportStatusCode(responseInfo), ReportItem.RequestKeyStatusCode); item.setReport(responseInfo != null ? responseInfo.reqId : null, ReportItem.RequestKeyRequestId); - item.setReport(request != null ? request.host : null, ReportItem.RequestKeyHost); + item.setReport(requestMetrics.request != null ? requestMetrics.request.host : null, ReportItem.RequestKeyHost); item.setReport(requestMetrics.remoteAddress, ReportItem.RequestKeyRemoteIp); item.setReport(requestMetrics.remotePort, ReportItem.RequestKeyPort); item.setReport(requestInfo.bucket, ReportItem.RequestKeyTargetBucket); @@ -197,7 +221,7 @@ private void reportRequest(ResponseInfo responseInfo, String errorType = ReportItem.requestReportErrorType(responseInfo); item.setReport(errorType, ReportItem.RequestKeyErrorType); String errorDesc = null; - if (responseInfo != null && errorType != null){ + if (responseInfo != null && errorType != null) { errorDesc = responseInfo.error != null ? responseInfo.error : responseInfo.message; } item.setReport(errorDesc, ReportItem.RequestKeyErrorDescription); @@ -210,9 +234,9 @@ private void reportRequest(ResponseInfo responseInfo, item.setReport(Utils.getCurrentNetworkType(), ReportItem.RequestKeyNetworkType); item.setReport(Utils.getCurrentSignalStrength(), ReportItem.RequestKeySignalStrength); - item.setReport(request.uploadServer.getSource(), ReportItem.RequestKeyPrefetchedDnsSource); - if (request.uploadServer.getIpPrefetchedTime() != null){ - Long prefetchTime = request.uploadServer.getIpPrefetchedTime() - currentTimestamp; + item.setReport(server.getSource(), ReportItem.RequestKeyPrefetchedDnsSource); + if (server.getIpPrefetchedTime() != null) { + Long prefetchTime = server.getIpPrefetchedTime() - currentTimestamp; item.setReport(prefetchTime, ReportItem.RequestKeyPrefetchedBefore); } item.setReport(DnsPrefetcher.getInstance().lastPrefetchErrorMessage, ReportItem.RequestKeyPrefetchedErrorMessage); @@ -220,6 +244,18 @@ private void reportRequest(ResponseInfo responseInfo, item.setReport(requestMetrics.clientName, ReportItem.RequestKeyHttpClient); item.setReport(requestMetrics.clientVersion, ReportItem.RequestKeyHttpClientVersion); + if (requestMetrics.connectCheckMetrics != null) { + String connectCheckDuration = String.format(Locale.ENGLISH,"%d", requestMetrics.connectCheckMetrics.totalElapsedTime()); + String connectCheckStatusCode = ""; + if (requestMetrics.connectCheckMetrics.response != null) { + connectCheckStatusCode = String.format(Locale.ENGLISH,"%d", requestMetrics.connectCheckMetrics.response.statusCode); + } + String networkMeasuring = String.format("duration:%s status_code:%s", connectCheckDuration, connectCheckStatusCode); + item.setReport(networkMeasuring, ReportItem.RequestKeyNetworkMeasuring); + } + + item.setReport(requestMetrics.httpVersion, ReportItem.RequestKeyHttpVersion); + UploadInfoReporter.getInstance().report(item, token.token); } diff --git a/library/src/main/java/com/qiniu/android/http/request/IUploadRegion.java b/library/src/main/java/com/qiniu/android/http/request/IUploadRegion.java index 6afd16339..300c65bf2 100644 --- a/library/src/main/java/com/qiniu/android/http/request/IUploadRegion.java +++ b/library/src/main/java/com/qiniu/android/http/request/IUploadRegion.java @@ -13,5 +13,5 @@ public interface IUploadRegion { void setupRegionData(ZoneInfo zoneInfo); - IUploadServer getNextServer(boolean isOldServer, ResponseInfo responseInfo, IUploadServer freezeServer); + IUploadServer getNextServer(UploadRequestState requestState, ResponseInfo responseInfo, IUploadServer freezeServer); } diff --git a/library/src/main/java/com/qiniu/android/http/request/IUploadServer.java b/library/src/main/java/com/qiniu/android/http/request/IUploadServer.java index d8328743f..9c93c1a41 100644 --- a/library/src/main/java/com/qiniu/android/http/request/IUploadServer.java +++ b/library/src/main/java/com/qiniu/android/http/request/IUploadServer.java @@ -1,15 +1,36 @@ package com.qiniu.android.http.request; -public interface IUploadServer { +public abstract class IUploadServer { + public static String HttpVersion1 = "http_version_1"; + public static String HttpVersion2 = "http_version_2"; + public static String HttpVersion3 = "http_version_3"; - String getServerId(); + public boolean isHttp3() { + String httpVersion = getHttpVersion(); + if (httpVersion == null) { + return false; + } + return httpVersion.equals(IUploadServer.HttpVersion3); + } - String getHost(); + public boolean isHttp2() { + String httpVersion = getHttpVersion(); + if (httpVersion == null) { + return false; + } + return httpVersion.equals(IUploadServer.HttpVersion2); + } - String getIp(); + public abstract String getServerId(); - String getSource(); + public abstract String getHttpVersion(); - Long getIpPrefetchedTime(); + public abstract String getHost(); + + public abstract String getIp(); + + public abstract String getSource(); + + public abstract Long getIpPrefetchedTime(); } diff --git a/library/src/main/java/com/qiniu/android/http/request/Request.java b/library/src/main/java/com/qiniu/android/http/request/Request.java index 943d31470..ec6345478 100644 --- a/library/src/main/java/com/qiniu/android/http/request/Request.java +++ b/library/src/main/java/com/qiniu/android/http/request/Request.java @@ -20,8 +20,6 @@ public class Request { public String host; public String ip; - protected IUploadServer uploadServer; - public Request(String urlString, String httpMethod, Map allHeaders, @@ -36,12 +34,12 @@ public Request(String urlString, } public InetAddress getInetAddress(){ - if (host == null || uploadServer == null || uploadServer.getIp() == null) { + if (host == null || ip == null || ip.length() == 0) { return null; } try { - InetAddress ipAddress = InetAddress.getByName(uploadServer.getIp()); + InetAddress ipAddress = InetAddress.getByName(ip); return InetAddress.getByAddress(host, ipAddress.getAddress()); } catch (Exception e) { return null; diff --git a/library/src/main/java/com/qiniu/android/http/request/RequestTransaction.java b/library/src/main/java/com/qiniu/android/http/request/RequestTransaction.java index 8848fac13..363c4eda6 100644 --- a/library/src/main/java/com/qiniu/android/http/request/RequestTransaction.java +++ b/library/src/main/java/com/qiniu/android/http/request/RequestTransaction.java @@ -109,7 +109,7 @@ public boolean shouldRetry(ResponseInfo responseInfo, JSONObject response) { HashMap header = new HashMap<>(); header.put("User-Agent", userAgent); - String action = "/v4/query?ak=" + (token.accessKey != null ? token.accessKey : "") + "&bucket=" + (token.bucket != null ? token.bucket : ""); + String action = String.format("/v4/query?ak=%s&bucket=%s&sdk_version=%s&sdk_name=%s", token.accessKey, token.bucket, Utils.sdkVerion(), Utils.sdkLanguage()); regionRequest.get(action, isAsync, header, shouldRetryHandler, new HttpRegionRequest.RequestCompleteHandler() { @Override public void complete(ResponseInfo responseInfo, UploadRegionRequestMetrics requestMetrics, JSONObject response) { diff --git a/library/src/main/java/com/qiniu/android/http/request/UploadRequestState.java b/library/src/main/java/com/qiniu/android/http/request/UploadRequestState.java index c7f5b063c..a25b01b7b 100644 --- a/library/src/main/java/com/qiniu/android/http/request/UploadRequestState.java +++ b/library/src/main/java/com/qiniu/android/http/request/UploadRequestState.java @@ -1,7 +1,8 @@ package com.qiniu.android.http.request; -class UploadRequestState { +public class UploadRequestState { + private boolean isUseOldServer; private boolean isUserCancel; boolean isUserCancel(){ @@ -11,4 +12,19 @@ boolean isUserCancel(){ void setUserCancel(boolean isUserCancel) { this.isUserCancel = isUserCancel; } + + public boolean isUseOldServer() { + return isUseOldServer; + } + + public void setUseOldServer(boolean useOldServer) { + isUseOldServer = useOldServer; + } + + protected UploadRequestState clone() { + UploadRequestState state = new UploadRequestState(); + state.isUseOldServer = isUseOldServer; + state.isUserCancel = isUserCancel; + return state; + } } diff --git a/library/src/main/java/com/qiniu/android/http/request/httpclient/SystemHttpClient.java b/library/src/main/java/com/qiniu/android/http/request/httpclient/SystemHttpClient.java index d8a8b78fa..9161a8908 100644 --- a/library/src/main/java/com/qiniu/android/http/request/httpclient/SystemHttpClient.java +++ b/library/src/main/java/com/qiniu/android/http/request/httpclient/SystemHttpClient.java @@ -81,7 +81,7 @@ public void request(Request request, completeHandler = complete; okhttp3.Request.Builder requestBuilder = createRequestBuilder(requestProgress); - if (requestBuilder == null){ + if (requestBuilder == null) { ResponseInfo responseInfo = ResponseInfo.invalidArgument("invalid http request"); handleError(request, responseInfo.statusCode, responseInfo.message, complete); return; @@ -90,14 +90,14 @@ public void request(Request request, ResponseTag tag = new ResponseTag(); call = httpClient.newCall(requestBuilder.tag(tag).build()); - if (isAsync){ + if (isAsync) { call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); String msg = e.getMessage(); int status = getStatusCodeByException(e); - if (call.isCanceled()){ + if (call.isCanceled()) { status = ResponseInfo.Cancelled; msg = "user cancelled"; } @@ -123,7 +123,7 @@ public void run() { e.printStackTrace(); String msg = e.getMessage(); int status = getStatusCodeByException(e); - if (call.isCanceled()){ + if (call.isCanceled()) { status = ResponseInfo.Cancelled; msg = "user cancelled"; } @@ -140,8 +140,8 @@ public synchronized void cancel() { } } - private OkHttpClient createHttpClient(ProxyConfiguration connectionProxy){ - if (currentRequest == null){ + private OkHttpClient createHttpClient(ProxyConfiguration connectionProxy) { + if (currentRequest == null) { return null; } @@ -159,7 +159,7 @@ private OkHttpClient createHttpClient(ProxyConfiguration connectionProxy){ clientBuilder.dns(new Dns() { @Override public List lookup(String s) throws UnknownHostException { - if (currentRequest.getInetAddress() != null && s.equals(currentRequest.host)){ + if (currentRequest.getInetAddress() != null && s.equals(currentRequest.host)) { List inetAddressList = new ArrayList<>(); inetAddressList.add(currentRequest.getInetAddress()); return inetAddressList; @@ -199,14 +199,14 @@ public okhttp3.Response intercept(Chain chain) throws IOException { return clientBuilder.build(); } - private synchronized static ConnectionPool getConnectPool(){ - if (pool == null){ + private synchronized static ConnectionPool getConnectPool() { + if (pool == null) { pool = new ConnectionPool(5, 10, TimeUnit.MINUTES); } return pool; } - private okhttp3.Request.Builder createRequestBuilder(final RequestClientProgress progress){ + private okhttp3.Request.Builder createRequestBuilder(final RequestClientProgress progress) { if (currentRequest == null) { return null; } @@ -215,14 +215,14 @@ private okhttp3.Request.Builder createRequestBuilder(final RequestClientProgress okhttp3.Request.Builder requestBuilder = null; if (currentRequest.httpMethod.equals(Request.HttpMethodHEAD) || - currentRequest.httpMethod.equals(Request.HttpMethodGet)){ + currentRequest.httpMethod.equals(Request.HttpMethodGet)) { requestBuilder = new okhttp3.Request.Builder().get().url(currentRequest.urlString); - for (String key : currentRequest.allHeaders.keySet()){ + for (String key : currentRequest.allHeaders.keySet()) { String value = currentRequest.allHeaders.get(key); requestBuilder.header(key, value); } } else if (currentRequest.httpMethod.equals(Request.HttpMethodPOST) || - currentRequest.httpMethod.equals(Request.HttpMethodPUT)){ + currentRequest.httpMethod.equals(Request.HttpMethodPUT)) { requestBuilder = new okhttp3.Request.Builder().url(currentRequest.urlString); requestBuilder = requestBuilder.headers(allHeaders); @@ -240,15 +240,15 @@ private okhttp3.Request.Builder createRequestBuilder(final RequestClientProgress rbody = new CountingRequestBody(rbody, new ProgressHandler() { @Override public void onProgress(long bytesWritten, long totalSize) { - if (progress != null){ - progress.progress(bytesWritten, totalSize); - } + if (progress != null) { + progress.progress(bytesWritten, totalSize); + } } }, currentRequest.httpBody.length, null); - if (currentRequest.httpMethod.equals(Request.HttpMethodPOST)){ + if (currentRequest.httpMethod.equals(Request.HttpMethodPOST)) { requestBuilder = requestBuilder.post(rbody); - } else if (currentRequest.httpMethod.equals(Request.HttpMethodPUT)){ + } else if (currentRequest.httpMethod.equals(Request.HttpMethodPUT)) { requestBuilder = requestBuilder.put(rbody); } @@ -256,86 +256,127 @@ public void onProgress(long bytesWritten, long totalSize) { return requestBuilder; } - private EventListener createEventLister(){ + private EventListener createEventLister() { return new EventListener() { - @Override public void callStart(Call call) { + @Override + public void callStart(Call call) { metrics.startDate = new Date(); } - @Override public void dnsStart(Call call, - String domainName) { + + @Override + public void dnsStart(Call call, + String domainName) { metrics.domainLookupStartDate = new Date(); } - @Override public void dnsEnd(Call call, - String domainName, - List inetAddressList) { + + @Override + public void dnsEnd(Call call, + String domainName, + List inetAddressList) { metrics.domainLookupEndDate = new Date(); } - @Override public void connectStart(Call call, - InetSocketAddress inetSocketAddress, - Proxy proxy) { + + @Override + public void connectStart(Call call, + InetSocketAddress inetSocketAddress, + Proxy proxy) { metrics.connectStartDate = new Date(); metrics.remoteAddress = inetSocketAddress.getAddress().getHostAddress(); metrics.remotePort = inetSocketAddress.getPort(); metrics.localAddress = AndroidNetwork.getHostIP(); } - @Override public void secureConnectStart(Call call) { + + @Override + public void secureConnectStart(Call call) { metrics.connectEndDate = new Date(); } - @Override public void secureConnectEnd(Call call, - Handshake handshake) { + + @Override + public void secureConnectEnd(Call call, + Handshake handshake) { metrics.secureConnectionStartDate = new Date(); } - @Override public void connectEnd(Call call, - InetSocketAddress inetSocketAddress, - Proxy proxy, - Protocol protocol) { + + @Override + public void connectEnd(Call call, + InetSocketAddress inetSocketAddress, + Proxy proxy, + Protocol protocol) { metrics.secureConnectionEndDate = new Date(); } - @Override public void connectFailed(Call call, - InetSocketAddress inetSocketAddress, - Proxy proxy, - Protocol protocol, - IOException ioe) { + + @Override + public void connectFailed(Call call, + InetSocketAddress inetSocketAddress, + Proxy proxy, + Protocol protocol, + IOException ioe) { metrics.connectEndDate = new Date(); } - @Override public void connectionAcquired(Call call, Connection connection) { + + @Override + public void connectionAcquired(Call call, Connection connection) { } - @Override public void connectionReleased(Call call, Connection connection) { + + @Override + public void connectionReleased(Call call, Connection connection) { } - @Override public void requestHeadersStart(Call call) { + + @Override + public void requestHeadersStart(Call call) { metrics.requestStartDate = new Date(); } - @Override public void requestHeadersEnd(Call call, okhttp3.Request request) { + + @Override + public void requestHeadersEnd(Call call, okhttp3.Request request) { metrics.countOfRequestHeaderBytesSent = request.headers().toString().length(); } - @Override public void requestBodyStart(Call call) { + + @Override + public void requestBodyStart(Call call) { } - @Override public void requestBodyEnd(Call call, long byteCount) { + + @Override + public void requestBodyEnd(Call call, long byteCount) { metrics.requestEndDate = new Date(); metrics.countOfRequestBodyBytesSent = byteCount; } + public void requestFailed(Call call, IOException ioe) { metrics.requestEndDate = new Date(); metrics.countOfRequestBodyBytesSent = 0; } - @Override public void responseHeadersStart(Call call) { + + @Override + public void responseHeadersStart(Call call) { metrics.responseStartDate = new Date(); } - @Override public void responseHeadersEnd(Call call, Response response) { + + @Override + public void responseHeadersEnd(Call call, Response response) { } - @Override public void responseBodyStart(Call call) { + + @Override + public void responseBodyStart(Call call) { } - @Override public void responseBodyEnd(Call call, long byteCount) { + + @Override + public void responseBodyEnd(Call call, long byteCount) { metrics.responseEndDate = new Date(); } + public void responseFailed(Call call, IOException ioe) { metrics.responseEndDate = new Date(); } - @Override public void callEnd(Call call) { + + @Override + public void callEnd(Call call) { metrics.endDate = new Date(); } - @Override public void callFailed(Call call, IOException ioe) { + + @Override + public void callFailed(Call call, IOException ioe) { metrics.endDate = new Date(); } }; @@ -344,15 +385,14 @@ public void responseFailed(Call call, IOException ioe) { private synchronized void handleError(Request request, int responseCode, String errorMsg, - RequestClientCompleteHandler complete){ + RequestClientCompleteHandler complete) { if (metrics == null || metrics.response != null) { return; } - ResponseInfo info = ResponseInfo.create(request, responseCode, null,null, errorMsg); + ResponseInfo info = ResponseInfo.create(request, responseCode, null, null, errorMsg); metrics.response = info; - metrics.response = null; - metrics.request = null; + metrics.request = request; complete.complete(info, metrics, info.response); releaseResource(); @@ -360,7 +400,7 @@ private synchronized void handleError(Request request, private synchronized void handleResponse(Request request, okhttp3.Response response, - RequestClientCompleteHandler complete){ + RequestClientCompleteHandler complete) { if (metrics == null || metrics.response != null) { return; } @@ -384,14 +424,15 @@ private synchronized void handleResponse(Request request, errorMessage = e.getMessage(); } - if (responseBody == null){ + if (responseBody == null) { errorMessage = response.message(); - } else if (responseContentType(response) != "application/json"){ + } else if (responseContentType(response) != "application/json") { String responseString = new String(responseBody); - if (responseString.length() > 0){ + if (responseString.length() > 0) { try { responseJson = new JSONObject(responseString); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } } } else { try { @@ -405,12 +446,20 @@ private synchronized void handleResponse(Request request, final ResponseInfo info = ResponseInfo.create(request, statusCode, responseHeader, responseJson, errorMessage); metrics.response = info; + metrics.request = request; + if (response.protocol() == Protocol.HTTP_1_0) { + metrics.httpVersion = "1.0"; + } else if (response.protocol() == Protocol.HTTP_1_1) { + metrics.httpVersion = "1.1"; + } else if (response.protocol() == Protocol.HTTP_2) { + metrics.httpVersion = "2"; + } complete.complete(info, metrics, info.response); releaseResource(); } - private void releaseResource(){ + private void releaseResource() { this.currentRequest = null; this.requestProgress = null; this.completeHandler = null; @@ -427,10 +476,10 @@ private static String responseContentType(okhttp3.Response response) { return mediaType.type() + "/" + mediaType.subtype(); } - private int getStatusCodeByException(Exception e){ + private int getStatusCodeByException(Exception e) { int statusCode = NetworkError; String msg = e.getMessage(); - if (msg != null && msg.contains("Canceled")){ + if (msg != null && msg.contains("Canceled")) { statusCode = ResponseInfo.Cancelled; } else if (e instanceof CancellationHandler.CancellationException) { statusCode = ResponseInfo.Cancelled; diff --git a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadDomainRegion.java b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadDomainRegion.java index fe659801a..bc5c51bb4 100644 --- a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadDomainRegion.java +++ b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadDomainRegion.java @@ -4,8 +4,10 @@ import com.qiniu.android.http.ResponseInfo; import com.qiniu.android.http.dns.DnsPrefetcher; import com.qiniu.android.http.dns.IDnsNetworkAddress; +import com.qiniu.android.http.networkStatus.UploadServerNetworkStatus; import com.qiniu.android.http.request.IUploadRegion; import com.qiniu.android.http.request.IUploadServer; +import com.qiniu.android.http.request.UploadRequestState; import com.qiniu.android.storage.GlobalConfiguration; import com.qiniu.android.utils.LogUtil; import com.qiniu.android.utils.StringUtils; @@ -17,12 +19,16 @@ public class UploadDomainRegion implements IUploadRegion { + private static int Http3FrozenTime = 3600 * 24; - // 是否获取过,PS:当第一次获取Domain,而区域所有Domain又全部冻结时,返回一个domain尝试一次 - private boolean hasGot; + // 是否支持http3 + private boolean http3Enabled; + + // 是否冻结过Host,PS:如果没有冻结过 Host,则当前 Region 上传也就不会有错误信息,可能会返回-9,所以必须要再进行一次尝试 + private boolean hasFreezeHost; private boolean isAllFrozen; // 局部冻结管理对象 - private UploadServerFreezeManager partialFreezeManager = new UploadServerFreezeManager(); + private UploadServerFreezeManager partialHttp2Freezer = new UploadServerFreezeManager(); private ArrayList domainHostList; private HashMap domainHashMap; @@ -78,6 +84,9 @@ public void setupRegionData(ZoneInfo zoneInfo) { this.zoneInfo = zoneInfo; isAllFrozen = false; + http3Enabled = zoneInfo.http3Enabled; + // 暂不开启 + http3Enabled = false; ArrayList domainHostList = new ArrayList<>(); if (zoneInfo.domains != null) { @@ -97,28 +106,98 @@ public void setupRegionData(ZoneInfo zoneInfo) { LogUtil.i("region old:" + StringUtils.toNonnullString(oldDomainHostList)); } + private HashMap createDomainDictionary(List hosts) { + HashMap domainHashMap = new HashMap<>(); + for (int i = 0; i < hosts.size(); i++) { + String host = hosts.get(i); + UploadServerDomain domain = new UploadServerDomain(host); + domainHashMap.put(host, domain); + } + return domainHashMap; + } + @Override - public IUploadServer getNextServer(boolean isOldServer, ResponseInfo responseInfo, IUploadServer freezeServer) { - if (isAllFrozen) { + public IUploadServer getNextServer(UploadRequestState requestState, ResponseInfo responseInfo, IUploadServer freezeServer) { + if (isAllFrozen || requestState == null) { return null; } freezeServerIfNeed(responseInfo, freezeServer); - ArrayList hostList = isOldServer ? oldDomainHostList : domainHostList; - HashMap domainInfo = isOldServer ? oldDomainHashMap : domainHashMap; - IUploadServer server = null; + UploadServer server = null; + boolean isUseOldServer = requestState.isUseOldServer(); + ArrayList hostList = isUseOldServer ? oldDomainHostList : domainHostList; + HashMap domainInfo = isUseOldServer ? oldDomainHashMap : domainHashMap; + + // 1. 优先选择http3 + if (http3Enabled && freezeServer != null && freezeServer.isHttp3()) { + for (String host : hostList) { + UploadServerDomain domain = domainInfo.get(host); + if (domain == null) { + continue; + } + IUploadServer domainServer = domain.getServer(new UploadServerDomain.GetServerCondition() { + @Override + public boolean condition(String host, UploadServer serverP, UploadServer filterServer) { + + // 1.1 剔除冻结对象 + String filterServerIP = filterServer == null ? null : filterServer.getIp(); + String frozenType = UploadServerFreezeUtil.getFrozenType(host, filterServerIP); + boolean isFrozen = UploadServerFreezeUtil.isTypeFrozenByFreezeManagers(frozenType, new UploadServerFreezeManager[]{UploadServerFreezeUtil.globalHttp3Freezer()}); + + if (isFrozen) { + return false; + } + + // 1.2 挑选网络状态最优 + return UploadServerNetworkStatus.isServerNetworkBetter(filterServer, serverP); + } + }); + + server = (UploadServer) UploadServerNetworkStatus.getBetterNetworkServer(domainServer, server); + + if (server != null) { + break; + } + } + + if (server != null) { + server.setHttpVersion(IUploadServer.HttpVersion3); + return server; + } + } + + // 2. 挑选http2 for (String host : hostList) { UploadServerDomain domain = domainInfo.get(host); - if (domain != null) { - server = domain.getServer(new UploadServerFreezeManager[]{partialFreezeManager, UploadServerFreezeManager.getInstance()}); + if (domain == null) { + continue; } + IUploadServer domainServer = domain.getServer(new UploadServerDomain.GetServerCondition() { + @Override + public boolean condition(String host, UploadServer serverP, UploadServer filterServer) { + // 1.1 剔除冻结对象 + String filterServerIP = filterServer == null ? null : filterServer.getIp(); + String frozenType = UploadServerFreezeUtil.getFrozenType(host, filterServerIP); + boolean isFrozen = UploadServerFreezeUtil.isTypeFrozenByFreezeManagers(frozenType, new UploadServerFreezeManager[]{partialHttp2Freezer, UploadServerFreezeUtil.globalHttp2Freezer()}); + + if (isFrozen) { + return false; + } + + // 1.2 挑选网络状态最优 + return UploadServerNetworkStatus.isServerNetworkBetter(filterServer, serverP); + } + }); + + server = (UploadServer) UploadServerNetworkStatus.getBetterNetworkServer(domainServer, server); + if (server != null) { break; } } - if (server == null && !hasGot && hostList.size() > 0) { + if (server == null && !hasFreezeHost && hostList.size() > 0) { int index = (int) (Math.random() * hostList.size()); String host = hostList.get(index); UploadServerDomain domain = domainInfo.get(host); @@ -127,16 +206,13 @@ public IUploadServer getNextServer(boolean isOldServer, ResponseInfo responseInf } unfreezeServer(server); } - hasGot = true; - if (server == null) { + if (server != null) { + server.setHttpVersion(IUploadServer.HttpVersion2); + LogUtil.i("get server host:" + StringUtils.toNonnullString(server.getHost()) + " ip:" + StringUtils.toNonnullString(server.getIp())); + } else { isAllFrozen = true; - } - - if (server == null) { LogUtil.i("get server host:null ip:null"); - } else { - LogUtil.i("get server host:" + StringUtils.toNonnullString(server.getHost()) + " ip:" + StringUtils.toNonnullString(server.getIp())); } return server; @@ -148,34 +224,29 @@ private void freezeServerIfNeed(ResponseInfo responseInfo, IUploadServer freezeS return; } - // 无法连接到Host || Host不可用, 局部冻结 + String frozenType = UploadServerFreezeUtil.getFrozenType(freezeServer.getHost(), freezeServer.getIp()); + // 1. http3 冻结 + if (freezeServer.isHttp3()) { + if (!responseInfo.canConnectToHost() || responseInfo.isHostUnavailable()) { + hasFreezeHost = true; + UploadServerFreezeUtil.globalHttp3Freezer().freezeType(frozenType, Http3FrozenTime); + } + return; + } + + // 2. http2 冻结 + // 2.1 无法连接到Host || Host不可用, 局部冻结 if (!responseInfo.canConnectToHost() || responseInfo.isHostUnavailable()) { + hasFreezeHost = true; LogUtil.i("partial freeze server host:" + StringUtils.toNonnullString(freezeServer.getHost()) + " ip:" + StringUtils.toNonnullString(freezeServer.getIp())); - - UploadServerDomain domain = null; - domain = domainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.freeze(freezeServer.getIp(), partialFreezeManager, GlobalConfiguration.getInstance().partialHostFrozenTime); - } - domain = oldDomainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.freeze(freezeServer.getIp(), partialFreezeManager, GlobalConfiguration.getInstance().partialHostFrozenTime); - } + partialHttp2Freezer.freezeType(frozenType, GlobalConfiguration.getInstance().partialHostFrozenTime); } - // Host不可用,全局冻结 + // 2.2 Host不可用,全局冻结 if (responseInfo.isHostUnavailable()) { + hasFreezeHost = true; LogUtil.i("global freeze server host:" + StringUtils.toNonnullString(freezeServer.getHost()) + " ip:" + StringUtils.toNonnullString(freezeServer.getIp())); - - UploadServerDomain domain = null; - domain = domainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.freeze(freezeServer.getIp(), UploadServerFreezeManager.getInstance(), GlobalConfiguration.getInstance().globalHostFrozenTime); - } - domain = oldDomainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.freeze(freezeServer.getIp(), UploadServerFreezeManager.getInstance(), GlobalConfiguration.getInstance().globalHostFrozenTime); - } + UploadServerFreezeUtil.globalHttp2Freezer().freezeType(frozenType, GlobalConfiguration.getInstance().globalHostFrozenTime); } } @@ -184,31 +255,13 @@ private void unfreezeServer(IUploadServer freezeServer) { return; } - UploadServerDomain domain = null; - domain = domainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.unfreeze(freezeServer.getIp(), new UploadServerFreezeManager[]{partialFreezeManager, UploadServerFreezeManager.getInstance()}); - } - domain = oldDomainHashMap.get(freezeServer.getServerId()); - if (domain != null) { - domain.unfreeze(freezeServer.getIp(), new UploadServerFreezeManager[]{partialFreezeManager, UploadServerFreezeManager.getInstance()}); - } - } - - private HashMap createDomainDictionary(List hosts) { - HashMap domainHashMap = new HashMap<>(); - for (int i = 0; i < hosts.size(); i++) { - String host = hosts.get(i); - UploadServerDomain domain = new UploadServerDomain(host); - domainHashMap.put(host, domain); - } - return domainHashMap; + String frozenType = UploadServerFreezeUtil.getFrozenType(freezeServer.getHost(), freezeServer.getIp()); + partialHttp2Freezer.unfreezeType(frozenType); } private static class UploadServerDomain { - private boolean isAllFrozen = false; protected final String host; protected ArrayList ipGroupList = new ArrayList<>(); @@ -216,59 +269,44 @@ protected UploadServerDomain(String host) { this.host = host; } - protected IUploadServer getServer(UploadServerFreezeManager[] freezeManagerList) { - if (isAllFrozen || host == null || host.length() == 0) { + protected UploadServer getServer(GetServerCondition condition) { + if (host == null || host.length() == 0) { return null; } - if (ipGroupList == null || ipGroupList.size() == 0) { - createIpGroupList(); + synchronized (this) { + if (ipGroupList == null || ipGroupList.size() == 0) { + createIpGroupList(); + } } + UploadServer server = null; + // 解析到IP: if (ipGroupList != null && ipGroupList.size() > 0) { - UploadServer server = null; for (UploadIpGroup ipGroup : ipGroupList) { - // 黑名单中不存在 & 未被冻结 - if (ipGroup.groupType != null && !isGroupFrozenByFreezeManagers(ipGroup.groupType, freezeManagerList)) { - IDnsNetworkAddress networkAddress = ipGroup.getNetworkAddress(); - server = new UploadServer(host, host, networkAddress.getIpValue(), networkAddress.getSourceValue(), networkAddress.getTimestampValue()); + IDnsNetworkAddress networkAddress = ipGroup.getNetworkAddress(); + UploadServer filterServer = new UploadServer(host, host, networkAddress.getIpValue(), networkAddress.getSourceValue(), networkAddress.getTimestampValue()); + + if (condition == null || condition.condition(host, server, filterServer)) { + server = filterServer; + } + + if (condition == null) { break; } } - if (server == null) { - isAllFrozen = true; - } + return server; } // 未解析到IP: - // 黑名单中不存在 & 未被冻结 - String groupType = Utils.getIpType(null, host); - if (groupType != null && !isGroupFrozenByFreezeManagers(groupType, freezeManagerList)) { - return new UploadServer(host, host, null, null, null); - } else { - isAllFrozen = true; - return null; + if (condition == null || condition.condition(host, null, null)) { + // 未解析时,没有可比性,直接返回自身,自身即为最优 + server = new UploadServer(host, host, null, null, null); } - } - protected boolean isGroupFrozenByFreezeManagers(String groupType, UploadServerFreezeManager[] freezeManagerList) { - if (groupType == null) { - return true; - } - if (freezeManagerList == null || freezeManagerList.length == 0) { - return false; - } - - boolean isFrozen = false; - for (UploadServerFreezeManager freezeManager : freezeManagerList) { - isFrozen = freezeManager.isFreezeHost(host, groupType); - if (isFrozen) { - break; - } - } - return isFrozen; + return server; } protected UploadServer getOneServer() { @@ -286,7 +324,7 @@ protected UploadServer getOneServer() { } } - private synchronized void createIpGroupList() { + private void createIpGroupList() { if (ipGroupList != null && ipGroupList.size() > 0) { return; } @@ -322,20 +360,8 @@ private synchronized void createIpGroupList() { this.ipGroupList = ipGroupList; } - protected void freeze(String ip, UploadServerFreezeManager freezeManager, int frozenTime) { - if (freezeManager == null) { - return; - } - freezeManager.freezeHost(host, Utils.getIpType(ip, this.host), frozenTime); - } - - protected void unfreeze(String ip, UploadServerFreezeManager[] freezeManagerList) { - if (freezeManagerList == null || freezeManagerList.length == 0) { - return; - } - for (UploadServerFreezeManager freezeManager : freezeManagerList) { - freezeManager.unfreezeHost(host, Utils.getIpType(ip, this.host)); - } + protected interface GetServerCondition { + boolean condition(String host, UploadServer server, UploadServer filterServer); } } diff --git a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServer.java b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServer.java index 1f79ae2d3..dabb9cd89 100644 --- a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServer.java +++ b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServer.java @@ -2,13 +2,14 @@ import com.qiniu.android.http.request.IUploadServer; -public class UploadServer implements IUploadServer { +public class UploadServer extends IUploadServer { private final String serverId; private final String host; private final String ip; private final String source; private final Long ipPrefetchedTime; + private String httpVersion; public UploadServer(String serverId, String host, @@ -27,6 +28,15 @@ public String getServerId() { return this.serverId; } + @Override + public String getHttpVersion() { + return httpVersion; + } + + public void setHttpVersion(String httpVersion) { + this.httpVersion = httpVersion; + } + @Override public String getIp() { return ip; diff --git a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManager.java b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManager.java index 620842473..c21bdb1f2 100644 --- a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManager.java +++ b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeManager.java @@ -20,58 +20,46 @@ public static UploadServerFreezeManager getInstance() { return manager; } - public boolean isFreezeHost(String host, String type) { - if (host == null || host.length() == 0) { + public boolean isTypeFrozen(String type) { + if (type == null || type.length() == 0) { return true; } boolean isFrozen = true; - String infoKey = getItemInfoKey(host, type); - UploadServerFreezeItem item = frozenInfo.get(infoKey); + UploadServerFreezeItem item = frozenInfo.get(type); if (item == null || !item.isFrozenByDate(new Date())) { isFrozen = false; } return isFrozen; } - public void freezeHost(String host, String type, int frozenTime) { - if (host == null || host.length() == 0) { + public void freezeType(String type, int frozenTime) { + if (type == null || type.length() == 0) { return; } - String infoKey = getItemInfoKey(host, type); - UploadServerFreezeItem item = frozenInfo.get(infoKey); + UploadServerFreezeItem item = frozenInfo.get(type); if (item == null) { - item = new UploadServerFreezeItem(host, type); - frozenInfo.put(infoKey, item); + item = new UploadServerFreezeItem(type); + frozenInfo.put(type, item); } item.freeze(frozenTime); } - public void unfreezeHost(String host, String type) { - if (host == null || host.length() == 0) { + public void unfreezeType(String type) { + if (type == null || type.length() == 0) { return; } - String infoKey = getItemInfoKey(host, type); - if (infoKey != null) { - frozenInfo.remove(infoKey); - } - } - - private String getItemInfoKey(String host, String type) { - return String.format("%s:%s", (host != null ? host : "none"), (type != null ? type : "none")); + frozenInfo.remove(type); } private static class UploadServerFreezeItem { - protected final String host; protected final String type; - protected Date freezeDate; + private Date freezeDate; - protected UploadServerFreezeItem(String host, - String type) { - this.host = host; + private UploadServerFreezeItem(String type) { this.type = type; } - protected synchronized boolean isFrozenByDate(Date date) { + private synchronized boolean isFrozenByDate(Date date) { boolean isFrozen = true; if (freezeDate == null || freezeDate.getTime() < date.getTime()) { isFrozen = false; @@ -79,7 +67,7 @@ protected synchronized boolean isFrozenByDate(Date date) { return isFrozen; } - protected synchronized void freeze(int frozenTime) { + private synchronized void freeze(int frozenTime) { freezeDate = new Date(Utils.currentTimestamp() + frozenTime * 1000); } diff --git a/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeUtil.java b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeUtil.java new file mode 100644 index 000000000..fa8c09aee --- /dev/null +++ b/library/src/main/java/com/qiniu/android/http/serverRegion/UploadServerFreezeUtil.java @@ -0,0 +1,40 @@ +package com.qiniu.android.http.serverRegion; + +import com.qiniu.android.utils.Utils; + +import java.util.List; + +public class UploadServerFreezeUtil { + private final static UploadServerFreezeManager globalHttp3Freezer = new UploadServerFreezeManager(); + public static UploadServerFreezeManager globalHttp3Freezer() { + return globalHttp3Freezer; + } + + private final static UploadServerFreezeManager globalHttp2Freezer = new UploadServerFreezeManager(); + public static UploadServerFreezeManager globalHttp2Freezer() { + return globalHttp2Freezer; + } + + public static boolean isTypeFrozenByFreezeManagers(String type, UploadServerFreezeManager[] freezeManagerList) { + if (type == null || type.length() == 0) { + return true; + } + if (freezeManagerList == null || freezeManagerList.length == 0) { + return false; + } + + boolean isFrozen = false; + for (UploadServerFreezeManager freezeManager : freezeManagerList) { + isFrozen = freezeManager.isTypeFrozen(type); + if (isFrozen) { + break; + } + } + return isFrozen; + } + + public static String getFrozenType(String host, String ip) { + String ipType = Utils.getIpType(ip, host); + return String.format("%s-%s", host, ipType); + } +} diff --git a/library/src/main/java/com/qiniu/android/storage/GlobalConfiguration.java b/library/src/main/java/com/qiniu/android/storage/GlobalConfiguration.java index 2e73b8d89..f17d2b6bc 100644 --- a/library/src/main/java/com/qiniu/android/storage/GlobalConfiguration.java +++ b/library/src/main/java/com/qiniu/android/storage/GlobalConfiguration.java @@ -52,7 +52,7 @@ public class GlobalConfiguration { * 当请求的 Response 为网络异常时,并发对 connectCheckURLStrings 中 URLString 进行 HEAD 请求,以此检测当前网络状态的链接状态,其中任意一个 URLString 链接成功则认为当前网络状态链接良好; * 当 connectCheckURLStrings 为 nil 或者 空数组时则弃用检测功能。 */ - public String[] connectCheckURLStrings = new String[]{"http://www.qiniu.com", "http://www.baidu.com", "http://www.google.com"}; + public String[] connectCheckURLStrings = new String[]{"https://www.qiniu.com", "https://www.baidu.com", "https://www.google.com"}; /** * 网络连接状态检测HEAD请求超时,默认:3s