NewLife/NewLife.Remoting

代码整理
大石头 编写于 2024-05-05 22:03:26
共计: 修改9个文件,增加144行、删除20行。
修改 +110 -11
修改 +9 -0
修改 +9 -0
修改 +3 -3
修改 +6 -0
修改 +2 -1
修改 +3 -3
修改 +1 -1
修改 +1 -1
修改 +110 -11
diff --git a/NewLife.Remoting/Clients/ClientBase.cs b/NewLife.Remoting/Clients/ClientBase.cs
index b65266e..aaf0ff2 100644
--- a/NewLife.Remoting/Clients/ClientBase.cs
+++ b/NewLife.Remoting/Clients/ClientBase.cs
@@ -5,6 +5,7 @@ using System.Net.NetworkInformation;
 using System.Reflection;
 using NewLife.Caching;
 using NewLife.Log;
+using NewLife.Reflection;
 using NewLife.Remoting.Models;
 using NewLife.Remoting.Services;
 using NewLife.Security;
@@ -36,7 +37,7 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
     public Int32 Delay { get; set; }
 
     /// <summary>最大失败数。超过该数时,新的数据将被抛弃,默认120</summary>
-    public Int32 MaxFails { get; set; } = 120;
+    public Int32 MaxFails { get; set; } = 1 * 24 * 60;
 
     private readonly ConcurrentDictionary<String, Delegate> _commands = new(StringComparer.OrdinalIgnoreCase);
     /// <summary>命令集合</summary>
@@ -118,6 +119,10 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
         }
     }
 
+    /// <summary>设置令牌。派生类可重定义逻辑</summary>
+    /// <param name="token"></param>
+    protected virtual void SetToken(String? token) { }
+
     /// <summary>获取相对于服务器的当前时间,避免两端时间差</summary>
     /// <returns></returns>
     public DateTime GetNow() => DateTime.Now.Add(_span);
