public MinerSignSet(IMinerDataRedis redis) { _redis = redis; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); redis.GetAllAsync().ContinueWith(t => { _initedOn = DateTime.Now; foreach (var item in t.Result) { _dicByClientId[item.ClientId] = MinerSign.Create(item); } IsReadied = true; stopwatch.Stop(); NTMinerConsole.UserOk($"矿机签名集就绪,耗时 {stopwatch.GetElapsedSeconds().ToString("f2")} 秒"); VirtualRoot.RaiseEvent(new MinerSignSetInitedEvent()); }); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在MinerSignSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <MinerDataRemovedMqEvent>("收到MinerClientRemovedMq消息后移除内存中对应的记录", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerDataRemovedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryRemove(message.ClientId, out MinerSign minerSign)) { if (AppRoot.MinerClientSessionSet.TryGetByClientId(minerSign.ClientId, out IMinerClientSession ntminerSession)) { ntminerSession.CloseAsync(WsCloseCode.Normal, "服务端移除了该矿机"); } } #endregion }); VirtualRoot.BuildEventPath <MinerDatasRemovedMqEvent>("收到MinerClientsRemovedMq消息后移除内存中对应的记录", LogEnum.None, this.GetType(), PathPriority.Normal, path: message => { #region if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerDataRemovedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } foreach (var clientId in message.ClientIds) { if (_dicByClientId.TryRemove(clientId, out MinerSign minerSign)) { if (AppRoot.MinerClientSessionSet.TryGetByClientId(clientId, out IMinerClientSession ntminerSession)) { ntminerSession.CloseAsync(WsCloseCode.Normal, "服务端移除了该矿机"); } } } #endregion }); VirtualRoot.BuildEventPath <Per1SecondEvent>("每秒钟将暂存的新设置的MinerSign发送到Mq", LogEnum.None, this.GetType(), PathPriority.Normal, message => { Task.Factory.StartNew(() => { MinerSign[] minerSignsSeted; lock (_lockForMinerSignsSeted) { minerSignsSeted = _minerSignsSeted.ToArray(); _minerSignsSeted.Clear(); } AppRoot.MinerClientMqSender.SendMinerSignsSeted(minerSignsSeted); }); }); }
public ClientDataSet( IMinerDataRedis minerRedis, IClientActiveOnRedis clientActiveOnRedis, ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender) : base(isPull: false, getDatas: callback => { var getMinersTask = minerRedis.GetAllAsync(); var getClientActiveOnsTask = clientActiveOnRedis.GetAllAsync(); var getSpeedsTask = speedDataRedis.GetAllAsync(); Task.WhenAll(getMinersTask, getClientActiveOnsTask, getSpeedsTask).ContinueWith(t => { NTMinerConsole.UserInfo($"从redis加载了 {getMinersTask.Result.Count} 条MinerData,和 {getSpeedsTask.Result.Count} 条SpeedData"); Dictionary <Guid, SpeedData> speedDataDic = getSpeedsTask.Result; Dictionary <string, DateTime> clientActiveOnDic = getClientActiveOnsTask.Result; List <ClientData> clientDatas = new List <ClientData>(); DateTime speedOn = DateTime.Now.AddMinutes(-3); foreach (var minerData in getMinersTask.Result) { var clientData = ClientData.Create(minerData); if (clientActiveOnDic.TryGetValue(minerData.Id, out DateTime activeOn)) { clientData.MinerActiveOn = activeOn; } clientDatas.Add(clientData); if (speedDataDic.TryGetValue(minerData.ClientId, out SpeedData speedData) && speedData.SpeedOn > speedOn) { clientData.Update(speedData, out bool _); } } callback?.Invoke(clientDatas); }); }) { _minerRedis = minerRedis; _clientActiveOnRedis = clientActiveOnRedis; _speedDataRedis = speedDataRedis; _mqSender = mqSender; VirtualRoot.BuildEventPath <Per100MinuteEvent>("周期将矿机的MinerActiveOn或NetActiveOn时间戳持久到redis", LogEnum.DevConsole, 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()} 条活跃矿机的时间戳被持久化"); }, this.GetType()); // 上面的持久化时间戳到redis的目的主要是为了下面那个周期找出不活跃的矿机记录删除掉的逻辑能够在重启WebApiServer服务进程后不中断 VirtualRoot.BuildEventPath <Per2MinuteEvent>("周期找出用不活跃的矿机记录删除掉", LogEnum.DevConsole, 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++; } else if (activeOn <= minerSpeedExpireTime) { _speedDataRedis.DeleteByClientIdAsync(clientData.ClientId); } else 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) { count = 0; foreach (var id in toRemoveIds) { RemoveByObjectId(id); count++; } NTMinerConsole.DevWarn($"{count.ToString()} 条MAC地址重复的矿机记录被删除"); } NTMinerConsole.DevDebug($"QueryClients平均耗时 {AverageQueryClientsMilliseconds.ToString()} 毫秒"); }, this.GetType()); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <SpeedDatasMqEvent>("收到SpeedDatasMq消息后更新ClientData内存", LogEnum.None, 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); } } }); }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsClosedMqEvent>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, 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; } }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientsWsBreathedMqEvent>("收到MinerClientsWsBreathedMq消息后更新NetActiveOn", LogEnum.None, 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; } } } }, this.GetType()); VirtualRoot.BuildEventPath <MinerSignSetedMqEvent>("更新内存中的MinerData的MinerSign部分", LogEnum.None, path: message => { if (_dicByObjectId.TryGetValue(message.Data.Id, out ClientData clientData)) { clientData.Update(message.Data); } else { clientData = ClientData.Create(message.Data); Add(clientData); } clientData.NetActiveOn = DateTime.Now; clientData.IsOnline = true; clientData.IsOuterUserEnabled = true; }, this.GetType()); VirtualRoot.BuildCmdPath <QueryClientsForWsMqCommand>(path: message => { QueryClientsResponse response = AppRoot.QueryClientsForWs(message.Query); _mqSender.SendResponseClientsForWs(message.AppId, message.LoginName, message.SessionId, message.MqMessageId, response); }, this.GetType(), LogEnum.None); }
public MinerSignSet(IMinerDataRedis redis) { _redis = redis; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); redis.GetAllAsync().ContinueWith(t => { _initedOn = DateTime.Now; foreach (var item in t.Result) { _dicByClientId[item.ClientId] = MinerSign.Create(item); } IsReadied = true; stopwatch.Stop(); NTMinerConsole.UserOk($"矿机签名集就绪,耗时 {stopwatch.GetElapsedSeconds().ToString("f2")} 秒"); VirtualRoot.RaiseEvent(new MinerSignSetInitedEvent()); }); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在MinerSignSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <MinerDataRemovedMqEvent>("收到MinerClientRemovedMq消息后移除内存中对应的记录", LogEnum.None, path: message => { #region if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (string.IsNullOrEmpty(message.MinerId)) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerDataRemovedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryRemove(message.ClientId, out MinerSign minerSign)) { if (AppRoot.MinerClientSessionSet.TryGetByClientId(minerSign.ClientId, out IMinerClientSession ntminerSession)) { ntminerSession.CloseAsync(WsCloseCode.Normal, "服务端移除了该矿机"); } } #endregion }, this.GetType()); VirtualRoot.BuildEventPath <MinerSignSetedMqEvent>("收到MinerSignSetedMq消息后更新内存中对应的记录", LogEnum.None, path: message => { #region if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (message.Data == null) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerSignSetedMqEvent) + ":" + MqKeyword.SafeIgnoreMessage); return; } _dicByClientId[message.Data.ClientId] = message.Data; #endregion }, this.GetType()); }