diff --git a/Storage/Storage.Test/ObjectControllerTests.cs b/Storage/Storage.Test/ObjectControllerTests.cs
index fc99e8f..c0b118f 100644
--- a/Storage/Storage.Test/ObjectControllerTests.cs
+++ b/Storage/Storage.Test/ObjectControllerTests.cs
@@ -26,6 +26,7 @@ namespace LeanCloudTests {
Assert.NotNull(obj.ObjectId);
Assert.NotNull(obj.CreatedAt);
Assert.NotNull(obj.UpdatedAt);
+ await Task.Delay(10000);
}
}
}
diff --git a/Storage/Storage/Internal/AppRouter/AppRouterController.cs b/Storage/Storage/Internal/AppRouter/AppRouterController.cs
index 1e0c73d..137b96f 100644
--- a/Storage/Storage/Internal/AppRouter/AppRouterController.cs
+++ b/Storage/Storage/Internal/AppRouter/AppRouterController.cs
@@ -3,101 +3,58 @@ using System.Net;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using LeanCloud.Storage.Internal;
+using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal
{
public class AppRouterController : IAppRouterController
{
private AppRouterState currentState;
- private object mutex = new object();
+ private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
///
/// Get current app's router state
///
///
- public AppRouterState Get()
- {
- if (string.IsNullOrEmpty(AVClient.CurrentConfiguration.ApplicationId))
- {
+ public AppRouterState Get() {
+ if (string.IsNullOrEmpty(AVClient.CurrentConfiguration.ApplicationId)) {
throw new AVException(AVException.ErrorCode.NotInitialized, "ApplicationId can not be null.");
}
- AppRouterState state;
- state = AppRouterState.GetInitial(AVClient.CurrentConfiguration.ApplicationId, AVClient.CurrentConfiguration.Region);
- lock (mutex)
- {
- if (currentState != null)
- {
- if (!currentState.isExpired())
- state = currentState;
+ try {
+ locker.EnterUpgradeableReadLock();
+ if (currentState != null && !currentState.IsExpired) {
+ return currentState;
}
+ // 从 AppRouter 获取服务器地址,只触发,不等待
+ QueryAsync(CancellationToken.None).OnSuccess(t => {
+ locker.EnterWriteLock();
+ currentState = t.Result;
+ currentState.Source = "router";
+ locker.ExitWriteLock();
+ });
+ return AppRouterState.GetFallbackServers(AVClient.CurrentConfiguration.ApplicationId, AVClient.CurrentConfiguration.Region);
+ } finally {
+ locker.ExitUpgradeableReadLock();
}
-
- if (state.isExpired())
- {
- lock (mutex)
- {
- state.FetchedAt = DateTime.Now + TimeSpan.FromMinutes(10);
- }
- Task.Factory.StartNew(RefreshAsync);
- }
- return state;
}
- public Task RefreshAsync()
- {
- return QueryAsync(CancellationToken.None).ContinueWith(t =>
- {
- if (!t.IsFaulted && !t.IsCanceled)
- {
- lock (mutex)
- {
- currentState = t.Result;
- }
- }
- });
- }
-
- public Task QueryAsync(CancellationToken cancellationToken)
- {
+ public async Task QueryAsync(CancellationToken cancellationToken) {
string appId = AVClient.CurrentConfiguration.ApplicationId;
string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId);
- return AVClient.HttpGetAsync(new Uri(url)).ContinueWith(t =>
- {
- var tcs = new TaskCompletionSource();
- if (t.Result.Item1 != HttpStatusCode.OK)
- {
- tcs.SetException(new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null));
- }
- else
- {
- var result = Json.Parse(t.Result.Item2) as IDictionary;
- tcs.SetResult(ParseAppRouterState(result));
- }
- return tcs.Task;
- }).Unwrap();
+ var ret = await AVClient.HttpGetAsync(new Uri(url));
+ if (ret.Item1 != HttpStatusCode.OK) {
+ throw new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null);
+ }
+
+ return await JsonUtils.DeserializeObjectAsync(ret.Item2);
}
- private static AppRouterState ParseAppRouterState(IDictionary jsonObj)
- {
- var state = new AppRouterState()
- {
- TTL = (int)jsonObj["ttl"],
- StatsServer = jsonObj["stats_server"] as string,
- RealtimeRouterServer = jsonObj["rtm_router_server"] as string,
- PushServer = jsonObj["push_server"] as string,
- EngineServer = jsonObj["engine_server"] as string,
- ApiServer = jsonObj["api_server"] as string,
- Source = "network",
- };
- return state;
- }
-
- public void Clear()
- {
+ public void Clear() {
+ locker.EnterWriteLock();
currentState = null;
+ locker.ExitWriteLock();
}
}
}
diff --git a/Storage/Storage/Internal/AppRouter/AppRouterState.cs b/Storage/Storage/Internal/AppRouter/AppRouterState.cs
index b2e61f9..61438ed 100644
--- a/Storage/Storage/Internal/AppRouter/AppRouterState.cs
+++ b/Storage/Storage/Internal/AppRouter/AppRouterState.cs
@@ -1,32 +1,59 @@
using System;
+using Newtonsoft.Json;
namespace LeanCloud.Storage.Internal
{
public class AppRouterState
{
- public long TTL { get; internal set; }
- public string ApiServer { get; internal set; }
- public string EngineServer { get; internal set; }
- public string PushServer { get; internal set; }
- public string RealtimeRouterServer { get; internal set; }
- public string StatsServer { get; internal set; }
- public string Source { get; internal set; }
+ [JsonProperty("ttl")]
+ public long TTL {
+ get; internal set;
+ }
- public DateTime FetchedAt { get; internal set; }
+ [JsonProperty("api_server")]
+ public string ApiServer {
+ get; internal set;
+ }
- private static object mutex = new object();
+ [JsonProperty("engine_server")]
+ public string EngineServer {
+ get; internal set;
+ }
- public AppRouterState()
- {
+ [JsonProperty("push_server")]
+ public string PushServer {
+ get; internal set;
+ }
+
+ [JsonProperty("rtm_router_server")]
+ public string RTMServer {
+ get; internal set;
+ }
+
+ [JsonProperty("stats_server")]
+ public string StatsServer {
+ get; internal set;
+ }
+
+ public string Source {
+ get; internal set;
+ }
+
+ public DateTime FetchedAt {
+ get; internal set;
+ }
+
+ public AppRouterState() {
FetchedAt = DateTime.Now;
}
///
/// Is this app router state expired.
///
- public bool isExpired()
- {
- return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL);
+ public bool IsExpired {
+ get {
+ return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL);
+ }
}
///
@@ -35,46 +62,43 @@ namespace LeanCloud.Storage.Internal
/// Current app's appId
/// Current app's region
/// Initial app router state
- public static AppRouterState GetInitial(string appId, AVClient.Configuration.AVRegion region)
- {
+ public static AppRouterState GetFallbackServers(string appId, AVClient.Configuration.AVRegion region) {
var regionValue = (int)region;
var prefix = appId.Substring(0, 8).ToLower();
switch (regionValue)
{
case 0:
// 华北
- return new AppRouterState()
- {
+ return new AppRouterState {
TTL = -1,
- ApiServer = String.Format("{0}.api.lncld.net", prefix),
- EngineServer = String.Format("{0}.engine.lncld.net", prefix),
- PushServer = String.Format("{0}.push.lncld.net", prefix),
- RealtimeRouterServer = String.Format("{0}.rtm.lncld.net", prefix),
- StatsServer = String.Format("{0}.stats.lncld.net", prefix),
- Source = "initial",
+ ApiServer = $"{prefix}.api.lncld.net",
+ EngineServer = $"{prefix}.engine.lncld.net",
+ PushServer = $"{prefix}.push.lncld.net",
+ RTMServer = $"{prefix}.rtm.lncld.net",
+ StatsServer = $"{prefix}.stats.lncld.net",
+ Source = "fallback",
};
case 1:
// 美国
- return new AppRouterState()
- {
+ return new AppRouterState {
TTL = -1,
- ApiServer = string.Format("{0}.api.lncldglobal.com", prefix),
- EngineServer = string.Format("{0}.engine.lncldglobal.com", prefix),
- PushServer = string.Format("{0}.push.lncldglobal.com", prefix),
- RealtimeRouterServer = string.Format("{0}.rtm.lncldglobal.com", prefix),
- StatsServer = string.Format("{0}.stats.lncldglobal.com", prefix),
- Source = "initial",
+ ApiServer = $"{prefix}.api.lncldglobal.com",
+ EngineServer = $"{prefix}.engine.lncldglobal.com",
+ PushServer = $"{prefix}.push.lncldglobal.com",
+ RTMServer = $"{prefix}.rtm.lncldglobal.com",
+ StatsServer = $"{prefix}.stats.lncldglobal.com",
+ Source = "fallback",
};
case 2:
// 华东
- return new AppRouterState() {
+ return new AppRouterState {
TTL = -1,
- ApiServer = string.Format("{0}.api.lncldapi.com", prefix),
- EngineServer = string.Format("{0}.engine.lncldapi.com", prefix),
- PushServer = string.Format("{0}.push.lncldapi.com", prefix),
- RealtimeRouterServer = string.Format("{0}.rtm.lncldapi.com", prefix),
- StatsServer = string.Format("{0}.stats.lncldapi.com", prefix),
- Source = "initial",
+ ApiServer = $"{prefix}.api.lncldapi.com",
+ EngineServer = $"{prefix}.engine.lncldapi.com",
+ PushServer = $"{prefix}.push.lncldapi.com",
+ RTMServer = $"{prefix}.rtm.lncldapi.com",
+ StatsServer = $"{prefix}.stats.lncldapi.com",
+ Source = "fallback",
};
default:
throw new AVException(AVException.ErrorCode.OtherCause, "invalid region");
diff --git a/Storage/Storage/Internal/AppRouter/IAppRouterController.cs b/Storage/Storage/Internal/AppRouter/IAppRouterController.cs
index 771ee18..24c3366 100644
--- a/Storage/Storage/Internal/AppRouter/IAppRouterController.cs
+++ b/Storage/Storage/Internal/AppRouter/IAppRouterController.cs
@@ -7,16 +7,7 @@ namespace LeanCloud.Storage.Internal
public interface IAppRouterController
{
AppRouterState Get();
- ///
- /// Start refresh the app router.
- ///
- ///
- Task RefreshAsync();
- void Clear();
- ///
- /// Query the app router.
- ///
- /// New AppRouterState
Task QueryAsync(CancellationToken cancellationToken);
+ void Clear();
}
}
diff --git a/Storage/Storage/Internal/Utilities/JsonExtensions.cs b/Storage/Storage/Internal/Utilities/JsonExtensions.cs
new file mode 100644
index 0000000..af58f27
--- /dev/null
+++ b/Storage/Storage/Internal/Utilities/JsonExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+using Newtonsoft.Json;
+using System.Threading.Tasks;
+
+namespace LeanCloud.Storage.Internal {
+ public static class JsonUtils {
+ public static Task DeserializeObjectAsync(string str) {
+ var tcs = new TaskCompletionSource();
+ Task.Run(() => {
+ try {
+ tcs.SetResult(JsonConvert.DeserializeObject(str));
+ } catch (Exception e) {
+ tcs.SetException(e);
+ }
+ });
+ return tcs.Task;
+ }
+ }
+}
diff --git a/Storage/Storage/Storage.csproj b/Storage/Storage/Storage.csproj
index 6de1371..46e9e03 100644
--- a/Storage/Storage/Storage.csproj
+++ b/Storage/Storage/Storage.csproj
@@ -8,4 +8,7 @@
+
+
+