Skip to content

Commit

Permalink
客户端全部使用接口来替代模型类,支持对象容器的依赖注入
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Jun 10, 2024
1 parent b826dac commit 9e93c31
Show file tree
Hide file tree
Showing 20 changed files with 307 additions and 139 deletions.
127 changes: 87 additions & 40 deletions NewLife.Remoting/Clients/ClientBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using NewLife.Caching;
using NewLife.Log;
using NewLife.Model;
using NewLife.Reflection;
using NewLife.Remoting.Models;
using NewLife.Security;
Expand All @@ -23,7 +24,10 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider,
public String? Secret { get; set; }

/// <summary>密码提供者</summary>
public IPasswordProvider PasswordProvider { get; set; } = new SaltPasswordProvider { Algorithm = "md5" };
public IPasswordProvider? PasswordProvider { get; set; }

/// <summary>服务提供者</summary>
public IServiceProvider? ServiceProvider { get; set; }

/// <summary>是否已登录</summary>
public Boolean Logined { get; set; }
Expand Down Expand Up @@ -51,19 +55,22 @@ public abstract class ClientBase : DisposeBase, ICommandClient, IEventProvider,
public IClientSetting? Setting { get; set; }

/// <summary>协议版本</summary>
private String _version;
private readonly static String _version;
private TimeSpan _span;
private readonly ConcurrentQueue<PingRequest> _fails = new();
private readonly ConcurrentQueue<IPingRequest> _fails = new();
private readonly ICache _cache = new MemoryCache();
#endregion

#region 构造
/// <summary>实例化</summary>
public ClientBase()
static ClientBase()
{
_version = Assembly.GetExecutingAssembly().GetName().Version + "";
var asm = AssemblyX.Entry ?? AssemblyX.Create(Assembly.GetExecutingAssembly());
_version = asm?.FileVersion + "";
}

/// <summary>实例化</summary>
public ClientBase() { }

/// <summary>通过客户端设置实例化</summary>
/// <param name="setting"></param>
public ClientBase(IClientSetting setting) : this()
Expand All @@ -89,6 +96,35 @@ protected override void Dispose(Boolean disposing)
#endregion

#region 方法
private Int32 _inited;
/// <summary>初始化</summary>
private void Init()
{
if (Interlocked.CompareExchange(ref _inited, 1, 0) != 0) return;

OnInit();
}

/// <summary>初始化</summary>
protected virtual void OnInit()
{
var provider = ServiceProvider ??= ObjectContainer.Provider;

// 找到容器,注册默认的模型实现,供后续InvokeAsync时自动创建正确的模型对象
var container = provider?.GetService<IObjectContainer>() ?? ObjectContainer.Current;
if (container != null)
{
container.TryAddTransient<ILoginRequest, LoginRequest>();
container.TryAddTransient<ILoginResponse, LoginResponse>();
container.TryAddTransient<ILogoutResponse, LogoutResponse>();
container.TryAddTransient<IPingRequest, PingRequest>();
container.TryAddTransient<IPingResponse, PingResponse>();
container.TryAddTransient<IUpgradeInfo, UpgradeInfo>();
}

PasswordProvider ??= GetService<IPasswordProvider>() ?? new SaltPasswordProvider { Algorithm = "md5" };
}

/// <summary>异步调用</summary>
/// <param name="action">动作</param>
/// <param name="args">参数</param>
Expand Down Expand Up @@ -144,8 +180,10 @@ protected virtual void SetToken(String? token) { }
#region 登录
/// <summary>登录</summary>
/// <returns></returns>
public virtual async Task<LoginResponse?> Login()
public virtual async Task<ILoginResponse?> Login()
{
Init();

using var span = Tracer?.NewSpan(nameof(Login), Code);
WriteLog("登录:{0}", Code);
try
Expand Down Expand Up @@ -217,24 +255,23 @@ protected void FixTime(Int64 startTime, Int64 serverTime)

/// <summary>获取登录信息</summary>
/// <returns></returns>
public virtual LoginRequest BuildLoginRequest()
public virtual ILoginRequest 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,
};
Init();

var info = GetService<ILoginRequest>() ?? new LoginRequest();
info.Code = Code;
info.Secret = Secret;
info.ClientId = $"{NetHelper.MyIP()}@{Process.GetCurrentProcess().Id}";
info.Version = _version;

return info;
}

