客户端服务端架构完成

This commit is contained in:
大石头 2019-01-01 16:40:42 +08:00
parent 45f148d464
commit c511a62585
14 changed files with 645 additions and 68 deletions

View File

@ -138,18 +138,6 @@ namespace Stardust.Data
return Find(_.Session == session);
}
/// <summary>根据应用、实例查找</summary>
/// <param name="appid">应用</param>
/// <param name="instance">实例</param>
/// <returns>实体对象</returns>
public static AppOnline FindByAppIDAndInstance(Int32 appid, String instance)
{
// 实体缓存
if (Meta.Session.Count < 1000) return Meta.Cache.Find(e => e.AppID == appid && e.Instance == instance);
return Find(_.AppID == appid & _.Instance == instance);
}
#endregion
#region

View File

@ -12,7 +12,8 @@ namespace Stardust.Data
[DataObject]
[Description("应用在线。一个应用有多个部署,每个在线会话对应一个服务地址")]
[BindIndex("IU_AppOnline_Session", true, "Session")]
[BindIndex("IU_AppOnline_AppID_Instance", true, "AppID,Instance")]
[BindIndex("IX_AppOnline_Client", false, "Client")]
[BindIndex("IX_AppOnline_AppID", false, "AppID")]
[BindTable("AppOnline", Description = "应用在线。一个应用有多个部署,每个在线会话对应一个服务地址", ConnName = "Registry", DbType = DatabaseType.None)]
public partial class AppOnline : IAppOnline
{
@ -33,22 +34,54 @@ namespace Stardust.Data
[BindColumn("AppID", "应用", "")]
public Int32 AppID { get { return _AppID; } set { if (OnPropertyChanging(__.AppID, value)) { _AppID = value; OnPropertyChanged(__.AppID); } } }
private String _Instance;
/// <summary>实例。IP@进程</summary>
[DisplayName("实例")]
[Description("实例。IP@进程")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Instance", "实例。IP@进程", "")]
public String Instance { get { return _Instance; } set { if (OnPropertyChanging(__.Instance, value)) { _Instance = value; OnPropertyChanged(__.Instance); } } }
private String _Session;
/// <summary>会话。tcp://ip:port</summary>
[DisplayName("会话")]
[Description("会话。tcp://ip:port")]
/// <summary>实例。IP加端口</summary>
[DisplayName("实例")]
[Description("实例。IP加端口")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Session", "会话。tcp://ip:port", "")]
[BindColumn("Session", "实例。IP加端口", "")]
public String Session { get { return _Session; } set { if (OnPropertyChanging(__.Session, value)) { _Session = value; OnPropertyChanged(__.Session); } } }
private String _Client;
/// <summary>客户端。IP加进程</summary>
[DisplayName("客户端")]
[Description("客户端。IP加进程")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Client", "客户端。IP加进程", "")]
public String Client { get { return _Client; } set { if (OnPropertyChanging(__.Client, value)) { _Client = value; OnPropertyChanged(__.Client); } } }
private String _Name;
/// <summary>名称。机器名称</summary>
[DisplayName("名称")]
[Description("名称。机器名称")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Name", "名称。机器名称", "", Master = true)]
public String Name { get { return _Name; } set { if (OnPropertyChanging(__.Name, value)) { _Name = value; OnPropertyChanged(__.Name); } } }
private String _Version;
/// <summary>版本。客户端</summary>
[DisplayName("版本")]
[Description("版本。客户端")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Version", "版本。客户端", "")]
public String Version { get { return _Version; } set { if (OnPropertyChanging(__.Version, value)) { _Version = value; OnPropertyChanged(__.Version); } } }
private DateTime _Compile;
/// <summary>编译时间。客户端</summary>
[DisplayName("编译时间")]
[Description("编译时间。客户端")]
[DataObjectField(false, false, true, 0)]
[BindColumn("Compile", "编译时间。客户端", "")]
public DateTime Compile { get { return _Compile; } set { if (OnPropertyChanging(__.Compile, value)) { _Compile = value; OnPropertyChanged(__.Compile); } } }
private String _Server;
/// <summary>服务端。客户端登录到哪个服务端IP加端口</summary>
[DisplayName("服务端")]
[Description("服务端。客户端登录到哪个服务端IP加端口")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Server", "服务端。客户端登录到哪个服务端IP加端口", "")]
public String Server { get { return _Server; } set { if (OnPropertyChanging(__.Server, value)) { _Server = value; OnPropertyChanged(__.Server); } } }
private String _Address;
/// <summary>服务地址。tcp://ip:port</summary>
[DisplayName("服务地址")]
@ -57,14 +90,6 @@ namespace Stardust.Data
[BindColumn("Address", "服务地址。tcp://ip:port", "")]
public String Address { get { return _Address; } set { if (OnPropertyChanging(__.Address, value)) { _Address = value; OnPropertyChanged(__.Address); } } }
private String _Version;
/// <summary>版本</summary>
[DisplayName("版本")]
[Description("版本")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Version", "版本", "")]
public String Version { get { return _Version; } set { if (OnPropertyChanging(__.Version, value)) { _Version = value; OnPropertyChanged(__.Version); } } }
private Int32 _Services;
/// <summary>服务数。该应用提供的服务数</summary>
[DisplayName("服务数")]
@ -126,10 +151,13 @@ namespace Stardust.Data
{
case __.ID : return _ID;
case __.AppID : return _AppID;
case __.Instance : return _Instance;
case __.Session : return _Session;
case __.Address : return _Address;
case __.Client : return _Client;
case __.Name : return _Name;
case __.Version : return _Version;
case __.Compile : return _Compile;
case __.Server : return _Server;
case __.Address : return _Address;
case __.Services : return _Services;
case __.Actions : return _Actions;
case __.Clients : return _Clients;
@ -145,10 +173,13 @@ namespace Stardust.Data
{
case __.ID : _ID = Convert.ToInt32(value); break;
case __.AppID : _AppID = Convert.ToInt32(value); break;
case __.Instance : _Instance = Convert.ToString(value); break;
case __.Session : _Session = Convert.ToString(value); break;
case __.Address : _Address = Convert.ToString(value); break;
case __.Client : _Client = Convert.ToString(value); break;
case __.Name : _Name = Convert.ToString(value); break;
case __.Version : _Version = Convert.ToString(value); break;
case __.Compile : _Compile = Convert.ToDateTime(value); break;
case __.Server : _Server = Convert.ToString(value); break;
case __.Address : _Address = Convert.ToString(value); break;
case __.Services : _Services = Convert.ToInt32(value); break;
case __.Actions : _Actions = Convert.ToString(value); break;
case __.Clients : _Clients = Convert.ToInt32(value); break;
@ -171,18 +202,27 @@ namespace Stardust.Data
/// <summary>应用</summary>
public static readonly Field AppID = FindByName(__.AppID);
/// <summary>实例。IP@进程</summary>
public static readonly Field Instance = FindByName(__.Instance);
/// <summary>会话。tcp://ip:port</summary>
/// <summary>实例。IP加端口</summary>
public static readonly Field Session = FindByName(__.Session);
/// <summary>客户端。IP加进程</summary>
public static readonly Field Client = FindByName(__.Client);
/// <summary>名称。机器名称</summary>
public static readonly Field Name = FindByName(__.Name);
/// <summary>版本。客户端</summary>
public static readonly Field Version = FindByName(__.Version);
/// <summary>编译时间。客户端</summary>
public static readonly Field Compile = FindByName(__.Compile);
/// <summary>服务端。客户端登录到哪个服务端IP加端口</summary>
public static readonly Field Server = FindByName(__.Server);
/// <summary>服务地址。tcp://ip:port</summary>
public static readonly Field Address = FindByName(__.Address);
/// <summary>版本</summary>
public static readonly Field Version = FindByName(__.Version);
/// <summary>服务数。该应用提供的服务数</summary>
public static readonly Field Services = FindByName(__.Services);
@ -213,18 +253,27 @@ namespace Stardust.Data
/// <summary>应用</summary>
public const String AppID = "AppID";
/// <summary>实例。IP@进程</summary>
public const String Instance = "Instance";
/// <summary>会话。tcp://ip:port</summary>
/// <summary>实例。IP加端口</summary>
public const String Session = "Session";
/// <summary>客户端。IP加进程</summary>
public const String Client = "Client";
/// <summary>名称。机器名称</summary>
public const String Name = "Name";
/// <summary>版本。客户端</summary>
public const String Version = "Version";
/// <summary>编译时间。客户端</summary>
public const String Compile = "Compile";
/// <summary>服务端。客户端登录到哪个服务端IP加端口</summary>
public const String Server = "Server";
/// <summary>服务地址。tcp://ip:port</summary>
public const String Address = "Address";
/// <summary>版本</summary>
public const String Version = "Version";
/// <summary>服务数。该应用提供的服务数</summary>
public const String Services = "Services";
@ -256,18 +305,27 @@ namespace Stardust.Data
/// <summary>应用</summary>
Int32 AppID { get; set; }
/// <summary>实例。IP@进程</summary>
String Instance { get; set; }
/// <summary>会话。tcp://ip:port</summary>
/// <summary>实例。IP加端口</summary>
String Session { get; set; }
/// <summary>客户端。IP加进程</summary>
String Client { get; set; }
/// <summary>名称。机器名称</summary>
String Name { get; set; }
/// <summary>版本。客户端</summary>
String Version { get; set; }
/// <summary>编译时间。客户端</summary>
DateTime Compile { get; set; }
/// <summary>服务端。客户端登录到哪个服务端IP加端口</summary>
String Server { get; set; }
/// <summary>服务地址。tcp://ip:port</summary>
String Address { get; set; }
/// <summary>版本</summary>
String Version { get; set; }
/// <summary>服务数。该应用提供的服务数</summary>
Int32 Services { get; set; }

View File

@ -48,6 +48,22 @@ namespace Stardust.Data
[BindColumn("Secret", "密钥", "")]
public String Secret { get { return _Secret; } set { if (OnPropertyChanging(__.Secret, value)) { _Secret = value; OnPropertyChanged(__.Secret); } } }
private String _Version;
/// <summary>版本</summary>
[DisplayName("版本")]
[Description("版本")]
[DataObjectField(false, false, true, 50)]
[BindColumn("Version", "版本", "")]
public String Version { get { return _Version; } set { if (OnPropertyChanging(__.Version, value)) { _Version = value; OnPropertyChanged(__.Version); } } }
private DateTime _Compile;
/// <summary>编译时间</summary>
[DisplayName("编译时间")]
[Description("编译时间")]
[DataObjectField(false, false, true, 0)]
[BindColumn("Compile", "编译时间", "")]
public DateTime Compile { get { return _Compile; } set { if (OnPropertyChanging(__.Compile, value)) { _Compile = value; OnPropertyChanged(__.Compile); } } }
private Boolean _Enable;
/// <summary>启用</summary>
[DisplayName("启用")]
@ -175,6 +191,8 @@ namespace Stardust.Data
case __.Name : return _Name;
case __.DisplayName : return _DisplayName;
case __.Secret : return _Secret;
case __.Version : return _Version;
case __.Compile : return _Compile;
case __.Enable : return _Enable;
case __.Services : return _Services;
case __.Actions : return _Actions;
@ -200,6 +218,8 @@ namespace Stardust.Data
case __.Name : _Name = Convert.ToString(value); break;
case __.DisplayName : _DisplayName = Convert.ToString(value); break;
case __.Secret : _Secret = Convert.ToString(value); break;
case __.Version : _Version = Convert.ToString(value); break;
case __.Compile : _Compile = Convert.ToDateTime(value); break;
case __.Enable : _Enable = Convert.ToBoolean(value); break;
case __.Services : _Services = Convert.ToInt32(value); break;
case __.Actions : _Actions = Convert.ToInt32(value); break;
@ -236,6 +256,12 @@ namespace Stardust.Data
/// <summary>密钥</summary>
public static readonly Field Secret = FindByName(__.Secret);
/// <summary>版本</summary>
public static readonly Field Version = FindByName(__.Version);
/// <summary>编译时间</summary>
public static readonly Field Compile = FindByName(__.Compile);
/// <summary>启用</summary>
public static readonly Field Enable = FindByName(__.Enable);
@ -296,6 +322,12 @@ namespace Stardust.Data
/// <summary>密钥</summary>
public const String Secret = "Secret";
/// <summary>版本</summary>
public const String Version = "Version";
/// <summary>编译时间</summary>
public const String Compile = "Compile";
/// <summary>启用</summary>
public const String Enable = "Enable";
@ -357,6 +389,12 @@ namespace Stardust.Data
/// <summary>密钥</summary>
String Secret { get; set; }
/// <summary>版本</summary>
String Version { get; set; }
/// <summary>编译时间</summary>
DateTime Compile { get; set; }
/// <summary>启用</summary>
Boolean Enable { get; set; }

View File

@ -6,6 +6,8 @@
<Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" />
<Column Name="DisplayName" DataType="String" Description="显示名" />
<Column Name="Secret" DataType="String" Description="密钥" />
<Column Name="Version" DataType="String" Description="版本" />
<Column Name="Compile" DataType="DateTime" Description="编译时间" />
<Column Name="Enable" DataType="Boolean" Description="启用" />
<Column Name="Services" DataType="Int32" Description="服务数。该应用提供的服务数" />
<Column Name="Actions" DataType="Int32" Description="功能数。该应用提供的功能数" />
@ -91,10 +93,13 @@
<Columns>
<Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
<Column Name="AppID" DataType="Int32" Description="应用" />
<Column Name="Instance" DataType="String" Description="实例。IP@进程" />
<Column Name="Session" DataType="String" Description="会话。tcp://ip:port" />
<Column Name="Session" DataType="String" Description="实例。IP加端口" />
<Column Name="Client" DataType="String" Description="客户端。IP加进程" />
<Column Name="Name" DataType="String" Master="True" Description="名称。机器名称" />
<Column Name="Version" DataType="String" Description="版本。客户端" />
<Column Name="Compile" DataType="DateTime" Description="编译时间。客户端" />
<Column Name="Server" DataType="String" Description="服务端。客户端登录到哪个服务端IP加端口" />
<Column Name="Address" DataType="String" Description="服务地址。tcp://ip:port" />
<Column Name="Version" DataType="String" Description="版本" />
<Column Name="Services" DataType="Int32" Description="服务数。该应用提供的服务数" />
<Column Name="Actions" DataType="String" Length="500" Description="功能列表" />
<Column Name="Clients" DataType="Int32" Description="客户端数。服务提供者当前服务的客户端数" />
@ -104,7 +109,8 @@
</Columns>
<Indexes>
<Index Columns="Session" Unique="True" />
<Index Columns="AppID,Instance" Unique="True" />
<Index Columns="Client" />
<Index Columns="AppID" />
</Indexes>
</Table>
<Table Name="AppHistory" Description="应用历史" ConnName="Registry">

View File

@ -1,7 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyTitle>星尘数据</AssemblyTitle>
<Description>星尘,轻量级服务框架。远程方法调用,服务自动注册和发现,负载均衡,动态伸缩,故障转移,性能监控。</Description>
<Copyright>版权所有(C) 新生命开发团队 2019</Copyright>
<Company>新生命开发团队</Company>
<FileVersion>1.0.2019.0101</FileVersion>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
</PropertyGroup>
<ItemGroup>

View File

@ -1,7 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyTitle>星尘管理平台</AssemblyTitle>
<Description>星尘,轻量级服务框架。远程方法调用,服务自动注册和发现,负载均衡,动态伸缩,故障转移,性能监控。</Description>
<Copyright>版权所有(C) 新生命开发团队 2019</Copyright>
<Company>新生命开发团队</Company>
<FileVersion>1.0.2019.0101</FileVersion>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
</PropertyGroup>
<ItemGroup>

View File

@ -1,12 +1,114 @@
using System;
using System.Net;
using NewLife;
using NewLife.Agent;
using NewLife.Log;
using NewLife.Net;
using NewLife.Remoting;
using NewLife.Threading;
using Stardust.Data;
namespace Stardust
{
class Program
{
static void Main(string[] args)
static void Main(String[] args) => new MyService().Main();
}
/// <summary>服务类。名字可以自定义</summary>
class MyService : AgentServiceBase<MyService>
{
/// <summary>是否使用线程池调度。false表示禁用线程池改用Agent线程</summary>
public Boolean Pooling { get; set; } = true;
public MyService()
{
ServiceName = "Stardust";
ThreadPoolX.QueueUserWorkItem(() =>
{
var n = App.Meta.Count;
AppStat.Meta.Session.Dal.Db.ShowSQL = false;
var set2 = XCode.Setting.Current;
if (set2.IsNew)
{
set2.Debug = true;
set2.ShowSQL = false;
set2.TraceSQLTime = 3000;
set2.SQLiteDbPath = @"..\Data";
set2.Save();
}
});
// 注册菜单,在控制台菜单中按 t 可以执行Test函数主要用于临时处理数据
AddMenu('t', "数据测试", Test);
}
ApiServer _Server;
private void Init()
{
var sc = _Server;
if (sc == null)
{
var set = Setting.Current;
sc = new ApiServer(set.Port)
{
Log = XTrace.Log
};
if (Setting.Current.Debug)
{
var ns = sc.EnsureCreate() as NetServer;
ns.Log = XTrace.Log;
#if DEBUG
ns.LogSend = true;
ns.LogReceive = true;
sc.EncoderLog = XTrace.Log;
#endif
}
// 注册服务
sc.Register<StarService>();
StarService.Log = XTrace.Log;
StarService.Local = new IPEndPoint(NetHelper.MyIP(), set.Port);
sc.Start();
_Server = sc;
}
}
/// <summary>服务启动</summary>
/// <remarks>
/// 安装Windows服务后服务启动会执行一次该方法。
/// 控制台菜单按5进入循环调试也会执行该方法。
/// </remarks>
protected override void StartWork(String reason)
{
Init();
base.StartWork(reason);
}
/// <summary>服务停止</summary>
/// <remarks>
/// 安装Windows服务后服务停止会执行该方法。
/// 控制台菜单按5进入循环调试任意键结束时也会执行该方法。
/// </remarks>
protected override void StopWork(String reason)
{
base.StopWork(reason);
_Server.TryDispose();
_Server = null;
}
/// <summary>数据测试菜单t</summary>
public void Test()
{
Console.WriteLine("Hello World!");
}
}
}

28
Stardust/Setting.cs Normal file
View File

@ -0,0 +1,28 @@
using System;
using System.ComponentModel;
using NewLife.Xml;
namespace Stardust
{
/// <summary>配置</summary>
[XmlConfigFile("Config/Stardust.config", 15000)]
public class Setting : XmlConfig<Setting>
{
#region
/// <summary>调试开关。默认true</summary>
[Description("调试开关。默认true")]
public Boolean Debug { get; set; } = true;
/// <summary>服务端口。默认6666</summary>
[Description("服务端口。默认6666")]
public Int32 Port { get; set; } = 6666;
#endregion
#region
/// <summary>实例化</summary>
public Setting()
{
}
#endregion
}
}

108
Stardust/StarClient.cs Normal file
View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using NewLife.Log;
using NewLife.Net;
using NewLife.Reflection;
using NewLife.Remoting;
using NewLife.Serialization;
namespace Stardust
{
public class StarClient : ApiClient
{
#region
/// <summary>用户名</summary>
public String UserName { get; set; }
/// <summary>密码</summary>
public String Password { get; set; }
/// <summary>是否已登录</summary>
public Boolean Logined { get; set; }
/// <summary>最后一次登录成功后的消息</summary>
public IDictionary<String, Object> Info { get; private set; }
#endregion
#region
/// <summary>实例化</summary>
public StarClient()
{
#if DEBUG
Log = XTrace.Log;
EncoderLog = XTrace.Log;
#endif
}
/// <summary>实例化</summary>
/// <param name="uri"></param>
public StarClient(String uri)
{
if (!uri.IsNullOrEmpty())
{
var u = new Uri(uri);
Servers = new[] { "{2}://{0}:{1}".F(u.Host, u.Port, u.Scheme) };
var us = u.UserInfo.Split(":");
if (us.Length > 0) UserName = us[0];
if (us.Length > 1) Password = us[1];
}
}
#endregion
#region
/// <summary>连接后自动登录</summary>
/// <param name="client">客户端</param>
/// <param name="force">强制登录</param>
protected override async Task<Object> OnLoginAsync(ISocketClient client, Boolean force)
{
if (Logined && !force) return null;
var asmx = AssemblyX.Entry;
var arg = new
{
user = UserName,
pass = Password.MD5(),
machine = Environment.MachineName,
processid = Process.GetCurrentProcess().Id,
version = asmx?.Version,
compile = asmx?.Compile,
};
var rs = await base.InvokeWithClientAsync<Object>(client, "Login", arg);
if (Setting.Current.Debug) XTrace.WriteLine("登录{0}成功!{1}", client, rs.ToJson());
Logined = true;
return Info = rs as IDictionary<String, Object>;
}
#endregion
#region
public async Task<Boolean> Report(String[] services)
{
return await InvokeAsync<Boolean>(nameof(Report), services);
}
#endregion
#region
#if DEBUG
/// <summary>创建</summary>
/// <param name="svr"></param>
/// <returns></returns>
protected override ISocketClient OnCreate(String svr)
{
var client = base.OnCreate(svr);
if (client != null) client.Log = Log;
return client;
}
#endif
#endregion
}
}

195
Stardust/StarService.cs Normal file
View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using NewLife.Log;
using NewLife.Net;
using NewLife.Remoting;
using Stardust.Data;
namespace Stardust
{
[Api(null)]
public class StarService : IApi, IActionFilter
{
#region
/// <summary>本地节点</summary>
public static EndPoint Local { get; set; }
#endregion
#region
public IApiSession Session { get; set; }
/// <summary>
/// 传入应用名和密钥登陆,
/// 返回应用名和应用显示名
/// </summary>
/// <param name="user">应用名</param>
/// <param name="pass"></param>
/// <returns></returns>
[Api(nameof(Login))]
public Object Login(String user, String pass)
{
if (user.IsNullOrEmpty()) throw new ArgumentNullException(nameof(user));
if (pass.IsNullOrEmpty()) throw new ArgumentNullException(nameof(pass));
var ns = Session as INetSession;
var ip = ns.Remote.Host;
var ps = ControllerContext.Current.Parameters;
WriteLog("[{0}]从[{1}]登录", user, ns.Remote);
// 找应用
var app = App.FindByName(user);
if (app == null || app.Secret.IsNullOrEmpty())
{
if (app == null) app = new App();
if (app.ID == 0)
{
app.Name = user;
//app.Secret = pass;
app.CreateIP = ip;
app.CreateTime = DateTime.Now;
app.Enable = true;
}
var name = ps["name"] + "";
if (!name.IsNullOrEmpty()) app.DisplayName = name;
app.UpdateIP = ip;
app.UpdateTime = DateTime.Now;
app.Save();
}
if (!app.Enable) throw new Exception("已禁用!");
// 核对密码
if (!app.Secret.IsNullOrEmpty())
{
var pass2 = app.Secret.MD5();
if (pass != pass2) throw new Exception("密码错误!");
}
// 应用上线
CreateOnline(app, ns, ps);
app.LastIP = ip;
app.LastLogin = DateTime.Now;
app.Save();
// 记录当前用户
Session["App"] = app;
return new
{
app.Name,
app.DisplayName,
};
}
void IActionFilter.OnActionExecuting(ControllerContext filterContext)
{
var act = filterContext.ActionName;
if (act == nameof(Login)) return;
if (Session["App"] is App app)
{
var online = GetOnline(app, Session as INetSession);
online.UpdateTime = DateTime.Now;
online.SaveAsync();
}
else
{
var ns = Session as INetSession;
throw new ApiException(401, "{0}未登录!不能执行{1}".F(ns.Remote, act));
}
}
void IActionFilter.OnActionExecuted(ControllerContext filterContext)
{
var ex = filterContext.Exception;
if (ex != null && !filterContext.ExceptionHandled)
{
// 显示错误
if (ex is ApiException)
XTrace.Log.Error(ex.Message);
else
XTrace.WriteException(ex);
}
}
#endregion
#region
/// <summary>报告服务列表</summary>
/// <param name="services"></param>
/// <returns></returns>
[Api(nameof(Report))]
public Boolean Report(String[] services)
{
return false;
}
#endregion
#region 线
AppOnline CreateOnline(IApp app, INetSession ns, IDictionary<String, Object> ps)
{
var ip = ns.Remote.Host;
var machine = ps["machine"] + "";
var pid = ps["processid"].ToInt();
var ver = ps["version"] + "";
var compile = ps["compile"].ToDateTime();
var online = GetOnline(app, ns);
// 客户端特性
online.Client = $"{(ip.IsNullOrEmpty() ? machine : ip)}@{pid}";
online.Name = machine;
online.Version = ver;
online.Compile = compile;
// 服务器特性
pid = Process.GetCurrentProcess().Id;
online.Server = Local + "@" + pid;
online.Save();
// 真正的用户
Session["AppOnline"] = online;
// 下线
ns.OnDisposed += (s, e) => online.Delete();
// 版本和编译时间
if (app.Version.IsNullOrEmpty() || app.Version.CompareTo(ver) < 0) app.Version = ver;
if (app.Compile.Year < 2000 || app.Compile < compile) app.Compile = compile;
return online;
}
AppOnline GetOnline(IApp app, INetSession ns)
{
if (Session["AppOnline"] is AppOnline online) return online;
var ip = ns.Remote.Host;
var ins = ns.Remote.EndPoint + "";
online = AppOnline.FindBySession(ins) ?? new AppOnline { CreateIP = ip };
online.AppID = app.ID;
online.Session = ins;
return online;
}
#endregion
#region
/// <summary>日志</summary>
public static ILog Log { get; set; }
/// <summary>写日志</summary>
/// <param name="format"></param>
/// <param name="args"></param>
public static void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
#endregion
}
}

View File

@ -1,10 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyTitle>星尘</AssemblyTitle>
<Description>星尘,轻量级服务框架。远程方法调用,服务自动注册和发现,负载均衡,动态伸缩,故障转移,性能监控。</Description>
<Copyright>版权所有(C) 新生命开发团队 2019</Copyright>
<Company>新生命开发团队</Company>
<FileVersion>1.0.2019.0101</FileVersion>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
<OutputPath>..\Bin</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Stardust.Data\Stardust.Data.csproj" />
</ItemGroup>

12
Test/Program.cs Normal file
View File

@ -0,0 +1,12 @@
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

10
Test/Test.csproj Normal file
View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyVersion>1.0.*</AssemblyVersion>
<Deterministic>false</Deterministic>
</PropertyGroup>
</Project>

View File

@ -16,6 +16,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stardust.Data", "Stardust.D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stardust.WebFx", "Stardust.WebFx\Stardust.WebFx.csproj", "{A9E0597C-E818-4B33-8AC3-BE134D68B78E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{3B87711E-FF90-470B-A429-CFDF3B9B1F5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -38,6 +40,10 @@ Global
{A9E0597C-E818-4B33-8AC3-BE134D68B78E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9E0597C-E818-4B33-8AC3-BE134D68B78E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9E0597C-E818-4B33-8AC3-BE134D68B78E}.Release|Any CPU.Build.0 = Release|Any CPU
{3B87711E-FF90-470B-A429-CFDF3B9B1F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B87711E-FF90-470B-A429-CFDF3B9B1F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B87711E-FF90-470B-A429-CFDF3B9B1F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B87711E-FF90-470B-A429-CFDF3B9B1F5F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE