private void Add(ClientData clientData) { if (!IsReadied) { return; } if (clientData.ClientId == Guid.Empty) { return; } if (!_dicByClientId.ContainsKey(clientData.ClientId)) { _dicByClientId.Add(clientData.ClientId, clientData); if (!_dicByObjectId.ContainsKey(clientData.Id)) { _dicByObjectId.Add(clientData.Id, clientData); } var minerData = MinerData.Create(clientData); _minerRedis.SetAsync(minerData).ContinueWith(t => { _mqSender.SendMinerDataAdded(minerData.Id); }); } }
public ClientDataSet(IMinerRedis minerRedis, ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender) : base(isPull: false, getDatas: callback => { var getMinersTask = minerRedis.GetAllAsync(); var getSpeedsTask = speedDataRedis.GetAllAsync(); Task.WhenAll(getMinersTask, getSpeedsTask).ContinueWith(t => { NTMinerConsole.UserInfo($"从redis加载了 {getMinersTask.Result.Count} 条MinerData,和 {getSpeedsTask.Result.Count} 条SpeedData"); var speedDatas = getSpeedsTask.Result; List <ClientData> clientDatas = new List <ClientData>(); DateTime speedOn = DateTime.Now.AddMinutes(-3); foreach (var minerData in getMinersTask.Result) { var clientData = ClientData.Create(minerData); // 该属性没有持久化而只在内存中,启动时将该属性值视为当前日期的前一天的零时加上CreatedOn的时间,别处有个周期清理7天不活跃矿机的任务 clientData.MinerActiveOn = DateTime.Today.AddDays(-1) + minerData.CreatedOn.TimeOfDay; clientDatas.Add(clientData); var speedData = speedDatas.FirstOrDefault(a => a.ClientId == minerData.ClientId); if (speedData != null && speedData.SpeedOn > speedOn) { clientData.Update(speedData, out bool _); } } callback?.Invoke(clientDatas); }); }) { _minerRedis = minerRedis; _speedDataRedis = speedDataRedis; _mqSender = mqSender; VirtualRoot.BuildEventPath <Per1MinuteEvent>("周期清理Redis中不活跃的来自挖矿端上报的算力记录", LogEnum.DevConsole, path: message => { DateTime time = message.BornOn.AddSeconds(-130); var toRemoveSpeed = _dicByClientId.Where(a => a.Value.MinerActiveOn != DateTime.MinValue && a.Value.MinerActiveOn <= time).ToArray(); _speedDataRedis.DeleteByClientIdsAsync(toRemoveSpeed.Select(a => a.Key).ToArray()); // 删除一周没有活跃过的客户端 time = message.BornOn.AddDays(-7); var toRemoveClient = _dicByObjectId.Where(a => a.Value.MinerActiveOn <= time).ToArray(); foreach (var kv in toRemoveClient) { base.RemoveByObjectId(kv.Key); } }, this.GetType()); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <SpeedDataMqMessage>("收到SpeedDataMq消息后更新ClientData内存", LogEnum.None, path: message => { if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (message.ClientId == Guid.Empty) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(_safeIgnoreMessage); return; } speedDataRedis.GetByClientIdAsync(message.ClientId).ContinueWith(t => { ReportSpeed(t.Result.SpeedDto, message.MinerIp, isFromWsServerNode: true); }); }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsOpenedMqMessage>("收到MinerClientWsOpenedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsClosedMqMessage>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = false; } }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsBreathedMqMessage>("收到MinerClientWsBreathedMq消息后更新NetActiveOn", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.BuildCmdPath <ChangeMinerSignMqMessage>(path: message => { if (_dicByObjectId.TryGetValue(message.Data.Id, out ClientData clientData)) { clientData.Update(message.Data, out bool isChanged); if (isChanged) { var minerData = MinerData.Create(clientData); _minerRedis.SetAsync(minerData).ContinueWith(t => { _mqSender.SendMinerSignChanged(minerData.Id); }); } } else { // 此时该矿机是第一次在服务端出现 NTMinerConsole.UserWarn("该矿机首次出现于WsServer:" + VirtualRoot.JsonSerializer.Serialize(message.Data)); clientData = ClientData.Create(MinerData.Create(message.Data)); clientData.NetActiveOn = DateTime.Now; clientData.IsOnline = true; clientData.IsOuterUserEnabled = true; Add(clientData); } }, this.GetType(), LogEnum.None); }
public ClientDataSet(IMinerRedis minerRedis, ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender) : base(isPull: false, getDatas: callback => { var getMinersTask = minerRedis.GetAllAsync(); var getSpeedsTask = speedDataRedis.GetAllAsync(); Task.WhenAll(getMinersTask, getSpeedsTask).ContinueWith(t => { var speedDatas = getSpeedsTask.Result; List <ClientData> clientDatas = new List <ClientData>(); foreach (var minerData in getMinersTask.Result) { var clientData = ClientData.Create(minerData); clientDatas.Add(clientData); var speedData = speedDatas.FirstOrDefault(a => a.ClientId == minerData.ClientId); if (speedData != null) { clientData.Update(speedData); } } callback?.Invoke(clientDatas); }); }) { _minerRedis = minerRedis; _speedDataRedis = speedDataRedis; _mqSender = mqSender; // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建 VirtualRoot.AddEventPath <SpeedDataMqMessage>("收到SpeedDataMq消息后更新ClientData内存", LogEnum.None, action: message => { if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (message.ClientId == Guid.Empty) { return; } if (IsOldMqMessage(message.Timestamp)) { Write.UserOk(_safeIgnoreMessage); return; } speedDataRedis.GetByClientIdAsync(message.ClientId).ContinueWith(t => { ReportSpeed(t.Result, message.MinerIp, isFromWsServerNode: true); }); }, this.GetType()); VirtualRoot.AddEventPath <MinerClientWsOpenedMqMessage>("收到MinerClientWsOpenedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, action: message => { if (IsOldMqMessage(message.Timestamp)) { Write.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.AddEventPath <MinerClientWsClosedMqMessage>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, action: message => { if (IsOldMqMessage(message.Timestamp)) { Write.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = false; } }, this.GetType()); VirtualRoot.AddEventPath <MinerClientWsBreathedMqMessage>("收到MinerClientWsBreathedMq消息后更新NetActiveOn", LogEnum.None, action: message => { if (IsOldMqMessage(message.Timestamp)) { Write.UserOk(_safeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.AddCmdPath <ChangeMinerSignMqMessage>(action: message => { if (_dicByObjectId.TryGetValue(message.Data.Id, out ClientData clientData)) { clientData.Update(message.Data, out bool isChanged); if (isChanged) { var minerData = MinerData.Create(clientData); _minerRedis.SetAsync(minerData).ContinueWith(t => { _mqSender.SendMinerSignChanged(minerData.Id); }); } } else { Add(ClientData.Create(MinerData.Create(message.Data))); } }, this.GetType(), LogEnum.DevConsole); }
public ClientDataSet(IMinerRedis minerRedis, ISpeedDataRedis speedDataRedis, IMinerClientMqSender mqSender) : base(isPull: false, getDatas: callback => { var getMinersTask = minerRedis.GetAllAsync(); var getSpeedsTask = speedDataRedis.GetAllAsync(); Task.WhenAll(getMinersTask, getSpeedsTask).ContinueWith(t => { NTMinerConsole.UserInfo($"从redis加载了 {getMinersTask.Result.Count} 条MinerData,和 {getSpeedsTask.Result.Count} 条SpeedData"); var speedDatas = getSpeedsTask.Result; List <ClientData> clientDatas = new List <ClientData>(); DateTime speedOn = DateTime.Now.AddMinutes(-3); foreach (var minerData in getMinersTask.Result) { var clientData = ClientData.Create(minerData); // 该属性没有持久化而只在内存中,启动时将该属性值视为当前日期的前一天的零时加上CreatedOn // 的时间从而将数据分散开来从而滑动清理,后面有个周期清理7天不活跃矿机的任务。WebApiServer启动后第6天才会进行第一次清理。 clientData.MinerActiveOn = DateTime.Today.AddDays(-1) + minerData.CreatedOn.TimeOfDay; clientDatas.Add(clientData); var speedData = speedDatas.FirstOrDefault(a => a.ClientId == minerData.ClientId); if (speedData != null && speedData.SpeedOn > speedOn) { clientData.Update(speedData, out bool _); } } callback?.Invoke(clientDatas); }); }) { _minerRedis = minerRedis; _speedDataRedis = speedDataRedis; _mqSender = mqSender; VirtualRoot.BuildEventPath <Per1MinuteEvent>("周期清理Redis中不活跃的来自挖矿端上报的算力记录", LogEnum.DevConsole, path: message => { DateTime time = message.BornOn.AddSeconds(-130); var toRemoveSpeed = _dicByClientId.Where(a => a.Value.MinerActiveOn != DateTime.MinValue && a.Value.MinerActiveOn <= time).ToArray(); _speedDataRedis.DeleteByClientIdsAsync(toRemoveSpeed.Select(a => a.Key).ToArray()); // 删除一段时间没有活跃过的客户端 const int nDay = 7; DateTime netActiveOn = message.BornOn.AddSeconds(-30); time = message.BornOn.AddDays(-nDay); var toRemoveClient = _dicByObjectId.Where(a => a.Value.MinerActiveOn <= time && a.Value.NetActiveOn <= netActiveOn).ToArray(); if (toRemoveClient.Length > 0) { NTMinerConsole.UserOk($"{toRemoveClient.Length.ToString()} 台矿机因 {nDay.ToString()} 天没有活跃,删除对应记录"); foreach (var kv in toRemoveClient) { base.RemoveByObjectId(kv.Key); } } }, this.GetType()); // 收到Mq消息之前一定已经初始化完成,因为Mq消费者在ClientSetInitedEvent事件之后才会创建 VirtualRoot.BuildEventPath <SpeedDataMqMessage>("收到SpeedDataMq消息后更新ClientData内存", LogEnum.None, path: message => { if (message.AppId == ServerRoot.HostConfig.ThisServerAddress) { return; } if (message.ClientId == Guid.Empty) { return; } if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(SpeedDataMqMessage) + ":" + MqKeyword.SafeIgnoreMessage); return; } speedDataRedis.GetByClientIdAsync(message.ClientId).ContinueWith(t => { ReportSpeed(t.Result.SpeedDto, message.MinerIp, isFromWsServerNode: true); }); }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsOpenedMqMessage>("收到MinerClientWsOpenedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerClientWsOpenedMqMessage) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsClosedMqMessage>("收到MinerClientWsClosedMq消息后更新NetActiveOn和IsOnline", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerClientWsClosedMqMessage) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = false; } }, this.GetType()); VirtualRoot.BuildEventPath <MinerClientWsBreathedMqMessage>("收到MinerClientWsBreathedMq消息后更新NetActiveOn", LogEnum.None, path: message => { if (IsOldMqMessage(message.Timestamp)) { NTMinerConsole.UserOk(nameof(MinerClientWsBreathedMqMessage) + ":" + MqKeyword.SafeIgnoreMessage); return; } if (_dicByClientId.TryGetValue(message.ClientId, out ClientData clientData)) { clientData.NetActiveOn = message.Timestamp; clientData.IsOnline = true; } }, this.GetType()); VirtualRoot.BuildCmdPath <ChangeMinerSignMqMessage>(path: message => { if (_dicByObjectId.TryGetValue(message.Data.Id, out ClientData clientData)) { clientData.Update(message.Data, out bool isChanged); if (isChanged) { var minerData = MinerData.Create(clientData); _minerRedis.SetAsync(minerData).ContinueWith(t => { _mqSender.SendMinerSignChanged(minerData.Id); }); } } else { clientData = ClientData.Create(MinerData.Create(message.Data)); Add(clientData); } clientData.NetActiveOn = DateTime.Now; clientData.IsOnline = true; clientData.IsOuterUserEnabled = true; }, this.GetType(), LogEnum.None); VirtualRoot.BuildCmdPath <QueryClientsForWsMqMessage>(path: message => { QueryClientsResponse response = AppRoot.QueryClientsForWs(message.Query); _mqSender.SendResponseClientsForWs(message.AppId, message.LoginName, message.SessionId, response); }, this.GetType(), LogEnum.None); }