/// <summary>注销</summary>
/// <param name="reason"></param>
/// <returns></returns>
public virtual async Task<LogoutResponse?> Logout(String reason)
public virtual async Task<ILogoutResponse?> Logout(String reason)
{
if (!Logined) return null;

Expand Down Expand Up @@ -266,18 +303,20 @@ public virtual LoginRequest BuildLoginRequest()
/// <summary>登录</summary>
/// <param name="request">登录信息</param>
/// <returns></returns>
protected virtual Task<LoginResponse?> LoginAsync(LoginRequest request) => InvokeAsync<LoginResponse>(Prefix + "Login", request);
protected virtual Task<ILoginResponse?> LoginAsync(ILoginRequest request) => InvokeAsync<ILoginResponse>(Prefix + "Login", request);

/// <summary>注销</summary>
/// <returns></returns>
protected virtual Task<LogoutResponse?> LogoutAsync(String reason) => InvokeAsync<LogoutResponse>(Prefix + "Logout", new { reason });
protected virtual Task<ILogoutResponse?> LogoutAsync(String reason) => InvokeAsync<ILogoutResponse>(Prefix + "Logout", new { reason });
#endregion

#region 心跳
/// <summary>心跳</summary>
/// <returns></returns>
public virtual async Task<PingResponse?> Ping()
public virtual async Task<IPingResponse?> Ping()
{
Init();

if (Tracer != null) DefaultSpan.Current = null;
using var span = Tracer?.NewSpan(nameof(Ping));
try
Expand All @@ -291,7 +330,7 @@ public virtual LoginRequest BuildLoginRequest()
return null;
}

PingResponse? rs = null;
IPingResponse? rs = null;
try
{
rs = await PingAsync(request);
Expand Down Expand Up @@ -350,14 +389,15 @@ public virtual LoginRequest BuildLoginRequest()
}

/// <summary>获取心跳信息</summary>
public virtual PingRequest BuildPingRequest()
public virtual IPingRequest BuildPingRequest()
{
var request = new PingRequest
{
Uptime = Environment.TickCount / 1000,
Time = DateTime.UtcNow.ToLong(),
Delay = Delay,
};
Init();

var request = GetService<IPingRequest>() ?? new PingRequest();
request.Uptime = Environment.TickCount / 1000;
request.Time = DateTime.UtcNow.ToLong();
request.Delay = Delay;

// 开始时间 Environment.TickCount 很容易溢出,导致开机24天后变成负数。
// 后来在 netcore3.0 增加了Environment.TickCount64
// 现在借助 Stopwatch 来解决
Expand All @@ -369,7 +409,7 @@ public virtual PingRequest BuildPingRequest()
/// <summary>心跳</summary>
/// <param name="request"></param>
/// <returns></returns>
protected virtual Task<PingResponse?> PingAsync(PingRequest request) => InvokeAsync<PingResponse>(Prefix + "Ping", request);
protected virtual Task<IPingResponse?> PingAsync(IPingRequest request) => InvokeAsync<IPingResponse>(Prefix + "Ping", request);

private TimerX? _timer;
private TimerX? _timerUpgrade;
Expand Down Expand Up @@ -568,7 +608,7 @@ public virtual Boolean WriteEvent(String type, String name, String? remark)
private String? _lastVersion;
/// <summary>获取更新信息</summary>
/// <returns></returns>
public async Task<UpgradeInfo?> Upgrade()
public async Task<IUpgradeInfo?> Upgrade()
{
using var span = Tracer?.NewSpan(nameof(Upgrade));
WriteLog("检查更新");
Expand All @@ -577,24 +617,24 @@ public virtual Boolean WriteEvent(String type, String name, String? remark)
var ug = new Upgrade { Log = XTrace.Log };
ug.DeleteBackup(".");

var ur = await UpgradeAsync();
if (ur != null && ur.Version != _lastVersion)
var info = await UpgradeAsync();
if (info != null && info.Version != _lastVersion)
{
WriteLog("发现更新:{0}", ur.ToJson(true));
WriteLog("发现更新:{0}", info.ToJson(true));

ug.Url = ur.Source;
ug.Url = info.Source;
await ug.Download();

// 检查文件完整性
if (ur.FileHash.IsNullOrEmpty() || ug.CheckFileHash(ur.FileHash))
if (info.FileHash.IsNullOrEmpty() || ug.CheckFileHash(info.FileHash))
{
// 执行更新,解压缩覆盖文件
var rs = ug.Update();
if (rs && !ur.Executor.IsNullOrEmpty()) ug.Run(ur.Executor);
_lastVersion = ur.Version;
if (rs && !info.Executor.IsNullOrEmpty()) ug.Run(info.Executor);
_lastVersion = info.Version;

// 强制更新时,马上重启
if (rs && ur.Force)
if (rs && info.Force)
{
// 重新拉起进程
rs = ug.Run("dotnet", "IoTClient.dll -upgrade");
Expand All @@ -604,12 +644,19 @@ public virtual Boolean WriteEvent(String type, String name, String? remark)
}
}

return ur;
return info;
}

/// <summary>更新</summary>
/// <returns></returns>
protected virtual Task<UpgradeInfo?> UpgradeAsync() => InvokeAsync<UpgradeInfo>(Prefix + "Upgrade");
protected virtual Task<IUpgradeInfo?> UpgradeAsync() => InvokeAsync<IUpgradeInfo>(Prefix + "Upgrade");
#endregion

#region 辅助
/// <summary>从服务提供者(对象容器)创建模型对象</summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual T? GetService<T>() where T : class => ServiceProvider?.GetService<T>() ?? ObjectContainer.Current.Resolve<T>();
#endregion

#region 日志
Expand Down
6 changes: 3 additions & 3 deletions NewLife.Remoting/Clients/HttpClientBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ protected override void SetToken(String? token)
/// <summary>登录</summary>
/// <param name="request">登录信息</param>
/// <returns></returns>
protected override async Task<LoginResponse?> LoginAsync(LoginRequest request)
protected override async Task<ILoginResponse?> LoginAsync(ILoginRequest request)
{
// 登录前清空令牌,避免服务端使用上一次信息
_client.Token = null;
Expand All @@ -105,7 +105,7 @@ protected override void SetToken(String? token)

/// <summary>注销</summary>
/// <returns></returns>
protected override async Task<LogoutResponse?> LogoutAsync(String reason)
protected override async Task<ILogoutResponse?> LogoutAsync(String reason)
{
var rs = await base.LogoutAsync(reason);

Expand All @@ -129,7 +129,7 @@ protected override async Task OnPing(Object state)
var rs = await Ping();

// 令牌
if (rs is PingResponse pr && !pr.Token.IsNullOrEmpty())
if (rs is IPingResponse pr && !pr.Token.IsNullOrEmpty())
_client.Token = pr.Token;

#if NETCOREAPP
Expand Down
8 changes: 4 additions & 4 deletions NewLife.Remoting/Clients/RpcClientBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected override void SetToken(String? token)
#region 登录
/// <summary>登录</summary>
/// <returns></returns>
public override async Task<LoginResponse?> Login()
public override async Task<ILoginResponse?> Login()
{
_client.Token = null;

Expand All @@ -78,7 +78,7 @@ protected override void SetToken(String? token)
/// <summary>登录</summary>
/// <param name="request">登录信息</param>
/// <returns></returns>
protected override async Task<LoginResponse?> LoginAsync(LoginRequest request)
protected override async Task<ILoginResponse?> LoginAsync(ILoginRequest request)
{
// 登录前清空令牌,避免服务端使用上一次信息
_client.Token = null;
Expand All @@ -93,7 +93,7 @@ protected override void SetToken(String? token)

/// <summary>注销</summary>
/// <returns></returns>
protected override async Task<LogoutResponse?> LogoutAsync(String reason)
protected override async Task<ILogoutResponse?> LogoutAsync(String reason)
{
var rs = await base.LogoutAsync(reason);

Expand All @@ -107,7 +107,7 @@ protected override void SetToken(String? token)
#region 心跳
/// <summary>心跳</summary>
/// <returns></returns>
public override async Task<PingResponse?> Ping()
public override async Task<IPingResponse?> Ping()
{
var rs = await base.Ping();

Expand Down
6 changes: 3 additions & 3 deletions NewLife.Remoting/Models/LoginEventArgs.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
namespace NewLife.Remoting.Models;

/// <summary>登录事件参数</summary>
public class LoginEventArgs(LoginRequest? request, LoginResponse? response) : EventArgs
public class LoginEventArgs(ILoginRequest? request, ILoginResponse? response) : EventArgs
{
/// <summary>请求</summary>
public LoginRequest? Request { get; set; } = request;
public ILoginRequest? Request { get; set; } = request;

/// <summary>响应</summary>
public LoginResponse? Response { get; set; } = response;
public ILoginResponse? Response { get; set; } = response;
}
18 changes: 17 additions & 1 deletion NewLife.Remoting/Models/LoginRequest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
namespace NewLife.Remoting.Models;

/// <summary>登录请求</summary>
public class LoginRequest
public interface ILoginRequest
{
/// <summary>编码</summary>
String? Code { get; set; }

/// <summary>密钥</summary>
String? Secret { get; set; }

/// <summary>实例。应用可能多实例部署,ip@proccessid</summary>
String? ClientId { get; set; }

/// <summary>版本</summary>
String? Version { get; set; }
}

/// <summary>登录请求</summary>
public class LoginRequest : ILoginRequest
{
#region 属性
/// <summary>编码</summary>
Expand Down
18 changes: 17 additions & 1 deletion NewLife.Remoting/Models/LoginResponse.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
namespace NewLife.Remoting.Models;

/// <summary>登录响应</summary>
public class LoginResponse
public interface ILoginResponse
{
/// <summary>编码。平台下发新编码</summary>
String? Code { get; set; }

/// <summary>密钥。平台下发新密钥</summary>
String? Secret { get; set; }

/// <summary>令牌</summary>
String? Token { get; set; }

/// <summary>服务器时间。Unix毫秒(UTC)</summary>
Int64 Time { get; set; }
}

/// <summary>登录响应</summary>
public class LoginResponse : ILoginResponse
{
#region 属性
/// <summary>编码。平台下发新编码</summary>
Expand Down
Loading

0 comments on commit 9e93c31

Please sign in to comment.