优化本地星尘探测,屏蔽超时异常信息

This commit is contained in:
大石头 2022-08-22 14:35:49 +08:00
parent 7d4e01cb2a
commit dd6b0c7eb7
3 changed files with 753 additions and 739 deletions

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0;net461</TargetFrameworks>
<AssemblyTitle>星尘代理</AssemblyTitle>
<Description>星尘,分布式资源调度,部署于每一个节点,连接服务端,支持节点监控、远程发布。</Description>
<Company>新生命开发团队</Company>

View File

@ -11,424 +11,434 @@ using NewLife.Remoting;
using Stardust.Managers;
using Stardust.Models;
namespace Stardust
namespace Stardust;
/// <summary>本地星尘客户端。连接本机星尘代理StarAgent</summary>
public class LocalStarClient
{
/// <summary>本地星尘客户端。连接本机星尘代理StarAgent</summary>
public class LocalStarClient
#region
/// <summary>代理信息</summary>
public AgentInfo Info { get; private set; }
/// <summary>本地服务端地址</summary>
public String Server { get => _local?.Server; set => _local.Server = value; }
private readonly AgentInfo _local;
private ApiClient _client;
#endregion
#region
/// <summary>实例化</summary>
public LocalStarClient()
{
#region
/// <summary>代理信息</summary>
public AgentInfo Info { get; private set; }
_local = AgentInfo.GetLocal();
_local.Server = StarSetting.Current.Server;
}
#endregion
/// <summary>本地服务端地址</summary>
public String Server { get => _local?.Server; set => _local.Server = value; }
#region
private void Init()
{
if (_client != null) return;
private readonly AgentInfo _local;
private ApiClient _client;
#endregion
#region
/// <summary>实例化</summary>
public LocalStarClient()
_client = new ApiClient("udp://127.0.0.1:5500")
{
_local = AgentInfo.GetLocal();
_local.Server = StarSetting.Current.Server;
}
#endregion
Timeout = 3_000,
Log = Log,
};
#region
private void Init()
var set = StarSetting.Current;
if (set.Debug) _client.EncoderLog = Log;
}
/// <summary>获取信息</summary>
/// <returns></returns>
public AgentInfo GetInfo()
{
var task = GetInfoAsync();
return task.Wait(500) ? task.Result : null;
}
/// <summary>获取信息</summary>
/// <returns></returns>
public async Task<AgentInfo> GetInfoAsync()
{
Init();
try
{
if (_client != null) return;
_client = new ApiClient("udp://127.0.0.1:5500")
{
Timeout = 3_000,
Log = Log,
};
var set = StarSetting.Current;
if (set.Debug) _client.EncoderLog = Log;
}
/// <summary>获取信息</summary>
/// <returns></returns>
public AgentInfo GetInfo()
{
var task = GetInfoAsync().ContinueWith(t => t.IsCompleted ? t.Result : null);
return task.Wait(500) ? task.Result : null;
}
/// <summary>获取信息</summary>
/// <returns></returns>
public async Task<AgentInfo> GetInfoAsync()
{
Init();
return Info = await _client.InvokeAsync<AgentInfo>("Info", _local);
}
#endregion
#region
/// <summary>自杀并重启</summary>
/// <returns></returns>
public Boolean KillAndRestartMySelf()
catch (TimeoutException)
{
Init();
var p = Process.GetCurrentProcess();
var fileName = p.MainModule.FileName;
var args = Environment.CommandLine.TrimStart(Path.ChangeExtension(fileName, ".dll")).Trim();
// 发起命令
var rs = _client.Invoke<String>("KillAndStart", new
{
processId = p.Id,
delay = 3,
fileName,
arguments = args,
workingDirectory = Environment.CurrentDirectory,
});
// 本进程退出
//p.Kill();
return !rs.IsNullOrEmpty();
return null;
}
#endregion
#region
/// <summary>探测并安装星尘代理</summary>
/// <param name="url">zip包下载源</param>
/// <param name="version">版本号</param>
/// <param name="target">目标目录</param>
public Boolean ProbeAndInstall(String url = null, String version = null, String target = null)
catch
{
//if (url.IsNullOrEmpty()) throw new ArgumentNullException(nameof(url));
if (url.IsNullOrEmpty())
{
var set = NewLife.Setting.Current;
url = set.PluginServer.EnsureEnd("/");
url += "star/";
if (Environment.Version.Major >= 6)
url += "staragent60.zip";
else if (Environment.Version.Major >= 5)
url += "staragent50.zip";
else if (Environment.Version.Major >= 4)
url += "staragent45.zip";
else
url += "staragent31.zip";
}
// 尝试连接,获取版本
try
{
var info = GetInfo();
if (info != null)
{
// 比目标版本高,不需要安装
if (String.Compare(info.Version, version) >= 0) return true;
if (!info.FileName.IsNullOrEmpty()) info.FileName = info.FileName.TrimEnd(" (deleted)");
if (target.IsNullOrEmpty()) target = Path.GetDirectoryName(info.FileName);
WriteLog("StarAgent在用版本 v{0},低于目标版本 v{1}", info.Version, version);
}
}
catch (Exception ex)
{
WriteLog("没有探测到StarAgent{0}", ex.GetTrue().Message);
}
if (target.IsNullOrEmpty())
{
// 在进程中查找
var p = Process.GetProcesses().FirstOrDefault(e => e.ProcessName == "StarAgent");
if (p != null)
{
try
{
target = Path.GetDirectoryName(p.MainModule.FileName);
}
catch
{
target = Path.GetDirectoryName(p.MainWindowTitle);
}
WriteLog("发现进程StarAgentProcessId={0}target={1}", p.Id, target);
}
}
// 准备安装,甭管是否能够成功重启,先覆盖了文件再说
{
if (target.IsNullOrEmpty()) target = "..\\staragent";
target = target.GetFullPath();
target.EnsureDirectory(false);
WriteLog("目标:{0}", target);
var ug = new Upgrade
{
SourceFile = Path.GetFileName(url).GetFullPath(),
DestinationPath = target,
Log = XTrace.Log,
};
WriteLog("下载:{0}", url);
var client = new HttpClient();
client.DownloadFileAsync(url, ug.SourceFile).Wait();
ug.Update();
File.Delete(ug.SourceFile);
}
{
// 在进程中查找
var info = Info;
var inService = info?.Arguments == "-s";
var p = info != null && info.ProcessId > 0 ?
Process.GetProcessById(info.ProcessId) :
Process.GetProcesses().FirstOrDefault(e => e.ProcessName == "StarAgent");
// 重启目标
if (p != null && !inService)
{
try
{
p.Kill();
}
catch (Win32Exception) { }
catch (Exception ex)
{
XTrace.WriteException(ex);
}
}
var fileName = info?.FileName;
if (!fileName.IsNullOrEmpty() && Path.GetFullPath(fileName).EqualIgnoreCase("dotnet.exe")) fileName = info.Arguments;
var rs = false;
if (Runtime.Windows)
rs = RunAgentOnWindows(fileName, target, inService);
else if (Runtime.Linux)
rs = RunAgentOnLinux(fileName, target, inService);
if (!rs)
rs = RunAgentOnDotnet(fileName, target, inService);
}
return true;
throw;
}
private Boolean RunAgentOnWindows(String fileName, String target, Boolean inService)
{
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.exe").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnWindows fileName={0}, inService={1}", fileName, inService);
if (inService)
{
Process.Start(fileName, "-stop");
Process.Start(fileName, "-start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo(fileName, "-run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
private Boolean RunAgentOnLinux(String fileName, String target, Boolean inService)
{
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnLinux fileName={0}, inService={1}", fileName, inService);
// 在Linux中设置执行权限
Process.Start("chmod", $"+x {fileName}");
if (inService)
{
Process.Start(fileName, "-stop");
Process.Start(fileName, "-start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo(fileName, "-run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
private Boolean RunAgentOnDotnet(String fileName, String target, Boolean inService)
{
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.dll").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnDotnet fileName={0}, inService={1}", fileName, inService);
if (inService)
{
Process.Start("dotnet", $"{fileName} -stop");
Process.Start("dotnet", $"{fileName} -start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo("dotnet", $"{fileName} -run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
/// <summary>探测并安装星尘代理</summary>
/// <param name="url">zip包下载源</param>
/// <param name="version">版本号</param>
/// <param name="target">目标目录</param>
public static Task ProbeAsync(String url = null, String version = null, String target = null)
{
return Task.Run(() =>
{
var client = new LocalStarClient();
client.ProbeAndInstall(url, version, target);
});
}
#endregion
#region
/// <summary>安装应用服务(星尘代理守护)</summary>
/// <param name="service"></param>
/// <returns></returns>
public async Task<ProcessInfo> Install(ServiceInfo service)
{
Init();
return await _client.InvokeAsync<ProcessInfo>("Install", service);
}
/// <summary>安装应用服务(星尘代理守护)</summary>
/// <param name="name">服务名,唯一标识</param>
/// <param name="fileName">文件</param>
/// <param name="arguments">参数</param>
/// <param name="workingDirectory">工作目录</param>
/// <returns></returns>
public async Task<ProcessInfo> Install(String name, String fileName, String arguments = null, String workingDirectory = null)
{
Init();
return await _client.InvokeAsync<ProcessInfo>("Install", new ServiceInfo
{
Name = name,
FileName = fileName,
Arguments = arguments,
WorkingDirectory = workingDirectory,
AutoStart = true,
ReloadOnChange = true,
});
}
/// <summary>卸载应用服务</summary>
/// <param name="serviceName"></param>
/// <returns></returns>
public async Task<Boolean> Uninstall(String serviceName)
{
Init();
return await _client.InvokeAsync<Boolean>("Uninstall", serviceName);
}
#endregion
#region
/// <summary>在局域网中广播扫描所有StarAgent</summary>
/// <param name="local">本地信息,用于告知对方我是谁</param>
/// <param name="timeout"></param>
/// <returns></returns>
public static IEnumerable<AgentInfo> Scan(AgentInfo local = null, Int32 timeout = 15_000)
{
var encoder = new JsonEncoder { Log = XTrace.Log };
// 构造请求消息
//var ms = new MemoryStream();
//var writer = new BinaryWriter(ms);
//writer.Write("Info");
//writer.Write(0);
//var msg = new DefaultMessage
//{
// Payload = ms.ToArray()
//};
//var buf = msg.ToPacket().ToArray();
var buf = encoder.CreateRequest("Info", null).ToPacket().ToArray();
// 广播消息
var udp = new UdpClient();
udp.Send(buf, buf.Length, new IPEndPoint(IPAddress.Broadcast, 5500));
var end = DateTime.Now.AddSeconds(timeout);
while (DateTime.Now < end)
{
var rs = new DefaultMessage();
IPEndPoint ep = null;
buf = udp.Receive(ref ep);
if (buf != null && rs.Read(buf) && encoder.Decode(rs, out var action, out _, out var data))
{
//ms = rs.Payload.GetStream();
//var reader=new BinaryReader(ms);
//var name=reader.ReadString();
//var code = reader.ReadInt32();
//var data=reader
var js = encoder.DecodeResult(action, data, rs);
var info = (AgentInfo)encoder.Convert(js, typeof(AgentInfo));
yield return info;
}
}
}
#endregion
#region
/// <summary>日志</summary>
public ILog Log { get; set; }
/// <summary>写日志</summary>
/// <param name="format"></param>
/// <param name="args"></param>
public void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
#endregion
}
#endregion
#region
/// <summary>自杀并重启</summary>
/// <returns></returns>
public Boolean KillAndRestartMySelf()
{
Init();
var p = Process.GetCurrentProcess();
var fileName = p.MainModule.FileName;
var args = Environment.CommandLine.TrimStart(Path.ChangeExtension(fileName, ".dll")).Trim();
// 发起命令
var rs = _client.Invoke<String>("KillAndStart", new
{
processId = p.Id,
delay = 3,
fileName,
arguments = args,
workingDirectory = Environment.CurrentDirectory,
});
// 本进程退出
//p.Kill();
return !rs.IsNullOrEmpty();
}
#endregion
#region
/// <summary>探测并安装星尘代理</summary>
/// <param name="url">zip包下载源</param>
/// <param name="version">版本号</param>
/// <param name="target">目标目录</param>
public Boolean ProbeAndInstall(String url = null, String version = null, String target = null)
{
//if (url.IsNullOrEmpty()) throw new ArgumentNullException(nameof(url));
if (url.IsNullOrEmpty())
{
var set = NewLife.Setting.Current;
url = set.PluginServer.EnsureEnd("/");
url += "star/";
if (Environment.Version.Major >= 6)
url += "staragent60.zip";
else if (Environment.Version.Major >= 5)
url += "staragent50.zip";
else if (Environment.Version.Major >= 4)
url += "staragent45.zip";
else
url += "staragent31.zip";
}
// 尝试连接,获取版本
try
{
var info = GetInfo();
if (info != null)
{
// 比目标版本高,不需要安装
if (String.Compare(info.Version, version) >= 0) return true;
if (!info.FileName.IsNullOrEmpty()) info.FileName = info.FileName.TrimEnd(" (deleted)");
if (target.IsNullOrEmpty()) target = Path.GetDirectoryName(info.FileName);
WriteLog("StarAgent在用版本 v{0},低于目标版本 v{1}", info.Version, version);
}
}
catch (Exception ex)
{
WriteLog("没有探测到StarAgent{0}", ex.GetTrue().Message);
}
if (target.IsNullOrEmpty())
{
// 在进程中查找
var p = Process.GetProcesses().FirstOrDefault(e => e.ProcessName == "StarAgent");
if (p != null)
{
try
{
target = Path.GetDirectoryName(p.MainModule.FileName);
}
catch
{
target = Path.GetDirectoryName(p.MainWindowTitle);
}
WriteLog("发现进程StarAgentProcessId={0}target={1}", p.Id, target);
}
}
// 准备安装,甭管是否能够成功重启,先覆盖了文件再说
{
if (target.IsNullOrEmpty()) target = "..\\staragent";
target = target.GetFullPath();
target.EnsureDirectory(false);
WriteLog("目标:{0}", target);
var ug = new Upgrade
{
SourceFile = Path.GetFileName(url).GetFullPath(),
DestinationPath = target,
Log = XTrace.Log,
};
WriteLog("下载:{0}", url);
var client = new HttpClient();
client.DownloadFileAsync(url, ug.SourceFile).Wait();
ug.Update();
File.Delete(ug.SourceFile);
}
{
// 在进程中查找
var info = Info;
var inService = info?.Arguments == "-s";
var p = info != null && info.ProcessId > 0 ?
Process.GetProcessById(info.ProcessId) :
Process.GetProcesses().FirstOrDefault(e => e.ProcessName == "StarAgent");
// 重启目标
if (p != null && !inService)
{
try
{
p.Kill();
}
catch (Win32Exception) { }
catch (Exception ex)
{
XTrace.WriteException(ex);
}
}
var fileName = info?.FileName;
if (!fileName.IsNullOrEmpty() && Path.GetFullPath(fileName).EqualIgnoreCase("dotnet.exe")) fileName = info.Arguments;
var rs = false;
if (Runtime.Windows)
rs = RunAgentOnWindows(fileName, target, inService);
else if (Runtime.Linux)
rs = RunAgentOnLinux(fileName, target, inService);
if (!rs)
rs = RunAgentOnDotnet(fileName, target, inService);
}
return true;
}
private Boolean RunAgentOnWindows(String fileName, String target, Boolean inService)
{
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.exe").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnWindows fileName={0}, inService={1}", fileName, inService);
if (inService)
{
Process.Start(fileName, "-stop");
Process.Start(fileName, "-start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo(fileName, "-run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
private Boolean RunAgentOnLinux(String fileName, String target, Boolean inService)
{
if (!fileName.IsNullOrEmpty() && Path.GetExtension(fileName) == ".dll") return false;
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnLinux fileName={0}, inService={1}", fileName, inService);
// 在Linux中设置执行权限
Process.Start("chmod", $"+x {fileName}");
if (inService)
{
Process.Start(fileName, "-stop");
Process.Start(fileName, "-start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo(fileName, "-run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
private Boolean RunAgentOnDotnet(String fileName, String target, Boolean inService)
{
if (fileName.IsNullOrEmpty()) fileName = target.CombinePath("StarAgent.dll").GetFullPath();
if (!File.Exists(fileName)) return false;
WriteLog("RunAgentOnDotnet fileName={0}, inService={1}", fileName, inService);
if (inService)
{
Process.Start("dotnet", $"{fileName} -stop");
Process.Start("dotnet", $"{fileName} -start");
WriteLog("启动服务成功");
}
else
{
var si = new ProcessStartInfo("dotnet", $"{fileName} -run")
{
WorkingDirectory = Path.GetDirectoryName(fileName),
UseShellExecute = true
};
var p = Process.Start(si);
WriteLog("启动进程成功 pid={0}", p.Id);
}
return true;
}
/// <summary>探测并安装星尘代理</summary>
/// <param name="url">zip包下载源</param>
/// <param name="version">版本号</param>
/// <param name="target">目标目录</param>
public static Task ProbeAsync(String url = null, String version = null, String target = null)
{
return Task.Run(() =>
{
var client = new LocalStarClient();
client.ProbeAndInstall(url, version, target);
});
}
#endregion
#region
/// <summary>安装应用服务(星尘代理守护)</summary>
/// <param name="service"></param>
/// <returns></returns>
public async Task<ProcessInfo> Install(ServiceInfo service)
{
Init();
return await _client.InvokeAsync<ProcessInfo>("Install", service);
}
/// <summary>安装应用服务(星尘代理守护)</summary>
/// <param name="name">服务名,唯一标识</param>
/// <param name="fileName">文件</param>
/// <param name="arguments">参数</param>
/// <param name="workingDirectory">工作目录</param>
/// <returns></returns>
public async Task<ProcessInfo> Install(String name, String fileName, String arguments = null, String workingDirectory = null)
{
Init();
return await _client.InvokeAsync<ProcessInfo>("Install", new ServiceInfo
{
Name = name,
FileName = fileName,
Arguments = arguments,
WorkingDirectory = workingDirectory,
AutoStart = true,
ReloadOnChange = true,
});
}
/// <summary>卸载应用服务</summary>
/// <param name="serviceName"></param>
/// <returns></returns>
public async Task<Boolean> Uninstall(String serviceName)
{
Init();
return await _client.InvokeAsync<Boolean>("Uninstall", serviceName);
}
#endregion
#region
/// <summary>在局域网中广播扫描所有StarAgent</summary>
/// <param name="local">本地信息,用于告知对方我是谁</param>
/// <param name="timeout"></param>
/// <returns></returns>
public static IEnumerable<AgentInfo> Scan(AgentInfo local = null, Int32 timeout = 15_000)
{
var encoder = new JsonEncoder { Log = XTrace.Log };
// 构造请求消息
//var ms = new MemoryStream();
//var writer = new BinaryWriter(ms);
//writer.Write("Info");
//writer.Write(0);
//var msg = new DefaultMessage
//{
// Payload = ms.ToArray()
//};
//var buf = msg.ToPacket().ToArray();
var buf = encoder.CreateRequest("Info", null).ToPacket().ToArray();
// 广播消息
var udp = new UdpClient();
udp.Send(buf, buf.Length, new IPEndPoint(IPAddress.Broadcast, 5500));
var end = DateTime.Now.AddSeconds(timeout);
while (DateTime.Now < end)
{
var rs = new DefaultMessage();
IPEndPoint ep = null;
buf = udp.Receive(ref ep);
if (buf != null && rs.Read(buf) && encoder.Decode(rs, out var action, out _, out var data))
{
//ms = rs.Payload.GetStream();
//var reader=new BinaryReader(ms);
//var name=reader.ReadString();
//var code = reader.ReadInt32();
//var data=reader
var js = encoder.DecodeResult(action, data, rs);
var info = (AgentInfo)encoder.Convert(js, typeof(AgentInfo));
yield return info;
}
}
}
#endregion
#region
/// <summary>日志</summary>
public ILog Log { get; set; }
/// <summary>写日志</summary>
/// <param name="format"></param>
/// <param name="args"></param>
public void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
#endregion
}

View File

@ -9,379 +9,383 @@ using NewLife.Model;
using NewLife.Reflection;
using NewLife.Remoting;
using NewLife.Security;
using NewLife.Serialization;
using Stardust.Configs;
using Stardust.Models;
using Stardust.Monitors;
using Stardust.Registry;
namespace Stardust
namespace Stardust;
/// <summary>星尘工厂</summary>
/// <remarks>
/// 星尘代理 https://newlifex.com/blood/staragent_install
/// 监控中心 https://newlifex.com/blood/stardust_monitor
/// 配置中心 https://newlifex.com/blood/stardust_configcenter
/// </remarks>
public class StarFactory : DisposeBase
{
/// <summary>星尘工厂</summary>
/// <remarks>
/// 星尘代理 https://newlifex.com/blood/staragent_install
/// 监控中心 https://newlifex.com/blood/stardust_monitor
/// 配置中心 https://newlifex.com/blood/stardust_configcenter
/// </remarks>
public class StarFactory : DisposeBase
#region
/// <summary>服务器地址</summary>
public String Server { get; set; }
/// <summary>应用</summary>
public String AppId { get; set; }
/// <summary>应用名</summary>
public String AppName { get; set; }
/// <summary>应用密钥</summary>
public String Secret { get; set; }
/// <summary>实例。应用可能多实例部署ip@proccessid</summary>
public String ClientId { get; set; }
///// <summary>服务名</summary>
//public String ServiceName { get; set; }
/// <summary>客户端</summary>
public IApiClient Client => _client;
/// <summary>应用客户端</summary>
public AppClient App => _client;
/// <summary>配置信息。从配置中心返回的信息头</summary>
public ConfigInfo ConfigInfo => (_config as StarHttpConfigProvider)?.ConfigInfo;
/// <summary>本地星尘代理</summary>
public LocalStarClient Local { get; private set; }
private AppClient _client;
private TokenHttpFilter _tokenFilter;
//private AppClient _appClient;
#endregion
#region
/// <summary>
/// 实例化星尘工厂先后读取appsettings.json、本地StarAgent、star.config
/// </summary>
public StarFactory() => Init();
/// <summary>实例化星尘工厂,指定地址、应用和密钥,创建工厂</summary>
/// <param name="server">服务端地址。为空时先后读取appsettings.json、本地StarAgent、star.config初始值为空不连接服务端</param>
/// <param name="appId">应用标识。为空时读取star.config初始值为入口程序集名称</param>
/// <param name="secret">应用密钥。为空时读取star.config初始值为空</param>
/// <returns></returns>
public StarFactory(String server, String appId, String secret)
{
#region
/// <summary>服务器地址</summary>
public String Server { get; set; }
Server = server;
AppId = appId;
Secret = secret;
/// <summary>应用</summary>
public String AppId { get; set; }
Init();
}
/// <summary>应用名</summary>
public String AppName { get; set; }
/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);
/// <summary>应用密钥</summary>
public String Secret { get; set; }
_tracer.TryDispose();
_config.TryDispose();
//_appClient.TryDispose();
}
/// <summary>实例。应用可能多实例部署ip@proccessid</summary>
public String ClientId { get; set; }
private void Init()
{
XTrace.WriteLine("正在初始化星尘……");
///// <summary>服务名</summary>
//public String ServiceName { get; set; }
Local = new LocalStarClient();
/// <summary>客户端</summary>
public IApiClient Client => _client;
// 从环境变量读取星尘地址、应用Id、密钥方便容器化部署
if (Server.IsNullOrEmpty()) Server = Environment.GetEnvironmentVariable("StarServer");
if (AppId.IsNullOrEmpty()) AppId = Environment.GetEnvironmentVariable("AppId");
if (Secret.IsNullOrEmpty()) Secret = Environment.GetEnvironmentVariable("Secret");
/// <summary>应用客户端</summary>
public AppClient App => _client;
/// <summary>配置信息。从配置中心返回的信息头</summary>
public ConfigInfo ConfigInfo => (_config as StarHttpConfigProvider)?.ConfigInfo;
/// <summary>本地星尘代理</summary>
public LocalStarClient Local { get; private set; }
private AppClient _client;
private TokenHttpFilter _tokenFilter;
//private AppClient _appClient;
#endregion
#region
/// <summary>
/// 实例化星尘工厂先后读取appsettings.json、本地StarAgent、star.config
/// </summary>
public StarFactory() => Init();
/// <summary>实例化星尘工厂,指定地址、应用和密钥,创建工厂</summary>
/// <param name="server">服务端地址。为空时先后读取appsettings.json、本地StarAgent、star.config初始值为空不连接服务端</param>
/// <param name="appId">应用标识。为空时读取star.config初始值为入口程序集名称</param>
/// <param name="secret">应用密钥。为空时读取star.config初始值为空</param>
/// <returns></returns>
public StarFactory(String server, String appId, String secret)
// 不区分大小写识别环境变量
foreach (DictionaryEntry item in Environment.GetEnvironmentVariables())
{
Server = server;
AppId = appId;
Secret = secret;
Init();
var key = item.Key + "";
if (Server.IsNullOrEmpty() && key.EqualIgnoreCase("StarServer"))
Server = item.Value + "";
else if (AppId.IsNullOrEmpty() && key.EqualIgnoreCase("AppId"))
AppId = item.Value + "";
else if (Secret.IsNullOrEmpty() && key.EqualIgnoreCase("Secret"))
Secret = item.Value + "";
}
/// <summary>销毁</summary>
/// <param name="disposing"></param>
protected override void Dispose(Boolean disposing)
// 读取本地appsetting
if (Server.IsNullOrEmpty() && File.Exists("appsettings.Development.json".GetFullPath()))
{
base.Dispose(disposing);
using var json = new JsonConfigProvider { FileName = "appsettings.Development.json" };
json.LoadAll();
_tracer.TryDispose();
_config.TryDispose();
//_appClient.TryDispose();
Server = json["StarServer"];
}
if (Server.IsNullOrEmpty() && File.Exists("appsettings.json".GetFullPath()))
{
using var json = new JsonConfigProvider { FileName = "appsettings.json" };
json.LoadAll();
Server = json["StarServer"];
}
private void Init()
if (!Server.IsNullOrEmpty() && Local.Server.IsNullOrEmpty()) Local.Server = Server;
//if (AppId != "StarAgent")
//{
// 借助本地StarAgent获取服务器地址
try
{
XTrace.WriteLine("正在初始化星尘……");
Local = new LocalStarClient();
// 从环境变量读取星尘地址、应用Id、密钥方便容器化部署
if (Server.IsNullOrEmpty()) Server = Environment.GetEnvironmentVariable("StarServer");
if (AppId.IsNullOrEmpty()) AppId = Environment.GetEnvironmentVariable("AppId");
if (Secret.IsNullOrEmpty()) Secret = Environment.GetEnvironmentVariable("Secret");
// 不区分大小写识别环境变量
foreach (DictionaryEntry item in Environment.GetEnvironmentVariables())
var inf = Local.GetInfo();
var server = inf?.Server;
if (!server.IsNullOrEmpty())
{
var key = item.Key + "";
if (Server.IsNullOrEmpty() && key.EqualIgnoreCase("StarServer"))
Server = item.Value + "";
else if (AppId.IsNullOrEmpty() && key.EqualIgnoreCase("AppId"))
AppId = item.Value + "";
else if (Secret.IsNullOrEmpty() && key.EqualIgnoreCase("Secret"))
Secret = item.Value + "";
if (Server.IsNullOrEmpty()) Server = server;
XTrace.WriteLine("星尘探测:{0}", server);
}
else
XTrace.WriteLine("星尘探测StarAgent Not Found");
}
catch (Exception ex)
{
XTrace.Log.Error("星尘探测失败!{0}", ex.Message);
}
//}
// 如果探测不到本地应用,则使用配置
var set = StarSetting.Current;
if (Server.IsNullOrEmpty()) Server = set.Server;
if (AppId.IsNullOrEmpty()) AppId = set.AppKey;
if (Secret.IsNullOrEmpty()) Secret = set.Secret;
// 生成ClientId用于唯一标识当前实例默认IP@pid
try
{
var executing = AssemblyX.Create(Assembly.GetExecutingAssembly());
var asm = AssemblyX.Entry ?? executing;
if (asm != null)
{
if (AppId.IsNullOrEmpty()) AppId = asm.Name;
if (AppName.IsNullOrEmpty()) AppName = asm.Title;
}
// 读取本地appsetting
if (Server.IsNullOrEmpty() && File.Exists("appsettings.Development.json".GetFullPath()))
{
using var json = new JsonConfigProvider { FileName = "appsettings.Development.json" };
json.LoadAll();
Server = json["StarServer"];
}
if (Server.IsNullOrEmpty() && File.Exists("appsettings.json".GetFullPath()))
{
using var json = new JsonConfigProvider { FileName = "appsettings.json" };
json.LoadAll();
Server = json["StarServer"];
}
if (!Server.IsNullOrEmpty() && Local.Server.IsNullOrEmpty()) Local.Server = Server;
// 借助本地StarAgent获取服务器地址
try
{
var inf = Local.GetInfo();
var server = inf?.Server;
if (!server.IsNullOrEmpty())
{
if (Server.IsNullOrEmpty()) Server = server;
XTrace.WriteLine("星尘探测:{0}", server);
}
}
catch (Exception ex)
{
XTrace.Log.Error("星尘探测失败!{0}", ex.Message);
}
// 如果探测不到本地应用,则使用配置
var set = StarSetting.Current;
if (Server.IsNullOrEmpty()) Server = set.Server;
if (AppId.IsNullOrEmpty()) AppId = set.AppKey;
if (Secret.IsNullOrEmpty()) Secret = set.Secret;
// 生成ClientId用于唯一标识当前实例默认IP@pid
try
{
var executing = AssemblyX.Create(Assembly.GetExecutingAssembly());
var asm = AssemblyX.Entry ?? executing;
if (asm != null)
{
if (AppId.IsNullOrEmpty()) AppId = asm.Name;
if (AppName.IsNullOrEmpty()) AppName = asm.Title;
}
ClientId = $"{NetHelper.MyIP()}@{Process.GetCurrentProcess().Id}";
}
catch
{
ClientId = Rand.NextString(8);
}
XTrace.WriteLine("星尘分布式服务 Server={0} AppId={1} ClientId={2}", Server, AppId, ClientId);
Valid();
var ioc = ObjectContainer.Current;
ioc.AddSingleton(this);
ioc.AddSingleton(p => Tracer);
ioc.AddSingleton(p => Config);
ioc.AddSingleton(p => Service);
ClientId = $"{NetHelper.MyIP()}@{Process.GetCurrentProcess().Id}";
}
catch
{
ClientId = Rand.NextString(8);
}
private Boolean Valid()
XTrace.WriteLine("星尘分布式服务 Server={0} AppId={1} ClientId={2}", Server, AppId, ClientId);
Valid();
var ioc = ObjectContainer.Current;
ioc.AddSingleton(this);
ioc.AddSingleton(p => Tracer);
ioc.AddSingleton(p => Config);
ioc.AddSingleton(p => Service);
}
private Boolean Valid()
{
//if (Server.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Server));
//if (AppId.IsNullOrEmpty()) throw new ArgumentNullException(nameof(AppId));
if (Server.IsNullOrEmpty() || AppId.IsNullOrEmpty()) return false;
if (_client == null)
{
//if (Server.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Server));
//if (AppId.IsNullOrEmpty()) throw new ArgumentNullException(nameof(AppId));
if (Server.IsNullOrEmpty() || AppId.IsNullOrEmpty()) return false;
if (_client == null)
if (!AppId.IsNullOrEmpty()) _tokenFilter = new TokenHttpFilter
{
if (!AppId.IsNullOrEmpty()) _tokenFilter = new TokenHttpFilter
{
UserName = AppId,
Password = Secret,
ClientId = ClientId,
};
UserName = AppId,
Password = Secret,
ClientId = ClientId,
};
var client = new AppClient(Server)
{
AppId = AppId,
AppName = AppName,
ClientId = ClientId,
NodeCode = Local?.Info?.Code,
Filter = _tokenFilter,
UseWebSocket = true,
Log = Log,
};
//var set = StarSetting.Current;
//if (set.Debug) client.Log = XTrace.Log;
_client = client;
InitTracer();
client.Tracer = _tracer;
client.Start();
}
return true;
}
#endregion
#region
private StarTracer _tracer;
/// <summary>监控中心</summary>
public ITracer Tracer
{
get
{
if (_tracer == null)
{
if (!Valid()) return null;
InitTracer();
}
return _tracer;
}
}
private void InitTracer()
{
XTrace.WriteLine("初始化星尘监控中心采样并定期上报应用性能埋点数据包括Api接口、Http请求、数据库操作、Redis操作等。可用于监控系统健康状态分析分布式系统的性能瓶颈。");
var tracer = new StarTracer(Server)
var client = new AppClient(Server)
{
AppId = AppId,
AppName = AppName,
//Secret = Secret,
ClientId = ClientId,
Client = _client,
NodeCode = Local?.Info?.Code,
Filter = _tokenFilter,
UseWebSocket = true,
Log = Log
Log = Log,
};
tracer.AttachGlobal();
_tracer = tracer;
}
#endregion
//var set = StarSetting.Current;
//if (set.Debug) client.Log = XTrace.Log;
#region
private HttpConfigProvider _config;
/// <summary>配置中心。务必在数据库操作和生成雪花Id之前使用激活</summary>
/// <remarks>
/// 文档 https://newlifex.com/blood/stardust_configcenter
/// </remarks>
public IConfigProvider Config
{
get
{
if (_config == null)
{
if (!Valid()) return null;
_client = client;
XTrace.WriteLine("初始化星尘配置中心提供集中配置管理能力自动从配置中心加载配置数据包括XCode数据库连接。配置中心同时支持分配应用实例的唯一WorkerId确保Snowflake算法能够生成绝对唯一的雪花Id");
InitTracer();
var config = new StarHttpConfigProvider
{
Server = Server,
AppId = AppId,
//Secret = Secret,
ClientId = ClientId,
Client = _client,
};
//if (!ClientId.IsNullOrEmpty()) config.ClientId = ClientId;
config.Attach(_client);
//!! 不需要默认加载,直到首次使用配置数据时才加载。因为有可能应用并不使用配置中心,仅仅是获取这个对象。避免网络不通时的报错日志
//config.LoadAll();
_config = config;
}
return _config;
}
}
#endregion
#region
private Boolean _initService;
/// <summary>注册中心,服务注册与发现</summary>
public IRegistry Service
{
get
{
if (!_initService)
{
if (!Valid()) return null;
_initService = true;
//_appClient = _client as AppClient;
XTrace.WriteLine("初始化星尘注册中心,提供服务注册与发布能力");
}
return _client;
}
client.Tracer = _tracer;
client.Start();
}
/// <summary>为指定服务创建客户端,从星尘注册中心获取服务地址。单例,应避免频繁创建客户端</summary>
/// <param name="serviceName"></param>
/// <param name="tag"></param>
/// <returns></returns>
public IApiClient CreateForService(String serviceName, String tag = null) => Task.Run(() => CreateForServiceAsync(serviceName, tag)).Result;
/// <summary>为指定服务创建客户端,从星尘注册中心获取服务地址。单例,应避免频繁创建客户端</summary>
/// <param name="serviceName"></param>
/// <param name="tag"></param>
/// <returns></returns>
public Task<IApiClient> CreateForServiceAsync(String serviceName, String tag = null) => Service.CreateForServiceAsync(serviceName, tag);
/// <summary>发布服务</summary>
/// <param name="serviceName">服务名</param>
/// <param name="address">服务地址</param>
/// <param name="tag">特性标签</param>
/// <param name="health">健康监测接口地址</param>
/// <returns></returns>
public Task<PublishServiceInfo> RegisterAsync(String serviceName, String address, String tag = null, String health = null) => Service.RegisterAsync(serviceName, address, tag, health);
/// <summary>消费得到服务地址信息</summary>
/// <param name="serviceName">服务名</param>
/// <param name="minVersion">最小版本</param>
/// <param name="tag">特性标签。只要包含该特性的服务提供者</param>
/// <returns></returns>
public Task<String[]> ResolveAddressAsync(String serviceName, String minVersion = null, String tag = null) => Service.ResolveAddressAsync(serviceName, minVersion, tag);
#endregion
#region
/// <summary>发送节点命令</summary>
/// <param name="nodeCode"></param>
/// <param name="command"></param>
/// <param name="argument"></param>
/// <param name="expire"></param>
/// <returns></returns>
public async Task<Int32> SendNodeCommand(String nodeCode, String command, String argument = null, Int32 expire = 3600)
{
if (!Valid()) return -1;
return await _client.PostAsync<Int32>("Node/SendCommand", new { Code = nodeCode, command, argument, expire });
}
/// <summary>发送应用命令</summary>
/// <param name="appId"></param>
/// <param name="command"></param>
/// <param name="argument"></param>
/// <param name="expire"></param>
/// <returns></returns>
public async Task<Int32> SendAppCommand(String appId, String command, String argument = null, Int32 expire = 3600)
{
if (!Valid()) return -1;
return await _client.PostAsync<Int32>("App/SendCommand", new { Code = appId, command, argument, expire });
}
#endregion
#region
/// <summary>日志。默认 XTrace.Log</summary>
public ILog Log { get; set; } = XTrace.Log;
#endregion
return true;
}
#endregion
#region
private StarTracer _tracer;
/// <summary>监控中心</summary>
public ITracer Tracer
{
get
{
if (_tracer == null)
{
if (!Valid()) return null;
InitTracer();
}
return _tracer;
}
}
private void InitTracer()
{
XTrace.WriteLine("初始化星尘监控中心采样并定期上报应用性能埋点数据包括Api接口、Http请求、数据库操作、Redis操作等。可用于监控系统健康状态分析分布式系统的性能瓶颈。");
var tracer = new StarTracer(Server)
{
AppId = AppId,
AppName = AppName,
//Secret = Secret,
ClientId = ClientId,
Client = _client,
Log = Log
};
tracer.AttachGlobal();
_tracer = tracer;
}
#endregion
#region
private HttpConfigProvider _config;
/// <summary>配置中心。务必在数据库操作和生成雪花Id之前使用激活</summary>
/// <remarks>
/// 文档 https://newlifex.com/blood/stardust_configcenter
/// </remarks>
public IConfigProvider Config
{
get
{
if (_config == null)
{
if (!Valid()) return null;
XTrace.WriteLine("初始化星尘配置中心提供集中配置管理能力自动从配置中心加载配置数据包括XCode数据库连接。配置中心同时支持分配应用实例的唯一WorkerId确保Snowflake算法能够生成绝对唯一的雪花Id");
var config = new StarHttpConfigProvider
{
Server = Server,
AppId = AppId,
//Secret = Secret,
ClientId = ClientId,
Client = _client,
};
//if (!ClientId.IsNullOrEmpty()) config.ClientId = ClientId;
config.Attach(_client);
//!! 不需要默认加载,直到首次使用配置数据时才加载。因为有可能应用并不使用配置中心,仅仅是获取这个对象。避免网络不通时的报错日志
//config.LoadAll();
_config = config;
}
return _config;
}
}
#endregion
#region
private Boolean _initService;
/// <summary>注册中心,服务注册与发现</summary>
public IRegistry Service
{
get
{
if (!_initService)
{
if (!Valid()) return null;
_initService = true;
//_appClient = _client as AppClient;
XTrace.WriteLine("初始化星尘注册中心,提供服务注册与发布能力");
}
return _client;
}
}
/// <summary>为指定服务创建客户端,从星尘注册中心获取服务地址。单例,应避免频繁创建客户端</summary>
/// <param name="serviceName"></param>
/// <param name="tag"></param>
/// <returns></returns>
public IApiClient CreateForService(String serviceName, String tag = null) => Task.Run(() => CreateForServiceAsync(serviceName, tag)).Result;
/// <summary>为指定服务创建客户端,从星尘注册中心获取服务地址。单例,应避免频繁创建客户端</summary>
/// <param name="serviceName"></param>
/// <param name="tag"></param>
/// <returns></returns>
public Task<IApiClient> CreateForServiceAsync(String serviceName, String tag = null) => Service.CreateForServiceAsync(serviceName, tag);
/// <summary>发布服务</summary>
/// <param name="serviceName">服务名</param>
/// <param name="address">服务地址</param>
/// <param name="tag">特性标签</param>
/// <param name="health">健康监测接口地址</param>
/// <returns></returns>
public Task<PublishServiceInfo> RegisterAsync(String serviceName, String address, String tag = null, String health = null) => Service.RegisterAsync(serviceName, address, tag, health);
/// <summary>消费得到服务地址信息</summary>
/// <param name="serviceName">服务名</param>
/// <param name="minVersion">最小版本</param>
/// <param name="tag">特性标签。只要包含该特性的服务提供者</param>
/// <returns></returns>
public Task<String[]> ResolveAddressAsync(String serviceName, String minVersion = null, String tag = null) => Service.ResolveAddressAsync(serviceName, minVersion, tag);
#endregion
#region
/// <summary>发送节点命令</summary>
/// <param name="nodeCode"></param>
/// <param name="command"></param>
/// <param name="argument"></param>
/// <param name="expire"></param>
/// <returns></returns>
public async Task<Int32> SendNodeCommand(String nodeCode, String command, String argument = null, Int32 expire = 3600)
{
if (!Valid()) return -1;
return await _client.PostAsync<Int32>("Node/SendCommand", new { Code = nodeCode, command, argument, expire });
}
/// <summary>发送应用命令</summary>
/// <param name="appId"></param>
/// <param name="command"></param>
/// <param name="argument"></param>
/// <param name="expire"></param>
/// <returns></returns>
public async Task<Int32> SendAppCommand(String appId, String command, String argument = null, Int32 expire = 3600)
{
if (!Valid()) return -1;
return await _client.PostAsync<Int32>("App/SendCommand", new { Code = appId, command, argument, expire });
}
#endregion
#region
/// <summary>日志。默认 XTrace.Log</summary>
public ILog Log { get; set; } = XTrace.Log;
#endregion
}