使用Remoting架构模型
This commit is contained in:
parent
15717165a7
commit
28225811ec
|
@ -32,7 +32,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.422-beta0942" />
|
||||
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.620-beta1617" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -8,6 +8,7 @@ using NewLife.Caching;
|
|||
using NewLife.Log;
|
||||
using NewLife.Net;
|
||||
using NewLife.Remoting;
|
||||
using NewLife.Remoting.Models;
|
||||
using NewLife.Threading;
|
||||
|
||||
namespace AntJob.Server;
|
||||
|
@ -95,7 +96,7 @@ class AntService : IApi, IActionFilter
|
|||
[Api(nameof(Login))]
|
||||
public LoginResponse Login(LoginModel model)
|
||||
{
|
||||
if (model.User.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.User));
|
||||
if (model.Code.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.Code));
|
||||
|
||||
var (app, rs) = _appService.Login(model, _Net.Remote.Host);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using AntJob.Models;
|
|||
using NewLife;
|
||||
using NewLife.Log;
|
||||
using NewLife.Remoting;
|
||||
using NewLife.Remoting.Models;
|
||||
using NewLife.Security;
|
||||
using NewLife.Web;
|
||||
|
||||
|
@ -22,29 +23,29 @@ public class AppService
|
|||
/// <returns></returns>
|
||||
public (App, LoginResponse) Login(LoginModel model, String ip)
|
||||
{
|
||||
if (model.User.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.User));
|
||||
if (model.Code.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.Code));
|
||||
|
||||
_log.Info("[{0}]从[{1}]登录[{2}@{3}]", model.User, ip, model.Machine, model.ProcessId);
|
||||
_log.Info("[{0}]从[{1}]登录[{2}@{3}]", model.Code, ip, model.Machine, model.ProcessId);
|
||||
|
||||
// 找应用
|
||||
var autoReg = false;
|
||||
var app = App.FindByName(model.User);
|
||||
if (app == null || app.Secret.MD5() != model.Pass)
|
||||
var app = App.FindByName(model.Code);
|
||||
if (app == null || app.Secret.MD5() != model.Secret)
|
||||
{
|
||||
app = CheckApp(app, model.User, model.Pass, ip);
|
||||
if (app == null) throw new ArgumentOutOfRangeException(nameof(model.User));
|
||||
app = CheckApp(app, model.Code, model.Secret, ip);
|
||||
if (app == null) throw new ArgumentOutOfRangeException(nameof(model.Code));
|
||||
|
||||
autoReg = true;
|
||||
}
|
||||
|
||||
if (app == null) throw new Exception($"应用[{model.User}]不存在!");
|
||||
if (app == null) throw new Exception($"应用[{model.Code}]不存在!");
|
||||
if (!app.Enable) throw new Exception("已禁用!");
|
||||
|
||||
// 核对密码
|
||||
if (!autoReg && !app.Secret.IsNullOrEmpty())
|
||||
{
|
||||
var pass2 = app.Secret.MD5();
|
||||
if (model.Pass != pass2) throw new Exception("密码错误!");
|
||||
if (model.Secret != pass2) throw new Exception("密码错误!");
|
||||
}
|
||||
|
||||
// 版本和编译时间
|
||||
|
@ -60,9 +61,9 @@ public class AppService
|
|||
online.CompileTime = model.Compile;
|
||||
online.Save();
|
||||
|
||||
WriteHistory(app, autoReg ? "注册" : "登录", true, $"[{model.User}/{model.Pass}]在[{model.Machine}@{model.ProcessId}]登录[{app}]成功");
|
||||
WriteHistory(app, autoReg ? "注册" : "登录", true, $"[{model.Code}/{model.Secret}]在[{model.Machine}@{model.ProcessId}]登录[{app}]成功");
|
||||
|
||||
var rs = new LoginResponse { Name = app.Name, DisplayName = app.DisplayName };
|
||||
var rs = new LoginResponse { Name = app.Name };
|
||||
if (autoReg) rs.Secret = app.Secret;
|
||||
|
||||
return (app, rs);
|
||||
|
|
|
@ -14,6 +14,7 @@ using NewLife.Caching;
|
|||
using NewLife.Cube;
|
||||
using NewLife.Log;
|
||||
using NewLife.Remoting;
|
||||
using NewLife.Remoting.Models;
|
||||
using NewLife.Serialization;
|
||||
using NewLife.Web;
|
||||
using IActionFilter = Microsoft.AspNetCore.Mvc.Filters.IActionFilter;
|
||||
|
@ -101,7 +102,7 @@ public class AntJobController : ControllerBase, IActionFilter
|
|||
[HttpPost(nameof(Login))]
|
||||
public LoginResponse Login(LoginModel model)
|
||||
{
|
||||
if (model.User.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.User));
|
||||
if (model.Code.IsNullOrEmpty()) throw new ArgumentNullException(nameof(model.Code));
|
||||
|
||||
var (app, rs) = _appService.Login(model, UserHost);
|
||||
|
||||
|
@ -123,7 +124,7 @@ public class AntJobController : ControllerBase, IActionFilter
|
|||
// 密码模式
|
||||
if (model.grant_type == "password")
|
||||
{
|
||||
var (app, rs) = _appService.Login(new LoginModel { User = model.UserName, Pass = model.Password }, ip);
|
||||
var (app, rs) = _appService.Login(new LoginModel { Code = model.UserName, Secret = model.Password }, ip);
|
||||
|
||||
var tokenModel = _appService.IssueToken(app.Name, set);
|
||||
|
||||
|
|
|
@ -45,9 +45,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NewLife.Core" Version="10.10.2024.601" />
|
||||
<PackageReference Include="NewLife.Remoting" Version="2.8.2024.419-beta0703" />
|
||||
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.422-beta0942" />
|
||||
<PackageReference Include="NewLife.Core" Version="10.10.2024.620-beta1127" />
|
||||
<PackageReference Include="NewLife.Remoting" Version="3.0.2024.624-beta0109" />
|
||||
<PackageReference Include="NewLife.Stardust" Version="2.9.2024.620-beta1617" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
using System.Reflection;
|
||||
using NewLife;
|
||||
using NewLife.Configuration;
|
||||
using NewLife.Remoting.Clients;
|
||||
|
||||
namespace AntJob;
|
||||
|
||||
/// <summary>蚂蚁配置。主要用于网络型调度系统</summary>
|
||||
[Config("Ant")]
|
||||
public class AntSetting : Config<AntSetting>
|
||||
public class AntSetting : Config<AntSetting>, IClientSetting
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>调试开关。默认false</summary>
|
||||
|
@ -25,6 +26,8 @@ public class AntSetting : Config<AntSetting>
|
|||
/// <summary>应用密钥。</summary>
|
||||
[Description("应用密钥。")]
|
||||
public String Secret { get; set; }
|
||||
|
||||
String IClientSetting.Code { get => AppID; set => AppID = value; }
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
namespace AntJob.Models;
|
||||
using NewLife.Remoting.Models;
|
||||
|
||||
namespace AntJob.Models;
|
||||
|
||||
/// <summary>登录模型</summary>
|
||||
public class LoginModel
|
||||
public class LoginModel : LoginRequest
|
||||
{
|
||||
/// <summary>用户名</summary>
|
||||
public String User { get; set; }
|
||||
///// <summary>用户名</summary>
|
||||
//public String User { get; set; }
|
||||
|
||||
/// <summary>用户名</summary>
|
||||
public String Pass { get; set; }
|
||||
///// <summary>用户名</summary>
|
||||
//public String Pass { get; set; }
|
||||
|
||||
/// <summary>显示名</summary>
|
||||
public String DisplayName { get; set; }
|
||||
|
@ -18,22 +20,22 @@ public class LoginModel
|
|||
/// <summary>进程Id</summary>
|
||||
public Int32 ProcessId { get; set; }
|
||||
|
||||
/// <summary>版本</summary>
|
||||
public String Version { get; set; }
|
||||
///// <summary>版本</summary>
|
||||
//public String Version { get; set; }
|
||||
|
||||
/// <summary>编译时间</summary>
|
||||
public DateTime Compile { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>登录响应</summary>
|
||||
public class LoginResponse
|
||||
{
|
||||
/// <summary>名称</summary>
|
||||
public String Name { get; set; }
|
||||
///// <summary>登录响应</summary>
|
||||
//public class LoginResponse
|
||||
//{
|
||||
// /// <summary>名称</summary>
|
||||
// public String Name { get; set; }
|
||||
|
||||
/// <summary>密钥。仅注册时返回</summary>
|
||||
public String Secret { get; set; }
|
||||
// /// <summary>密钥。仅注册时返回</summary>
|
||||
// public String Secret { get; set; }
|
||||
|
||||
/// <summary>显示名</summary>
|
||||
public String DisplayName { get; set; }
|
||||
}
|
||||
// /// <summary>显示名</summary>
|
||||
// public String DisplayName { get; set; }
|
||||
//}
|
|
@ -4,124 +4,107 @@ using System.Reflection;
|
|||
using AntJob.Data;
|
||||
using AntJob.Models;
|
||||
using NewLife;
|
||||
using NewLife.Net;
|
||||
using NewLife.Model;
|
||||
using NewLife.Reflection;
|
||||
using NewLife.Remoting;
|
||||
using NewLife.Serialization;
|
||||
using NewLife.Remoting.Clients;
|
||||
using NewLife.Remoting.Models;
|
||||
|
||||
namespace AntJob.Providers;
|
||||
|
||||
/// <summary>蚂蚁客户端</summary>
|
||||
public class AntClient : ApiClient
|
||||
public class AntClient : ClientBase
|
||||
{
|
||||
private readonly AntSetting _setting;
|
||||
#region 属性
|
||||
/// <summary>用户名</summary>
|
||||
public String UserName { get; set; }
|
||||
#endregion
|
||||
|
||||
/// <summary>密码</summary>
|
||||
public String Password { get; set; }
|
||||
#region 构造
|
||||
/// <summary>实例化</summary>
|
||||
public AntClient() => Prefix = "Ant/";
|
||||
|
||||
/// <summary>是否已登录</summary>
|
||||
public Boolean Logined { get; set; }
|
||||
|
||||
/// <summary>最后一次登录成功后的消息</summary>
|
||||
public LoginResponse Info { get; private set; }
|
||||
/// <summary>实例化</summary>
|
||||
/// <param name="setting"></param>
|
||||
public AntClient(AntSetting setting) : base(setting)
|
||||
{
|
||||
_setting = setting;
|
||||
Prefix = _setting.Server.StartsWithIgnoreCase("http://", "https://") ? "AntJob/" : "Ant/";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 方法
|
||||
/// <summary>实例化</summary>
|
||||
public AntClient()
|
||||
/// <summary>初始化</summary>
|
||||
protected override void OnInit()
|
||||
{
|
||||
ShowError = true;
|
||||
}
|
||||
var provider = ServiceProvider ??= ObjectContainer.Provider;
|
||||
|
||||
/// <summary>实例化</summary>
|
||||
/// <param name="uri"></param>
|
||||
public AntClient(String uri) : this()
|
||||
{
|
||||
if (!uri.IsNullOrEmpty())
|
||||
// 找到容器,注册默认的模型实现,供后续InvokeAsync时自动创建正确的模型对象
|
||||
var container = ModelExtension.GetService<IObjectContainer>(provider) ?? ObjectContainer.Current;
|
||||
if (container != null)
|
||||
{
|
||||
var ss = uri.Split(",", ";");
|
||||
|
||||
Servers = ss;
|
||||
|
||||
var u = new Uri(ss[0]);
|
||||
var us = u.UserInfo.Split(":");
|
||||
if (us.Length > 0) UserName = us[0];
|
||||
if (us.Length > 1) Password = us[1];
|
||||
container.TryAddTransient<ILoginRequest, LoginModel>();
|
||||
//container.TryAddTransient<ILoginResponse, LoginResponse>();
|
||||
//container.TryAddTransient<ILogoutResponse, LogoutResponse>();
|
||||
//container.TryAddTransient<IPingRequest, PingInfo>();
|
||||
//container.TryAddTransient<IPingResponse, PingResponse>();
|
||||
//container.TryAddTransient<IUpgradeInfo, UpgradeInfo>();
|
||||
}
|
||||
|
||||
Prefix = _setting.Server.StartsWithIgnoreCase("http://", "https://") ? "AntJob/" : "Ant/";
|
||||
|
||||
base.OnInit();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 登录
|
||||
/// <summary>连接后自动登录</summary>
|
||||
/// <param name="client">客户端</param>
|
||||
/// <param name="force">强制登录</param>
|
||||
protected override async Task<Object> OnLoginAsync(ISocketClient client, Boolean force)
|
||||
/// <summary>创建登录请求</summary>
|
||||
/// <returns></returns>
|
||||
public override ILoginRequest BuildLoginRequest()
|
||||
{
|
||||
if (Logined && !force) return null;
|
||||
|
||||
var asmx = AssemblyX.Entry;
|
||||
var title = asmx?.Asm.GetCustomAttribute<AssemblyTitleAttribute>();
|
||||
var dis = asmx?.Asm.GetCustomAttribute<DisplayNameAttribute>();
|
||||
var des = asmx?.Asm.GetCustomAttribute<DescriptionAttribute>();
|
||||
var dname = title?.Title ?? dis?.DisplayName ?? des?.Description;
|
||||
|
||||
var arg = new LoginModel
|
||||
var request = base.BuildLoginRequest();
|
||||
if (request is LoginModel model)
|
||||
{
|
||||
User = UserName,
|
||||
Pass = Password.IsNullOrEmpty() ? null : Password.MD5(),
|
||||
DisplayName = dname,
|
||||
Machine = Environment.MachineName,
|
||||
ProcessId = Process.GetCurrentProcess().Id,
|
||||
Version = asmx.Version,
|
||||
Compile = asmx.Compile,
|
||||
};
|
||||
var asmx = AssemblyX.Entry;
|
||||
var title = asmx?.Asm.GetCustomAttribute<AssemblyTitleAttribute>();
|
||||
var dis = asmx?.Asm.GetCustomAttribute<DisplayNameAttribute>();
|
||||
var des = asmx?.Asm.GetCustomAttribute<DescriptionAttribute>();
|
||||
var dname = title?.Title ?? dis?.DisplayName ?? des?.Description;
|
||||
|
||||
var rs = await base.InvokeWithClientAsync<LoginResponse>(client, "Login", arg);
|
||||
|
||||
var set = AntSetting.Current;
|
||||
if (set.Debug) Log?.Info("登录{0}成功!{1}", client, rs.ToJson());
|
||||
|
||||
// 保存下发密钥
|
||||
if (!rs.Secret.IsNullOrEmpty())
|
||||
{
|
||||
set.Secret = rs.Secret;
|
||||
set.Save();
|
||||
model.DisplayName = dname;
|
||||
model.Machine = Environment.MachineName;
|
||||
model.ProcessId = Process.GetCurrentProcess().Id;
|
||||
model.Compile = asmx.Compile;
|
||||
}
|
||||
|
||||
Logined = true;
|
||||
|
||||
return Info = rs;
|
||||
return request;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 核心方法
|
||||
/// <summary>获取指定名称的作业</summary>
|
||||
/// <returns></returns>
|
||||
public IJob[] GetJobs() => Invoke<JobModel[]>(nameof(GetJobs));
|
||||
public IJob[] GetJobs() => InvokeAsync<JobModel[]>(nameof(GetJobs)).Result;
|
||||
|
||||
/// <summary>批量添加作业</summary>
|
||||
/// <param name="jobs"></param>
|
||||
/// <returns></returns>
|
||||
public String[] AddJobs(IJob[] jobs) => Invoke<String[]>(nameof(AddJobs), new { jobs });
|
||||
public String[] AddJobs(IJob[] jobs) => InvokeAsync<String[]>(nameof(AddJobs), new { jobs }).Result;
|
||||
|
||||
/// <summary>申请作业任务</summary>
|
||||
/// <param name="job">作业</param>
|
||||
/// <param name="topic">主题</param>
|
||||
/// <param name="count">要申请的任务个数</param>
|
||||
/// <returns></returns>
|
||||
public ITask[] Acquire(String job, String topic, Int32 count) => Invoke<TaskModel[]>(nameof(Acquire), new AcquireModel
|
||||
public ITask[] Acquire(String job, String topic, Int32 count) => InvokeAsync<TaskModel[]>(nameof(Acquire), new AcquireModel
|
||||
{
|
||||
Job = job,
|
||||
Topic = topic,
|
||||
Count = count,
|
||||
});
|
||||
}).Result;
|
||||
|
||||
/// <summary>生产消息</summary>
|
||||
/// <param name="model">模型</param>
|
||||
/// <returns></returns>
|
||||
public Int32 Produce(ProduceModel model) => Invoke<Int32>(nameof(Produce), model);
|
||||
public Int32 Produce(ProduceModel model) => InvokeAsync<Int32>(nameof(Produce), model).Result;
|
||||
|
||||
/// <summary>报告状态(进度、成功、错误)</summary>
|
||||
/// <param name="task"></param>
|
||||
|
@ -134,7 +117,7 @@ public class AntClient : ApiClient
|
|||
{
|
||||
try
|
||||
{
|
||||
return Invoke<Boolean>(nameof(Report), task);
|
||||
return InvokeAsync<Boolean>(nameof(Report), task).Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -147,6 +130,6 @@ public class AntClient : ApiClient
|
|||
|
||||
/// <summary>获取当前应用的所有在线实例</summary>
|
||||
/// <returns></returns>
|
||||
public IPeer[] GetPeers() => Invoke<PeerModel[]>(nameof(GetPeers));
|
||||
public IPeer[] GetPeers() => InvokeAsync<PeerModel[]>(nameof(GetPeers)).Result;
|
||||
#endregion
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System.Threading.Tasks;
|
||||
using AntJob.Data;
|
||||
using AntJob.Data;
|
||||
using AntJob.Handlers;
|
||||
using AntJob.Models;
|
||||
using NewLife;
|
||||
|
@ -8,21 +7,9 @@ using NewLife.Threading;
|
|||
namespace AntJob.Providers;
|
||||
|
||||
/// <summary>网络任务提供者</summary>
|
||||
public class NetworkJobProvider : JobProvider
|
||||
public class NetworkJobProvider(AntSetting setting) : JobProvider
|
||||
{
|
||||
#region 属性
|
||||
/// <summary>调试,打开编码日志</summary>
|
||||
public Boolean Debug { get; set; }
|
||||
|
||||
/// <summary>调度中心地址</summary>
|
||||
public String Server { get; set; }
|
||||
|
||||
/// <summary>应用编号</summary>
|
||||
public String AppId { get; set; }
|
||||
|
||||
/// <summary>应用密钥</summary>
|
||||
public String Secret { get; set; }
|
||||
|
||||
/// <summary>客户端</summary>
|
||||
public AntClient Ant { get; set; }
|
||||
|
||||
|
@ -37,6 +24,8 @@ public class NetworkJobProvider : JobProvider
|
|||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
Stop();
|
||||
|
||||
_timer.TryDispose();
|
||||
_timer = null;
|
||||
}
|
||||
|
@ -46,21 +35,15 @@ public class NetworkJobProvider : JobProvider
|
|||
/// <summary>开始</summary>
|
||||
public override void Start()
|
||||
{
|
||||
var svr = Server;
|
||||
|
||||
WriteLog("正在连接调度中心:{0}", svr);
|
||||
WriteLog("正在连接调度中心:{0}", setting.Server);
|
||||
|
||||
// 使用配置中心账号
|
||||
var ant = new AntClient(svr)
|
||||
var ant = new AntClient(setting)
|
||||
{
|
||||
UserName = AppId,
|
||||
Password = Secret,
|
||||
|
||||
Tracer = Tracer,
|
||||
Log = Log,
|
||||
};
|
||||
if (Debug) ant.EncoderLog = Log;
|
||||
ant.Open();
|
||||
ant.Login().Wait();
|
||||
|
||||
// 断开前一个连接
|
||||
Ant.TryDispose();
|
||||
|
@ -118,6 +101,8 @@ public class NetworkJobProvider : JobProvider
|
|||
/// <summary>停止</summary>
|
||||
public override void Stop()
|
||||
{
|
||||
Ant?.Logout(nameof(Stop)).Wait(1_000);
|
||||
|
||||
// 断开前一个连接
|
||||
Ant.TryDispose();
|
||||
Ant = null;
|
||||
|
@ -274,6 +259,7 @@ public class NetworkJobProvider : JobProvider
|
|||
|
||||
#region 邻居
|
||||
private TimerX _timer;
|
||||
|
||||
private void DoCheckPeer(Object state)
|
||||
{
|
||||
var ps = Ant?.GetPeers();
|
||||
|
|
|
@ -93,13 +93,8 @@ public class Scheduler : DisposeBase
|
|||
}
|
||||
else
|
||||
{
|
||||
var rpc = new NetworkJobProvider
|
||||
{
|
||||
Debug = debug,
|
||||
Server = server,
|
||||
AppId = appId,
|
||||
Secret = secret,
|
||||
};
|
||||
var set = AntSetting.Current;
|
||||
var rpc = new NetworkJobProvider(set);
|
||||
|
||||
Provider = rpc;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue