public void SendToMinerStudioAsync(string loginName, WsMessage message) { // TODO:考虑给每一个登录的MinerStudio用户建立一个会话,由基于LoginName推送改为基于会话Id推送 var minerStudioSessions = GetSessionsByLoginName(loginName).ToArray();// 避免发生集合被修改的异常 foreach (var minerStudioSession in minerStudioSessions) { ServerRoot.IfStudioClientTestIdLogElseNothing(minerStudioSession.ClientId, $"{nameof(WsMessage)}.{message.Type}"); minerStudioSession.SendAsync(message); } }
public void SendToMinerStudioAsync(string loginName, WsMessage message) { // TODO:考虑给每一个登录的MinerStudio用户建立一个会话,由基于LoginName推送改为基于会话Id推送 var minerStudioSessions = GetSessionsByLoginName(loginName).ToArray();// 避免发生集合被修改的异常 var userData = AppRoot.UserSet.GetUser(UserId.CreateLoginNameUserId(loginName)); if (userData != null) { foreach (var minerStudioSession in minerStudioSessions) { ServerRoot.IfStudioClientTestIdLogElseNothing(minerStudioSession.ClientId, $"{nameof(WsMessage)}.{message.Type}"); minerStudioSession.SendAsync(message, userData.Password); } } }
public MinerStudioSessionSet(IWsSessionsAdapter wsSessions) : base(wsSessions) { VirtualRoot.BuildEventPath <UserPasswordChangedMqEvent>("群控用户密码变更后通知群控客户端重新登录", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.ReLogin)); }); VirtualRoot.BuildEventPath <ConsoleOutLinesMqEvent>("收到ConsoleOutLinesMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(ConsoleOutLinesMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.ConsoleOutLines) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <LocalMessagesMqEvent>("收到LocalMessagesMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(LocalMessagesMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.LocalMessages) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <DrivesMqEvent>("收到DrivesMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(DrivesMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.Drives) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <LocalIpsMqEvent>("收到LocalIpsMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(LocalIpsMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.LocalIps) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <OperationResultsMqEvent>("收到OperationResultsMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(OperationResultsMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.OperationResults) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <OperationReceivedMqEvent>("收到OperationReceivedMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(OperationReceivedMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.OperationReceived) { Data = new WrapperClientId { ClientId = message.ClientId } }); #endregion }); VirtualRoot.BuildEventPath <LocalJsonMqEvent>("收到LocalJsonMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(LocalJsonMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.SelfWorkLocalJson) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <GpuProfilesJsonMqEvent>("收到GpuProfilesJsonMq消息后检查对应的用户是否登录着本节点,如果是则处理,否则忽略", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GpuProfilesJsonMqEvent)); SendToMinerStudioAsync(message.LoginName, new WsMessage(message.MessageId, WsMessage.GpuProfilesJson) { Data = new WrapperClientIdData { ClientId = message.ClientId, Data = message.Data } }); #endregion }); VirtualRoot.BuildEventPath <QueryClientsForWsResponseMqEvent>("收到QueryClientsResponseMq消息后通过Ws通道发送给群控客户端", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (IsTooOld(message.Timestamp)) { return; } ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(QueryClientsForWsResponseMqEvent)); var userData = AppRoot.UserSet.GetUser(UserId.CreateLoginNameUserId(message.LoginName)); if (userData != null && wsSessions.TryGetSession(message.SessionId, out IWsSessionAdapter session)) { session.SendAsync(new WsMessage(Guid.NewGuid(), WsMessage.ClientDatas) { Data = message.Response }.SignToBytes(userData.Password)); } #endregion }); }
public MinerClientSessionSet(IWsSessionsAdapter wsSessions) : base(wsSessions) { VirtualRoot.BuildEventPath <GetConsoleOutLinesMqEvent>("收到GetConsoleOutLines Mq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetConsoleOutLinesMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetConsoleOutLinesMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetConsoleOutLines) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetLocalMessagesMqEvent>("收到GetLocalMessages Mq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetLocalMessagesMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetLocalMessagesMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetLocalMessages) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetOperationResultsMqEvent>("收到GetOperationResults Mq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetOperationResultsMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetOperationResultsMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetOperationResults) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetDrivesMqEvent>("收到GetDrives Mq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetDrivesMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetDrivesMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetDrives)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetLocalIpsMqEvent>("收到GetLocalIps Mq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetLocalIpsMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetLocalIpsMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetLocalIps)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetSpeedMqEvent>("收到GetSpeedMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (message.Requests == null || message.Requests.Length == 0) { return; } if (IsTooOld(message.Timestamp)) { return; } foreach (var request in message.Requests) { ServerRoot.IfStudioClientTestIdLogElseNothing(request.StudioId, nameof(GetSpeedMqEvent)); foreach (var clientId in request.ClientIds.Where(a => TryGetByClientId(a, out _))) { if (!IsOwnerBy(clientId, request.LoginName, message.Timestamp)) { continue; } ServerRoot.IfMinerClientTestIdLogElseNothing(clientId, nameof(GetSpeedMqEvent)); SendToMinerClientAsync(clientId, new WsMessage(message.MessageId, WsMessage.GetSpeed)); } } #endregion }, this.GetType()); VirtualRoot.BuildEventPath <EnableRemoteDesktopMqEvent>("收到EnableRemoteDesktopMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(EnableRemoteDesktopMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(EnableRemoteDesktopMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.EnableRemoteDesktop)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <BlockWAUMqEvent>("收到BlockWAUMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(BlockWAUMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(BlockWAUMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.BlockWAU)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SetVirtualMemoryMqEvent>("收到SetVirtualMemoryMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SetVirtualMemoryMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SetVirtualMemoryMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SetVirtualMemory) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SetLocalIpsMqEvent>("收到SetLocalIpsMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SetLocalIpsMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SetLocalIpsMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SetLocalIps) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SwitchRadeonGpuMqEvent>("收到SwitchRadeonGpuMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SwitchRadeonGpuMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SwitchRadeonGpuMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SwitchRadeonGpu) { Data = message.On }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetSelfWorkLocalJsonMqEvent>("收到GetSelfWorkLocalJsonMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetSelfWorkLocalJsonMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetSelfWorkLocalJsonMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetSelfWorkLocalJson)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SaveSelfWorkLocalJsonMqEvent>("收到SaveSelfWorkLocalJsonMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SaveSelfWorkLocalJsonMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SaveSelfWorkLocalJsonMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SaveSelfWorkLocalJson) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <GetGpuProfilesJsonMqEvent>("收到GetGpuProfilesJsonMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(GetGpuProfilesJsonMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(GetGpuProfilesJsonMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.GetGpuProfilesJson)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SaveGpuProfilesJsonMqEvent>("收到SaveGpuProfilesJsonMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SaveGpuProfilesJsonMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SaveGpuProfilesJsonMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SaveGpuProfilesJson) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <SetAutoBootStartMqEvent>("收到SetAutoBootStartMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(SetAutoBootStartMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(SetAutoBootStartMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.SetAutoBootStart) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <RestartWindowsMqEvent>("收到RestartWindowsMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(RestartWindowsMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(RestartWindowsMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.RestartWindows)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <ShutdownWindowsMqEvent>("收到ShutdownWindowsMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(ShutdownWindowsMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(ShutdownWindowsMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.ShutdownWindows)); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <UpgradeNTMinerMqEvent>("收到UpgradeNTMinerMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(UpgradeNTMinerMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(UpgradeNTMinerMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.UpgradeNTMiner) { Data = message.Data }); #endregion }, this.GetType()); // WsServer节点和WebApiServer节点都订阅了该消息,WsServer节点只处理非作业消息,WebApiServer节点只处理作业消息 VirtualRoot.BuildEventPath <StartMineMqEvent>("收到StartMineMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(StartMineMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(StartMineMqEvent)); Guid workId = message.Data; // 只处理非作业的 if (workId == Guid.Empty) { SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.StartMine) { Data = new WorkRequest { WorkId = workId, WorkerName = string.Empty, LocalJson = string.Empty, ServerJson = string.Empty } }); } #endregion }, this.GetType()); // WebApiServer节点订阅了StartMineMqMessage消息,当StartMineMqMessage消息是作业消息时WebApiServer节点重新广播StartWorkMineMqMessage消息 VirtualRoot.BuildEventPath <StartWorkMineMqEvent>("收到StartWorkMineMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(StartWorkMineMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(StartWorkMineMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.StartMine) { Data = message.Data }); #endregion }, this.GetType()); VirtualRoot.BuildEventPath <StopMineMqEvent>("收到StopMineMq消息后检查是否是应由本节点处理的消息,如果是则处理,否则忽略", LogEnum.None, path: message => { #region if (!IsOwnerBy(message.ClientId, message.LoginName, message.Timestamp)) { return; } ServerRoot.IfMinerClientTestIdLogElseNothing(message.ClientId, nameof(StopMineMqEvent)); ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(StopMineMqEvent)); SendToMinerClientAsync(message.ClientId, new WsMessage(message.MessageId, WsMessage.StopMine)); #endregion }, this.GetType()); }
public ClientDataSet( IMinerDataRedis minerRedis, IClientActiveOnRedis clientActiveOnRedis, ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender) : base(isPull: false, getDatas: callback => { var getMinerDatasTask = minerRedis.GetAllAsync(); var getClientActiveOnsTask = clientActiveOnRedis.GetAllAsync(); var getSpeedDatasTask = speedDataRedis.GetAllAsync(); Task.WhenAll(getMinerDatasTask, getClientActiveOnsTask, getSpeedDatasTask).ContinueWith(t => { NTMinerConsole.UserInfo($"从redis加载了 {getMinerDatasTask.Result.Count} 条MinerData,和 {getClientActiveOnsTask.Result.Count} 条ClientActiveOn,和 {getSpeedDatasTask.Result.Count} 条SpeedData"); Dictionary <Guid, SpeedData> speedDataDic = getSpeedDatasTask.Result; Dictionary <string, DateTime> clientActiveOnDic = getClientActiveOnsTask.Result; Dictionary <string, ClientData> clientDataDic = new Dictionary <string, ClientData>(); DateTime speedOn = DateTime.Now.AddMinutes(-3); foreach (var minerData in getMinerDatasTask.Result) { var clientData = ClientData.Create(minerData); if (clientActiveOnDic.TryGetValue(minerData.Id, out DateTime activeOn)) { clientData.MinerActiveOn = activeOn; } clientDataDic.Add(clientData.Id, clientData); if (speedDataDic.TryGetValue(minerData.ClientId, out SpeedData speedData) && speedData.SpeedOn > speedOn) { clientData.Update(speedData, out bool _); } } List <string> clientActiveOnToRemoves = new List <string>(); foreach (var minerId in clientActiveOnDic.Keys) { if (!clientDataDic.ContainsKey(minerId)) { clientActiveOnToRemoves.Add(minerId); } } if (clientActiveOnToRemoves.Count != 0) { clientActiveOnRedis.DeleteAsync(clientActiveOnToRemoves.ToArray()); } Guid[] speedDataToRemoves = speedDataDic.Values.Where(a => a.SpeedOn < speedOn).Select(a => a.ClientId).ToArray(); if (speedDataToRemoves.Length != 0) { speedDataRedis.DeleteByClientIdsAsync(speedDataToRemoves); } callback?.Invoke(clientDataDic.Values); }); }) { _minerRedis = minerRedis; _clientActiveOnRedis = clientActiveOnRedis; _speedDataRedis = speedDataRedis; _mqSender = mqSender; VirtualRoot.BuildEventPath <Per100MinuteEvent>("周期将矿机的MinerActiveOn或NetActiveOn时间戳持久到redis", LogEnum.DevConsole, this.GetType(), PathPriority.Normal, message => { var minerCients = _dicByObjectId.Values.ToArray(); DateTime time = message.BornOn.AddSeconds(-message.Seconds); int count = 0; foreach (var minerClient in minerCients) { // 如果活跃则更新到redis,否则就不更新了 DateTime activeOn = minerClient.GetActiveOn(); if (activeOn > time) { clientActiveOnRedis.SetAsync(minerClient.Id, activeOn); count++; } } NTMinerConsole.DevWarn($"{count.ToString()} 条活跃矿机的时间戳被持久化"); }); // 上面的持久化时间戳到redis的目的主要是为了下面那个周期找出不活跃的矿机记录删除掉的逻辑能够在重启WebApiServer服务进程后不中断 VirtualRoot.BuildEventPath <Per2MinuteEvent>("周期找出用不活跃的矿机记录删除掉", LogEnum.DevConsole, this.GetType(), PathPriority.Normal, message => { var clientDatas = _dicByObjectId.Values.ToArray(); Dictionary <string, List <ClientData> > dicByMACAddress = new Dictionary <string, List <ClientData> >(); DateTime minerClientExpireTime = message.BornOn.AddDays(-7); // 因为SpeedData尺寸较大,时效性较短,可以比CientData更早删除 DateTime minerSpeedExpireTime = message.BornOn.AddMinutes(-3); int count = 0; foreach (var clientData in clientDatas) { DateTime activeOn = clientData.GetActiveOn(); // 如果7天都没有活跃了 if (activeOn <= minerClientExpireTime) { RemoveByObjectId(clientData.Id); count++; } if (activeOn <= minerSpeedExpireTime && activeOn >= minerSpeedExpireTime.AddSeconds(-message.Seconds - 10)) { _speedDataRedis.DeleteByClientIdAsync(clientData.ClientId); } if (!string.IsNullOrEmpty(clientData.MACAddress)) { if (!dicByMACAddress.TryGetValue(clientData.MACAddress, out List <ClientData> list)) { list = new List <ClientData>(); dicByMACAddress.Add(clientData.MACAddress, list); } list.Add(clientData); } } if (count > 0) { NTMinerConsole.DevWarn($"{count.ToString()} 条不活跃的矿机记录被删除"); } List <string> toRemoveIds = new List <string>(); foreach (var kv in dicByMACAddress) { if (kv.Value.Count > 1) { toRemoveIds.AddRange(kv.Value.Select(a => a.Id)); } } if (toRemoveIds.Count > 0) { RemoveByObjectIds(toRemoveIds); NTMinerConsole.DevWarn($"{toRemoveIds.Count.ToString()} 条MAC地址重复的矿机记录被删除"); } NTMinerConsole.DevDebug($"QueryClients平均耗时 {AverageQueryClientsMilliseconds.ToString()} 毫秒"); }); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <SpeedDatasMqEvent>("收到SpeedDatasMq消息后更新ClientData内存", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (message.ClientIdIps == null || message.ClientIdIps.Length == 0) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(SpeedDatasMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } speedDataRedis.GetByClientIdsAsync(message.ClientIdIps.Select(a => a.ClientId).ToArray()).ContinueWith(t => { if (t.Result != null && t.Result.Length != 0) { foreach (var item in t.Result) { string minerIp = string.Empty; var clientIdIp = message.ClientIdIps.FirstOrDefault(a => a.ClientId == item.ClientId); if (clientIdIp != null) { minerIp = clientIdIp.MinerIp; } ReportSpeed(item.SpeedDto, minerIp, isFromWsServerNode: true); } } }); }); VirtualRoot.BuildEventPath <MinerClientWsClosedMqEvent>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerClientWsClosedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = false; } }); VirtualRoot.BuildEventPath <MinerClientsWsBreathedMqEvent>("收到MinerClientsWsBreathedMq消息后更新NetActiveOn", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerClientsWsBreathedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (message.ClientIds != null && message.ClientIds.Length != 0) { foreach (var clientId in message.ClientIds) { if (_dicByClientId.TryGetValue(clientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } } } }); VirtualRoot.BuildEventPath <MinerSignsSetedMqEvent>("更新内存中的MinerData的MinerSign部分", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { if (message.Data != null && message.Data.Length != 0) { foreach (var minerSign in message.Data) { if (_dicByObjectId.TryGetValue(minerSign.Id, out ClientData clientData)) { string oldLoginName = clientData.LoginName; string newLoginName = minerSign.LoginName; clientData.Update(minerSign); UpdateClientDatasCache(oldLoginName, newLoginName, clientData); } else { clientData = ClientData.Create(minerSign); Add(clientData); } clientData.NetActiveOn = DateTime.Now; clientData.IsOnline = true; clientData.IsOuterUserEnabled = true; } } }); VirtualRoot.BuildCmdPath <QueryClientsForWsMqCommand>(this.GetType(), LogEnum.None, path: message => { ServerRoot.IfStudioClientTestIdLogElseNothing(message.StudioId, nameof(QueryClientsForWsMqCommand)); QueryClientsResponse response = AppRoot.QueryClientsForWs(message.Query); _mqSender.SendResponseClientsForWs(message.AppId, message.LoginName, message.StudioId, message.SessionId, message.MqMessageId, response); }); VirtualRoot.BuildCmdPath <AutoQueryClientsForWsMqCommand>(this.GetType(), LogEnum.None, path: message => { if (message.Queries != null && message.Queries.Length != 0) { foreach (var query in message.Queries) { ServerRoot.IfStudioClientTestIdLogElseNothing(query.StudioId, nameof(QueryClientsForWsMqCommand)); } QueryClientsResponseEx[] responses = AppRoot.QueryClientsForWs(message.Queries); _mqSender.SendResponseClientsForWs(message.AppId, message.MqMessageId, responses); } }); }