@@ -134,6 +139,8 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
         {
             var request = BuildLoginRequest();
 
+            // 登录前清空令牌,避免服务端使用上一次信息
+            SetToken(null);
             Logined = false;
 
             var rs = await LoginAsync(request);
@@ -148,9 +155,11 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
 
             FixTime(rs.Time, rs.Time);
 
+            // 登录后设置用于用户认证的token
+            SetToken(rs.Token);
             Logined = true;
 
-            OnLogined?.Invoke(this, new LoginEventArgs { Request = request, Response = rs });
+            OnLogined?.Invoke(this, new(request, rs));
 
             StartTimer();
 
@@ -189,10 +198,13 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
     /// <returns></returns>
     public virtual LoginRequest BuildLoginRequest()
     {
+        var asm = AssemblyX.Entry ?? AssemblyX.Create(Assembly.GetExecutingAssembly());
         var info = new LoginRequest
         {
             Code = Code,
             Secret = Secret.IsNullOrEmpty() ? null : PasswordProvider.Hash(Secret),
+            ClientId = $"{NetHelper.MyIP()}@{Process.GetCurrentProcess().Id}",
+            Version = asm?.FileVersion,
         };
 
         return info;
@@ -212,6 +224,9 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
         {
             var rs = await LogoutAsync(reason);
 
+            // 更新令牌
+            SetToken(rs?.Token);
+
             StopTimer();
 
             Logined = false;
@@ -248,13 +263,48 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
         {
             var request = BuildPingRequest();
 
-            var rs = await PingAsync(request);
-            if (rs != null)
+            // 如果网络不可用,直接保存到队列
+            if (!NetworkInterface.GetIsNetworkAvailable())
             {
-                // 由服务器改变采样频率
-                if (rs.Period > 0 && _timer != null) _timer.Period = rs.Period * 1000;
+                if (_fails.Count < MaxFails) _fails.Enqueue(request);
+                return null;
+            }
 
-                FixTime(rs.Time, rs.ServerTime);
+            PingResponse? rs = null;
+            try
+            {
+                rs = await PingAsync(request);
+                if (rs != null)
+                {
+                    // 由服务器改变采样频率
+                    if (rs.Period > 0 && _timer != null) _timer.Period = rs.Period * 1000;
+
+                    FixTime(rs.Time, rs.ServerTime);
+
+                    // 更新令牌。即将过期时,服务端会返回新令牌
+                    if (!rs.Token.IsNullOrEmpty()) SetToken(rs.Token);
+
+                    // 推队列
+                    if (rs.Commands != null && rs.Commands.Length > 0)
+                    {
+                        foreach (var model in rs.Commands)
+                        {
+                            await ReceiveCommand(model);
+                        }
+                    }
+                }
+            }
+            catch
+            {
+                if (_fails.Count < MaxFails) _fails.Enqueue(request);
+
+                throw;
+            }
+
+            // 上报正常,处理历史,失败则丢弃
+            while (_fails.TryDequeue(out var info))
+            {
+                await PingAsync(info);
             }
 
             return rs;
@@ -264,7 +314,7 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
             span?.SetError(ex, null);
 
             var ex2 = ex.GetTrue();
-            if (ex2 is ApiException aex && (aex.Code == 402 || aex.Code == 403))
+            if (ex2 is ApiException aex && (aex.Code == ApiCode.Unauthorized || aex.Code == ApiCode.Forbidden))
             {
                 WriteLog("重新登录");
                 await Login();
@@ -311,9 +361,9 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
             {
                 if (_timer == null)
                 {
-                    _timer = new TimerX(OnPing, null, 3_000, 60_000, "Device") { Async = true };
-                    _timerUpgrade = new TimerX(s => Upgrade(), null, 5_000, 600_000, "Device") { Async = true };
-                    _eventTimer = new TimerX(DoPostEvent, null, 3_000, 60_000, "Device") { Async = true };
+                    _timer = new TimerX(OnPing, null, 3_000, 60_000, "Client") { Async = true };
+                    _timerUpgrade = new TimerX(s => Upgrade(), null, 5_000, 600_000, "Client") { Async = true };
+                    _eventTimer = new TimerX(DoPostEvent, null, 3_000, 60_000, "Client") { Async = true };
                 }
             }
         }
@@ -335,6 +385,55 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider, 
     /// <returns></returns>
     protected virtual Task OnPing(Object state) => Ping();
 
+    async Task ReceiveCommand(CommandModel model)
+    {
+        if (model == null) return;
+
+        // 去重,避免命令被重复执行
+        if (!_cache.Add($"cmd:{model.Id}", model, 3600)) return;
+
+        // 埋点,建立调用链
+        using var span = Tracer?.NewSpan("cmd:" + model.Command, model);
+        if (!model.TraceId.IsNullOrEmpty()) span?.Detach(model.TraceId);
+        try
+        {
+            //todo 有效期判断可能有隐患,现在只是假设服务器和客户端在同一个时区,如果不同,可能会出现问题
+            var now = GetNow();
+            XTrace.WriteLine("Got Command: {0}", model.ToJson());
+            if (model.Expire.Year < 2000 || model.Expire > now)
+            {
+                // 延迟执行
+                var ts = model.StartTime - now;
+                if (ts.TotalMilliseconds > 0)
+                {
+                    TimerX.Delay(s =>
+                    {
+                        _ = OnReceiveCommand(model);
+                    }, (Int32)ts.TotalMilliseconds);
+
+                    var reply = new CommandReplyModel
+                    {
+                        Id = model.Id,
+                        Status = CommandStatus.处理中,
+                        Data = $"已安排计划执行 {model.StartTime.ToFullString()}"
+                    };
+                    await CommandReply(reply);
+                }
+                else
+                    await OnReceiveCommand(model);
+            }
+            else
+            {
+                var reply = new CommandReplyModel { Id = model.Id, Status = CommandStatus.取消 };
+                await CommandReply(reply);
+            }
+        }
+        catch (Exception ex)
+        {
+            span?.SetError(ex, null);
+        }
+    }
+
     /// <summary>触发收到命令的动作</summary>
     /// <param name="model"></param>
     protected virtual async Task OnReceiveCommand(CommandModel model)
修改 +9 -0
diff --git a/NewLife.Remoting/Clients/HttpClientBase.cs b/NewLife.Remoting/Clients/HttpClientBase.cs
index 993818e..2a1736c 100644
--- a/NewLife.Remoting/Clients/HttpClientBase.cs
+++ b/NewLife.Remoting/Clients/HttpClientBase.cs
@@ -75,6 +75,15 @@ public class HttpClientBase : ClientBase
 
         protected override Service GetService() => Current = base.GetService();
     }
+
+    /// <summary>设置令牌。派生类可重定义逻辑</summary>
+    /// <param name="token"></param>
+    protected override void SetToken(String? token)
+    {
+        base.SetToken(token);
+
+        if (_client != null) _client.Token = token;
+    }
     #endregion
 
     #region 登录
修改 +9 -0
diff --git a/NewLife.Remoting/Clients/RpcClientBase.cs b/NewLife.Remoting/Clients/RpcClientBase.cs
index fb17943..a94432f 100644
--- a/NewLife.Remoting/Clients/RpcClientBase.cs
+++ b/NewLife.Remoting/Clients/RpcClientBase.cs
@@ -51,6 +51,15 @@ public class RpcClientBase : ClientBase
 
         protected override async Task<Object?> OnLoginAsync(ISocketClient client, Boolean force) => await InvokeWithClientAsync<Object>(client, Client.Prefix + "/Login", Client.BuildLoginRequest());
     }
+
+    /// <summary>设置令牌。派生类可重定义逻辑</summary>
+    /// <param name="token"></param>
+    protected override void SetToken(String? token)
+    {
+        base.SetToken(token);
+
+        if (_client != null) _client.Token = token;
+    }
     #endregion
 
     #region 登录
修改 +3 -3
diff --git a/NewLife.Remoting/Models/LoginEventArgs.cs b/NewLife.Remoting/Models/LoginEventArgs.cs
index 38696d5..f6433bf 100644
--- a/NewLife.Remoting/Models/LoginEventArgs.cs
+++ b/NewLife.Remoting/Models/LoginEventArgs.cs
@@ -1,11 +1,11 @@
 namespace NewLife.Remoting.Models;
 
 /// <summary>登录事件参数</summary>
-public class LoginEventArgs : EventArgs
+public class LoginEventArgs(LoginRequest? request, LoginResponse? response) : EventArgs
 {
     /// <summary>请求</summary>
-    public LoginRequest? Request { get; set; }
+    public LoginRequest? Request { get; set; } = request;
 
     /// <summary>响应</summary>
-    public LoginResponse? Response { get; set; }
+    public LoginResponse? Response { get; set; } = response;
 }
修改 +6 -0
diff --git a/NewLife.Remoting/Models/LoginRequest.cs b/NewLife.Remoting/Models/LoginRequest.cs
index 1581200..03b0649 100644
--- a/NewLife.Remoting/Models/LoginRequest.cs
+++ b/NewLife.Remoting/Models/LoginRequest.cs
@@ -9,5 +9,11 @@ public class LoginRequest
 
     /// <summary>密钥</summary>
     public String? Secret { get; set; }
+
+    /// <summary>实例。应用可能多实例部署,ip@proccessid</summary>
+    public String? ClientId { get; set; }
+
+    /// <summary>版本</summary>
+    public String? Version { get; set; }
     #endregion
 }
修改 +2 -1
diff --git a/NewLife.Remoting/NewLife.Remoting.csproj b/NewLife.Remoting/NewLife.Remoting.csproj
index c13e62f..5827e3a 100644
--- a/NewLife.Remoting/NewLife.Remoting.csproj
+++ b/NewLife.Remoting/NewLife.Remoting.csproj
@@ -38,6 +38,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <Compile Remove="Clients\WsClientBase.cs" />
     <Compile Remove="IpHelper.cs" />
   </ItemGroup>
 
@@ -49,7 +50,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
+    <PackageReference Include="NewLife.Core" Version="10.10.2024.501" />
   </ItemGroup>
 
   <ItemGroup>
修改 +3 -3
diff --git a/Samples/Zero.RpcServer/Zero.RpcServer.csproj b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
index d1ccd59..b6ff9b9 100644
--- a/Samples/Zero.RpcServer/Zero.RpcServer.csproj
+++ b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
@@ -20,9 +20,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.Redis" Version="5.6.2024.101" />
-    <PackageReference Include="NewLife.Stardust" Version="2.9.2024.101" />
-    <PackageReference Include="NewLife.XCode" Version="11.11.2024.402" />
+    <PackageReference Include="NewLife.Redis" Version="5.6.2024.402" />
+    <PackageReference Include="NewLife.Stardust" Version="2.9.2024.402" />
+    <PackageReference Include="NewLife.XCode" Version="11.12.2024.503" />
   </ItemGroup>
 
   <ItemGroup>
修改 +1 -1
diff --git a/Test/Test.csproj b/Test/Test.csproj
index 4f021c1..a018cd7 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -10,7 +10,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
+    <PackageReference Include="NewLife.Core" Version="10.10.2024.501" />
   </ItemGroup>
 
   <ItemGroup>
修改 +1 -1
diff --git a/XUnitTest/XUnitTest.csproj b/XUnitTest/XUnitTest.csproj
index a09bf63..debf0aa 100644
--- a/XUnitTest/XUnitTest.csproj
+++ b/XUnitTest/XUnitTest.csproj
@@ -12,7 +12,7 @@
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
     <PackageReference Include="Moq" Version="4.20.70" />
-    <PackageReference Include="NewLife.Core" Version="10.10.2024.427-beta2349" />
+    <PackageReference Include="NewLife.Core" Version="10.10.2024.501" />
     <PackageReference Include="NewLife.UnitTest" Version="1.0.2023.1204" />
     <PackageReference Include="xunit" Version="2.8.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">