双网关冗余(未完成)
This commit is contained in:
@@ -308,5 +308,75 @@
|
||||
</summary>
|
||||
<param name="pluginName">插件名称,一般建议使用nameof()解决。</param>
|
||||
</member>
|
||||
<member name="T:TouchSocket.Rpc.GeneratorRpcProxyAttribute">
|
||||
<summary>
|
||||
标识该接口将使用源生成自动生成调用的代理类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.Prefix">
|
||||
<summary>
|
||||
调用键的前缀,包括服务的命名空间,类名,不区分大小写。格式:命名空间.类名
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.GenericConstraintTypes">
|
||||
<summary>
|
||||
生成泛型方法的约束
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.MethodInvoke">
|
||||
<summary>
|
||||
是否仅以函数名调用,当为True是,调用时仅需要传入方法名即可。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.Namespace">
|
||||
<summary>
|
||||
生成代码的命名空间
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.ClassName">
|
||||
<summary>
|
||||
生成的类名,不要包含“I”,生成接口时会自动添加。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.GeneratorFlag">
|
||||
<summary>
|
||||
生成代码
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcProxyAttribute.InheritedInterface">
|
||||
<summary>
|
||||
继承接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:TouchSocket.Rpc.GeneratorRpcServerAttribute">
|
||||
<summary>
|
||||
标识将通过源生成器生成Rpc服务的调用委托。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:TouchSocket.Rpc.GeneratorRpcServerRegisterAttribute">
|
||||
<summary>
|
||||
标识将通过源生成器生成Rpc服务的注册代码。
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcServerRegisterAttribute.MethodName">
|
||||
<summary>
|
||||
方法名称。默认是“RegisterAllFrom+AssemblyName”
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcServerRegisterAttribute.ClassName">
|
||||
<summary>
|
||||
扩展类类名,默认是“RegisterRpcServerFrom+AssemblyName+Extension”
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:TouchSocket.Rpc.GeneratorRpcServerRegisterAttribute.Accessibility">
|
||||
<summary>
|
||||
访问修饰。
|
||||
<para>
|
||||
如果为<see cref="F:TouchSocket.Rpc.Accessibility.Both"/>,将生成注册公共Rpc服务与非公共服务两个方法。其中非公共方法会在<see cref="P:TouchSocket.Rpc.GeneratorRpcServerRegisterAttribute.MethodName"/>之前以Internal开头。
|
||||
如果为<see cref="F:TouchSocket.Rpc.Accessibility.Internal"/>,将只生成注册非公共Rpc服务。
|
||||
如果为<see cref="F:TouchSocket.Rpc.Accessibility.Public"/>,将只生成注册公共Rpc服务。
|
||||
</para>
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"Management": {
|
||||
"RemoteUri": "127.0.0.1:7778", //主(备)站IP
|
||||
"Port": 7777, //监听端口
|
||||
"VerifyToken": "ThingsGateway", //登录token,双方一致
|
||||
"HeartbeatInterval": 3000, //心跳间隔
|
||||
"MaxErrorCount": 3, //最大错误次数
|
||||
"Redundancy": {
|
||||
"Enable": false, //启用冗余
|
||||
"IsPrimary": true, //是否主站
|
||||
"IsHot": true
|
||||
//默认主站优先,当主站恢复后,从站切换回主站,并重新开始向从站数据同步。
|
||||
//备用站对外提供的数据都来自主站的数据同步
|
||||
//热切换:主备站都完成对采集的初始化,并且都采集数据
|
||||
//冷切换:主站完成对采集的初始化,并采集数据,只有当主站故障后,备用站才开始初始化并采集。
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"Management": {
|
||||
"ServerUri": "127.0.0.1:7777", //IP
|
||||
"ServerStandbyUri": "127.0.0.1:7777", //备用IP
|
||||
"Port": 7777, //监听端口
|
||||
"VerifyToken": "ThingsGateway",
|
||||
"HeartbeatInterval": 3000, //心跳间隔
|
||||
"MaxErrorCount": 3, //最大错误次数
|
||||
"Redundancy": {
|
||||
"Enable": true, //启用冗余
|
||||
"IsPrimary": true, //是否主站
|
||||
"IsHot": true //是否热冗余
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ public class Startup : AppStartup
|
||||
services.AddHostedService<CollectDeviceWorker>();
|
||||
services.AddHostedService<BusinessDeviceWorker>();
|
||||
services.AddHostedService<AlarmWorker>();
|
||||
services.AddConfigurableOptions<ManagementOptions>();
|
||||
services.AddHostedService<ManagementWoker>();
|
||||
|
||||
TypeExtension.DefaultDisplayNameFuncs.Add(a => a.GetCustomAttribute<DynamicPropertyAttribute>()?.Description);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,18 @@
|
||||
<PackageReference Include="CS-Script" Version="4.8.14" />
|
||||
<!--CS-Script与Furion冲突,直接安装覆盖版本-->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="2.0.0-beta.278" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Config\Management.Production.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\Management.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Config\Gateway.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
@@ -446,20 +446,9 @@ public class AlarmWorker : BackgroundService
|
||||
|
||||
private EasyLock _easyLock = new(false);
|
||||
|
||||
private async Task CollectDeviceWorker_Starting()
|
||||
{
|
||||
await StartAsync();
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Stoping()
|
||||
{
|
||||
await StopAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("报警服务启动");
|
||||
_appLifetime.ApplicationStarted.Register(() => { _easyLock.Release(); _easyLock = null; });
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
@@ -467,7 +456,6 @@ public class AlarmWorker : BackgroundService
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("报警服务停止");
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
@@ -475,8 +463,7 @@ public class AlarmWorker : BackgroundService
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
WorkerUtil.GetWoker<CollectDeviceWorker>().Starting += CollectDeviceWorker_Starting;
|
||||
WorkerUtil.GetWoker<CollectDeviceWorker>().Stoping += CollectDeviceWorker_Stoping;
|
||||
|
||||
GlobalData = _serviceScope.ServiceProvider.GetService<GlobalData>();
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
|
||||
@@ -187,22 +187,6 @@ public class BusinessDeviceWorker : DeviceWorker
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Starting()
|
||||
{
|
||||
await CreatThreadsAsync();
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Started()
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await StartAsync();
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Stoping()
|
||||
{
|
||||
await StopAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -215,9 +199,7 @@ public class BusinessDeviceWorker : DeviceWorker
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
WorkerUtil.GetWoker<CollectDeviceWorker>().Starting += CollectDeviceWorker_Starting;
|
||||
WorkerUtil.GetWoker<CollectDeviceWorker>().Started += CollectDeviceWorker_Started;
|
||||
WorkerUtil.GetWoker<CollectDeviceWorker>().Stoping += CollectDeviceWorker_Stoping;
|
||||
|
||||
PluginService = _serviceScope.ServiceProvider.GetService<IPluginService>();
|
||||
GlobalData = _serviceScope.ServiceProvider.GetService<GlobalData>();
|
||||
await WhileExecuteAsync(stoppingToken);
|
||||
@@ -116,6 +116,12 @@ public class CollectDeviceWorker : DeviceWorker
|
||||
using var stoppingToken = new CancellationTokenSource();
|
||||
_stoppingToken = stoppingToken.Token;
|
||||
stoppingToken.Cancel();
|
||||
await StopAsync();
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
internal async Task StopAsync()
|
||||
{
|
||||
await BeforeRemoveAllChannelThreadAsync();
|
||||
//停止其他后台服务
|
||||
await ProtectedStoping();
|
||||
@@ -123,18 +129,6 @@ public class CollectDeviceWorker : DeviceWorker
|
||||
await RemoveAllChannelThreadAsync();
|
||||
//停止其他后台服务
|
||||
await ProtectedStoped();
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
PluginService = _serviceScope.ServiceProvider.GetService<IPluginService>();
|
||||
GlobalData = _serviceScope.ServiceProvider.GetService<GlobalData>();
|
||||
//重启采集线程,会启动其他后台服务
|
||||
await RestartAsync();
|
||||
await WhileExecuteAsync(stoppingToken);
|
||||
}
|
||||
|
||||
#endregion worker服务
|
||||
@@ -0,0 +1,368 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Furion.ConfigurableOptions;
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp;
|
||||
using TouchSocket.Dmtp.FileTransfer;
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ManagementOptions : IConfigurableOptions
|
||||
{
|
||||
public string ServerUri { get; set; }
|
||||
public string ServerStandbyUri { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string VerifyToken { get; set; }
|
||||
public int HeartbeatInterval { get; set; }
|
||||
public int MaxErrorCount { get; set; }
|
||||
public Redundancy Redundancy { get; set; }
|
||||
}
|
||||
|
||||
public class Redundancy
|
||||
{
|
||||
public bool Enable { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
public bool IsHot { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TODO:网关管理服务
|
||||
/// </summary>
|
||||
public class ManagementWoker : BackgroundService
|
||||
{
|
||||
protected IServiceScope _serviceScope;
|
||||
private readonly IHostApplicationLifetime _appLifetime;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="ManagementWoker"/>
|
||||
public ManagementWoker(IServiceScopeFactory serviceScopeFactory, IHostApplicationLifetime appLifetime)
|
||||
{
|
||||
_serviceScope = serviceScopeFactory.CreateScope();
|
||||
_logger = _serviceScope.ServiceProvider.GetService<ILoggerFactory>().CreateLogger("网关管理服务");
|
||||
_appLifetime = appLifetime;
|
||||
}
|
||||
|
||||
internal readonly EasyLock workerLock = new();
|
||||
|
||||
private async Task CollectDeviceWorker_Starting()
|
||||
{
|
||||
if (isStart)
|
||||
{
|
||||
await WorkerUtil.GetWoker<AlarmWorker>().StartAsync();
|
||||
await WorkerUtil.GetWoker<BusinessDeviceWorker>().CreatThreadsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Stoping()
|
||||
{
|
||||
await WorkerUtil.GetWoker<AlarmWorker>().StopAsync();
|
||||
await WorkerUtil.GetWoker<BusinessDeviceWorker>().StopAsync();
|
||||
}
|
||||
|
||||
private async Task CollectDeviceWorker_Started()
|
||||
{
|
||||
if (isStart)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await WorkerUtil.GetWoker<BusinessDeviceWorker>().StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
#region worker服务
|
||||
|
||||
private EasyLock _easyLock = new();
|
||||
|
||||
internal volatile bool isStart = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关管理服务启动");
|
||||
await _easyLock.WaitAsync();
|
||||
_appLifetime.ApplicationStarted.Register(() => { _easyLock.Release(); _easyLock = null; });
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关管理服务停止");
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
internal ManagementOptions options;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
var globalData = _serviceScope.ServiceProvider.GetService<GlobalData>();
|
||||
options = App.GetOptions<ManagementOptions>();
|
||||
var collectDeviceWorker = WorkerUtil.GetWoker<CollectDeviceWorker>();
|
||||
collectDeviceWorker.Starting += CollectDeviceWorker_Starting;
|
||||
collectDeviceWorker.Started += CollectDeviceWorker_Started;
|
||||
collectDeviceWorker.Stoping += CollectDeviceWorker_Stoping;
|
||||
|
||||
if (options.Redundancy.Enable)
|
||||
{
|
||||
if (options.Redundancy.IsHot)
|
||||
{
|
||||
//热备冗余,直接启动采集服务
|
||||
await collectDeviceWorker.RestartAsync();
|
||||
}
|
||||
var udpDmtp = GetUdpDmtp(options);
|
||||
await udpDmtp.StartAsync();//启动
|
||||
var firstStart = true;
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
GatewayState? gatewayState = null;
|
||||
try
|
||||
{
|
||||
await workerLock.WaitAsync();
|
||||
{
|
||||
var readErrorCount = 0;
|
||||
while (readErrorCount < options.MaxErrorCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
gatewayState = await udpDmtp.GetDmtpRpcActor().InvokeTAsync<GatewayState>("GetGatewayStateAsync", InvokeOption.WaitInvoke, isStart);
|
||||
break;
|
||||
}
|
||||
catch
|
||||
{
|
||||
readErrorCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
workerLock.Release();
|
||||
}
|
||||
if (gatewayState != null)
|
||||
{
|
||||
if (gatewayState.IsPrimary == options.Redundancy.IsPrimary)
|
||||
{
|
||||
_logger.LogInformation("主备站设置重复,退出冗余服务!");
|
||||
await StartAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (gatewayState == null)
|
||||
{
|
||||
//无法获取状态,启动本机
|
||||
if (!isStart)
|
||||
{
|
||||
_logger.LogInformation("无法连接冗余站点,本机将切换到正常状态");
|
||||
await StartAsync();
|
||||
}
|
||||
}
|
||||
else if (gatewayState.IsPrimary)
|
||||
{
|
||||
//主站已经启动
|
||||
if (gatewayState.IsStart)
|
||||
{
|
||||
if (isStart || firstStart)
|
||||
{
|
||||
_logger.LogInformation("主站已恢复,本机将切换到备用状态");
|
||||
await StopAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//等待主站切换到正常后,再停止从站
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//从站已经启动
|
||||
if (gatewayState.IsStart)
|
||||
{
|
||||
//等待从站切换到备用后,再启动主站
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isStart)
|
||||
{
|
||||
_logger.LogInformation("本机(主站)将切换到正常状态");
|
||||
await StartAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:发布到从站数据
|
||||
//if (options.Redundancy.IsPrimary)
|
||||
//{
|
||||
// await udpDmtp.GetDmtpRpcActor().InvokeTAsync<GatewayState>("UpdateGatewayDataAsync", InvokeOption.WaitInvoke, globalData.CollectDevices, globalData.BusinessDevices);
|
||||
//}
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "循环线程出错");
|
||||
}
|
||||
finally
|
||||
{
|
||||
firstStart = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isStart = true;
|
||||
//无冗余,直接启动采集服务
|
||||
await collectDeviceWorker.RestartAsync();
|
||||
}
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "循环线程出错");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await workerLock.WaitAsync();
|
||||
|
||||
isStart = true;
|
||||
if (options.Redundancy.IsHot)
|
||||
{
|
||||
await CollectDeviceWorker_Starting();
|
||||
await CollectDeviceWorker_Started();
|
||||
}
|
||||
else
|
||||
{
|
||||
var collectDeviceWorker = WorkerUtil.GetWoker<CollectDeviceWorker>();
|
||||
await collectDeviceWorker.RestartAsync();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
workerLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StopAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await workerLock.WaitAsync();
|
||||
|
||||
isStart = false;
|
||||
if (options.Redundancy.IsHot)
|
||||
{
|
||||
await CollectDeviceWorker_Stoping();
|
||||
}
|
||||
else
|
||||
{
|
||||
var collectDeviceWorker = WorkerUtil.GetWoker<CollectDeviceWorker>();
|
||||
await collectDeviceWorker.StopAsync();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
workerLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion worker服务
|
||||
|
||||
#region
|
||||
|
||||
private void LogOut(TouchSocket.Core.LogLevel logLevel, object source, string message, Exception exception)
|
||||
{
|
||||
_logger?.Log_Out(logLevel, source, message, exception);
|
||||
}
|
||||
|
||||
private UdpDmtp GetUdpDmtp(ManagementOptions options)
|
||||
{
|
||||
var udpDmtp = new UdpDmtp();
|
||||
var config = new TouchSocketConfig()
|
||||
.SetRemoteIPHost(options.ServerUri)
|
||||
.SetBindIPHost(options.Port)
|
||||
.SetDmtpOption(
|
||||
new DmtpOption() { VerifyToken = options.VerifyToken })
|
||||
.ConfigureContainer(a =>
|
||||
{
|
||||
a.AddEasyLogger(LogOut);
|
||||
a.AddRpcStore(store =>
|
||||
{
|
||||
store.RegisterServer<ReverseCallbackServer>();
|
||||
// store.Container.RegisterSingleton<IHostApplicationLifetime>(_appLifetime);
|
||||
// store.Container.RegisterSingleton(client);
|
||||
});
|
||||
})
|
||||
.ConfigurePlugins(a =>
|
||||
{
|
||||
a.UseDmtpFileTransfer();//必须添加文件传输插件
|
||||
//a.Add<FilePlugin>();
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
.SetTick(TimeSpan.FromMilliseconds(options.HeartbeatInterval))
|
||||
.SetMaxFailCount(options.MaxErrorCount);
|
||||
a.UseDmtpRpc();
|
||||
});
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
config.UseUdpConnReset();
|
||||
}
|
||||
udpDmtp.Setup(config);
|
||||
return udpDmtp;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class GatewayState
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启动
|
||||
/// </summary>
|
||||
public bool IsStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否主站
|
||||
/// </summary>
|
||||
public bool IsPrimary { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
internal class ReverseCallbackServer : RpcServer
|
||||
{
|
||||
private ILog _logger;
|
||||
private ManagementWoker managementWoker;
|
||||
|
||||
public ReverseCallbackServer(ILog log)
|
||||
{
|
||||
_logger = log;
|
||||
managementWoker = WorkerUtil.GetWoker<ManagementWoker>();
|
||||
}
|
||||
|
||||
private EasyLock easyLock = new();
|
||||
|
||||
[DmtpRpc(true)]//使用方法名作为调用键
|
||||
public async Task<GatewayState> GetGatewayStateAsync(bool isStart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await easyLock.WaitAsync();
|
||||
|
||||
//冗余双方站点可能存在同时执行冗余切换的情况
|
||||
{
|
||||
GatewayState result = new();
|
||||
result.IsStart = managementWoker.isStart;
|
||||
|
||||
//避免出现偶发同时启动
|
||||
if (isStart && result.IsStart)
|
||||
{
|
||||
//请求方停止
|
||||
result.RequestStop = true;
|
||||
}
|
||||
if (!isStart && !result.IsStart)
|
||||
{
|
||||
//请求方启动
|
||||
result.RequestStop = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
easyLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// TODO:网关远程管理服务,使用client模式,适用于端口要求严格的网络环境
|
||||
/// </summary>
|
||||
public class ManagementSlaveWoker : BackgroundService
|
||||
{
|
||||
protected IServiceScope _serviceScope;
|
||||
private readonly IHostApplicationLifetime _appLifetime;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="AlarmWorker"/>
|
||||
public ManagementSlaveWoker(IServiceScopeFactory serviceScopeFactory, IHostApplicationLifetime appLifetime)
|
||||
{
|
||||
_serviceScope = serviceScopeFactory.CreateScope();
|
||||
_logger = _serviceScope.ServiceProvider.GetService<ILoggerFactory>().CreateLogger("网关远程管理Slave服务");
|
||||
_appLifetime = appLifetime;
|
||||
}
|
||||
|
||||
#region worker服务
|
||||
|
||||
private EasyLock _easyLock = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关远程管理Slave服务启动");
|
||||
await _easyLock.WaitAsync();
|
||||
_appLifetime.ApplicationStarted.Register(() => { _easyLock.Release(); _easyLock = null; });
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关远程管理Slave服务停止");
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion worker服务
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// TODO:网关远程管理服务,使用client模式,适用于端口要求严格的网络环境
|
||||
/// </summary>
|
||||
public class ManagementMasterWoker : BackgroundService
|
||||
{
|
||||
protected IServiceScope _serviceScope;
|
||||
private readonly IHostApplicationLifetime _appLifetime;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="AlarmWorker"/>
|
||||
public ManagementMasterWoker(IServiceScopeFactory serviceScopeFactory, IHostApplicationLifetime appLifetime)
|
||||
{
|
||||
_serviceScope = serviceScopeFactory.CreateScope();
|
||||
_logger = _serviceScope.ServiceProvider.GetService<ILoggerFactory>().CreateLogger("网关远程管理Master服务");
|
||||
_appLifetime = appLifetime;
|
||||
}
|
||||
|
||||
#region worker服务
|
||||
|
||||
private EasyLock _easyLock = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关远程管理Master服务启动");
|
||||
await _easyLock.WaitAsync();
|
||||
_appLifetime.ApplicationStarted.Register(() => { _easyLock.Release(); _easyLock = null; });
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("网关远程管理Master服务停止");
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion worker服务
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Furion.Logging.Extensions;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 网关更新服务
|
||||
/// </summary>
|
||||
public class UpdatesWorker : BackgroundService
|
||||
{
|
||||
protected IServiceScope _serviceScope;
|
||||
private readonly IHostApplicationLifetime _appLifetime;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
/// <inheritdoc cref="AlarmWorker"/>
|
||||
public UpdatesWorker(IServiceScopeFactory serviceScopeFactory, IHostApplicationLifetime appLifetime)
|
||||
{
|
||||
_serviceScope = serviceScopeFactory.CreateScope();
|
||||
_logger = _serviceScope.ServiceProvider.GetService<ILoggerFactory>().CreateLogger("网关更新服务");
|
||||
_appLifetime = appLifetime;
|
||||
}
|
||||
|
||||
#region worker服务
|
||||
|
||||
private EasyLock _easyLock = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("更新服务启动");
|
||||
await _easyLock.WaitAsync();
|
||||
_appLifetime.ApplicationStarted.Register(() => { _easyLock.Release(); _easyLock = null; });
|
||||
await base.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger?.LogInformation("更新服务停止");
|
||||
return base.StopAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await _easyLock?.WaitAsync();
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(60000, stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion worker服务
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NewLife.Core" Version="10.7.2024.202" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="TouchSocket" Version="2.0.0-beta.277" />
|
||||
<PackageReference Include="TouchSocket" Version="2.0.0-beta.278" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'!='net45'">
|
||||
|
||||
Reference in New Issue
Block a user