From 5d88f601b39f0955d673c84a3a2cb67a032a7916 Mon Sep 17 00:00:00 2001 From: oneRain Date: Mon, 4 Nov 2019 15:45:42 +0800 Subject: [PATCH] =?UTF-8?q?*=20AppRouter.cs:=20chore:=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Common.csproj: * HttpUtils.cs: * TaskExtensions.cs: * AppRouterController.cs: --- .../{AppRouterState.cs => AppRouter.cs} | 25 ++- Common/AppRouter/AppRouterController.cs | 45 +++++- Common/Common.csproj | 3 + Common/Http/HttpUtils.cs | 2 +- Common/Task/TaskExtensions.cs | 148 ++++++++++++++++++ 5 files changed, 208 insertions(+), 15 deletions(-) rename Common/AppRouter/{AppRouterState.cs => AppRouter.cs} (79%) create mode 100644 Common/Task/TaskExtensions.cs diff --git a/Common/AppRouter/AppRouterState.cs b/Common/AppRouter/AppRouter.cs similarity index 79% rename from Common/AppRouter/AppRouterState.cs rename to Common/AppRouter/AppRouter.cs index f163ec7..6b0ae61 100644 --- a/Common/AppRouter/AppRouterState.cs +++ b/Common/AppRouter/AppRouter.cs @@ -2,8 +2,10 @@ using Newtonsoft.Json; namespace LeanCloud.Common { - public class AppRouterState { + public class AppRouter { + // 华东应用 App Id 后缀 const string EAST_CHINA_SUFFIX = "-9Nh9j0Va"; + // 美国应用 App Id 后缀 const string US_SUFFIX = "-MdYXbMMI"; [JsonProperty("ttl")] @@ -36,6 +38,11 @@ namespace LeanCloud.Common { get; internal set; } + [JsonProperty("play_server")] + public string PlayServer { + get; internal set; + } + public string Source { get; internal set; } @@ -44,51 +51,57 @@ namespace LeanCloud.Common { get; internal set; } - public AppRouterState() { + public AppRouter() { FetchedAt = DateTimeOffset.Now; } public bool IsExpired { get { + if (TTL == -1) { + return false; + } return DateTimeOffset.Now > FetchedAt.AddSeconds(TTL); } } - public static AppRouterState GetFallbackServers(string appId) { + public static AppRouter GetFallbackServers(string appId) { var prefix = appId.Substring(0, 8).ToLower(); var suffix = appId.Substring(appId.Length - 9); switch (suffix) { case EAST_CHINA_SUFFIX: // 华东 - return new AppRouterState { + return new AppRouter { TTL = -1, 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", + PlayServer = $"{prefix}.play.lncldapi.com", Source = "fallback", }; case US_SUFFIX: // 美国 - return new AppRouterState { + return new AppRouter { TTL = -1, 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", + PlayServer = $"{prefix}.play.lncldglobal.com", Source = "fallback", }; default: // 华北 - return new AppRouterState { + return new AppRouter { TTL = -1, 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", + PlayServer = $"{prefix}.play.lncld.net", Source = "fallback", }; } diff --git a/Common/AppRouter/AppRouterController.cs b/Common/AppRouter/AppRouterController.cs index 6a8ea2b..3500f20 100644 --- a/Common/AppRouter/AppRouterController.cs +++ b/Common/AppRouter/AppRouterController.cs @@ -6,11 +6,32 @@ using Newtonsoft.Json; namespace LeanCloud.Common { public class AppRouterController { - private AppRouterState currentState; + readonly string appId; - private readonly SemaphoreSlim locker = new SemaphoreSlim(1); + AppRouter currentState; - public async Task Get(string appId) { + readonly SemaphoreSlim locker = new SemaphoreSlim(1); + + public AppRouterController(string appId, string server) { + if (!IsInternationalApp(appId) && string.IsNullOrEmpty(server)) { + // 国内 App 必须设置域名 + throw new ArgumentException("You must init with your domain."); + } + if (!string.IsNullOrEmpty(server)) { + currentState = new AppRouter { + ApiServer = server, + EngineServer = server, + PushServer = server, + RTMServer = server, + StatsServer = server, + PlayServer = server, + TTL = -1 + }; + } + this.appId = appId; + } + + public async Task Get() { if (string.IsNullOrEmpty(appId)) { throw new ArgumentNullException(nameof(appId)); } @@ -23,9 +44,9 @@ namespace LeanCloud.Common { try { if (currentState == null) { try { - currentState = await QueryAsync(appId); + currentState = await QueryAsync(); } catch (Exception) { - currentState = AppRouterState.GetFallbackServers(appId); + currentState = AppRouter.GetFallbackServers(appId); } } return currentState; @@ -34,7 +55,7 @@ namespace LeanCloud.Common { } } - async Task QueryAsync(string appId) { + async Task QueryAsync() { HttpClient client = null; HttpRequestMessage request = null; HttpResponseMessage response = null; @@ -51,9 +72,9 @@ namespace LeanCloud.Common { response = await client.SendAsync(request); string content = await response.Content.ReadAsStringAsync(); - HttpUtils.PrintResponse(response); + HttpUtils.PrintResponse(response, content); - AppRouterState state = JsonConvert.DeserializeObject(content); + AppRouter state = JsonConvert.DeserializeObject(content); state.Source = "router"; return state; @@ -73,5 +94,13 @@ namespace LeanCloud.Common { public void Clear() { currentState = null; } + + static bool IsInternationalApp(string appId) { + if (appId.Length < 9) { + return false; + } + string suffix = appId.Substring(appId.Length - 9); + return suffix == "-MdYXbMMI"; + } } } diff --git a/Common/Common.csproj b/Common/Common.csproj index 74f5fd0..6c06788 100644 --- a/Common/Common.csproj +++ b/Common/Common.csproj @@ -2,6 +2,8 @@ netstandard2.0 + 0.1.0 + LeanCloud.Common @@ -10,5 +12,6 @@ + diff --git a/Common/Http/HttpUtils.cs b/Common/Http/HttpUtils.cs index 22f2d6e..7edaa6e 100644 --- a/Common/Http/HttpUtils.cs +++ b/Common/Http/HttpUtils.cs @@ -3,7 +3,7 @@ using System.Text; using System.Net.Http; namespace LeanCloud.Common { - public class HttpUtils { + public static class HttpUtils { public static void PrintRequest(HttpClient client, HttpRequestMessage request, string content = null) { if (client == null) { return; diff --git a/Common/Task/TaskExtensions.cs b/Common/Task/TaskExtensions.cs new file mode 100644 index 0000000..2137ec5 --- /dev/null +++ b/Common/Task/TaskExtensions.cs @@ -0,0 +1,148 @@ +// Copyright (c) 2015-present, Parse, LLC. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. An additional grant of patent rights can be found in the PATENTS file in the same directory. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.ExceptionServices; +using System.Threading.Tasks; + +namespace LeanCloud.Common { + /// + /// Provides helper methods that allow us to use terser code elsewhere. + /// + public static class TaskExtensions { + /// + /// Ensures a task (even null) is awaitable. + /// + /// + /// + /// + public static Task Safe(this Task task) { + return task ?? Task.FromResult(default); + } + + /// + /// Ensures a task (even null) is awaitable. + /// + /// + /// + public static Task Safe(this Task task) { + return task ?? Task.FromResult(null); + } + + public delegate void PartialAccessor(ref T arg); + + public static TValue GetOrDefault(this IDictionary self, + TKey key, + TValue defaultValue) { + if (self.TryGetValue(key, out TValue value)) { + return value; + } + return defaultValue; + } + + public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) { + return Equals(a, b) || + (a != null && b != null && + a.SequenceEqual(b)); + } + + public static Task OnSuccess(this Task task, + Func continuation) { + return task.ContinueWith(t => { + if (t.IsFaulted) { + var ex = t.Exception.Flatten(); + if (ex.InnerExceptions.Count == 1) { + ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); + } else { + ExceptionDispatchInfo.Capture(ex).Throw(); + } + // Unreachable + return Task.FromResult(default(TResult)); + } else if (t.IsCanceled) { + var tcs = new TaskCompletionSource(); + tcs.SetCanceled(); + return tcs.Task; + } else { + return Task.FromResult(continuation(t)); + } + }).Unwrap(); + } + + public static Task OnSuccess(this Task task, + Func, TResult> continuation) { + return ((Task)task).OnSuccess(t => continuation((Task)t)); + } + + public static Task OnSuccess(this Task task, Action> continuation) { + return task.OnSuccess((Func, object>)(t => { + continuation(t); + return null; + })); + } + + public static Task OnSuccess(this Task task, Action continuation) { + return task.OnSuccess((Func)(t => { + continuation(t); + return null; + })); + } + + // TaskScheduler + public static Task OnSuccess(this Task task, + Func continuation, TaskScheduler scheduler) { + return task.ContinueWith(t => { + if (t.IsFaulted) { + var ex = t.Exception.Flatten(); + if (ex.InnerExceptions.Count == 1) { + ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); + } else { + ExceptionDispatchInfo.Capture(ex).Throw(); + } + // Unreachable + return Task.FromResult(default(TResult)); + } else if (t.IsCanceled) { + var tcs = new TaskCompletionSource(); + tcs.SetCanceled(); + return tcs.Task; + } else { + return Task.FromResult(continuation(t)); + } + }, scheduler).Unwrap(); + } + + public static Task OnSuccess(this Task task, + Func, TResult> continuation, TaskScheduler scheduler) { + return ((Task)task).OnSuccess(t => continuation((Task)t), scheduler); + } + + public static Task OnSuccess(this Task task, + Action> continuation, TaskScheduler scheduler) { + return task.OnSuccess((Func, object>)(t => { + continuation(t); + return null; + }), scheduler); + } + + public static Task OnSuccess(this Task task, + Action continuation, TaskScheduler scheduler) { + return task.OnSuccess((Func)(t => { + continuation(t); + return null; + }), scheduler); + } + + public static Task WhileAsync(Func> predicate, Func body) { + Func iterate = null; + iterate = () => { + return predicate().OnSuccess(t => { + if (!t.Result) { + return Task.FromResult(0); + } + return body().OnSuccess(_ => iterate()).Unwrap(); + }).Unwrap(); + }; + return iterate(); + } + } +}