/* * 作者:吴隆健 * 日期: 2019-04-15 * 时间: 15:27 * */ using System; using System.Collections.Concurrent; using Server.DB.Redis; using Server.Data.Module; using System.Collections.Generic; using System.Threading; using MrWu.Debug; using System.Threading.Tasks; using GameData; using Server.Core; using Server.MQ; using StackExchange.Redis; namespace Server { //发包就四种情况 /* * 1.发所有 * 2.发某类模块的所有 * 3.发给指定的 * 4.发给某类模块的任意一个 * */ //拦截多开与检查断线 //提供的服务就是判断在不在线, /// /// 服务器生命检查 /// public class ServerHeart : ServerNode { /// /// /// public static ServerHeart instanece { get; private set; } public bool IsSendHeart = false; /// /// /// protected ServerHeart() { } static ServerHeart() { instanece = new ServerHeart(); } /// /// 模块类型 /// public ModuleType myType { get; private set; } /// /// 模块名称 /// public string moduleName { get; private set; } /// /// 未收到心跳的最长时间,超过这个事件认为是掉线 /// private const int MaxNoGetHeart = 40; /// /// 1分钟不更新认为自己死亡 /// private readonly TimeSpan heartTime = new TimeSpan(0, 0, MaxNoGetHeart); /// /// 1分钟不更新认为死亡 单位秒 /// private const int dieTime = MaxNoGetHeart * 1000; /// /// 所有的服务器->这个数据 /// private readonly ConcurrentDictionary Servers = new ConcurrentDictionary(); private string m_nodeHeartKey; /// /// 节点信息 key /// /// private string nodeHeartKey { get { if (m_nodeHeartKey == null) m_nodeHeartKey = GetNodeHeartKey(moduleName, nodeNum); // "Game:LockModule:Module." + moduleName + "." + nodeNum; return m_nodeHeartKey; } } public string GetNodeHeartKey(string moduleName,int nodeNum) { return $"Game:LockModule:Module.{moduleName}.{nodeNum}"; } /// /// 定时器 _ 线程定时器 /// private Timer timer; DistributedLock disLock = new DistributedLock(10); /// /// /// /// /// /// //启动 -> 因为操作同一个库,所以必须分布式锁 -> 从redis中获取到自己这个服务器的编号 public bool Start(ModuleType type, int nodeNum) { this.myType = type; this.moduleName = Enum.GetName(type.GetType(), type); this.id = (int)type; this.nodeNum = nodeNum; //检查redis中的数据 bool start = disLock.LockRun(nodeHeartKey,() => { if (redisManager.db.KeyExists(nodeHeartKey)) return false; return Heart(); }); AddModule(id,moduleName, nodeNum); if (start) { //10秒一次 timer = new Timer(OnTime, null, 0, 10000); } return start; } /// /// 正常关闭模块 /// public void Close() { timer.Dispose(); if (heartTimer != null) heartTimer.Dispose(); disLock.LockRun(nodeHeartKey, () => { if (redisManager.db.KeyExists(nodeHeartKey)) redisManager.db.KeyDelete(nodeHeartKey); }); } private List tmpdieNode = new List(); /// /// 定时器 /// private void OnTime(Object obj) { //心跳 HeartAsync(); tmpdieNode.Clear(); //每10秒检测一次所有服务器是否掉线 try { foreach (var ser in Servers.Values) { foreach (var node in ser.nodes.Values) { if (node.type == id && node.nodeNum == nodeNum) continue; if (node.ExpireTime - TimeInfo.Instance.FrameTime < 15) //距离过期时间剩下15秒内 才去检查 { if (!tmpdieNode.Contains(node)) { tmpdieNode.Add(node); } } } } } catch (Exception e) { Debug.Error("D12E5112-817D-40C0-A8FB-CED9AC969945:" + e.ToString()); } _ = CheckDie(); } private async Task CheckDie() { if (tmpdieNode.Count <= 0) { return; } List checks = new List(); List> timeSpans = new List>(); IBatch batch = redisManager.db.CreateBatch(); for (int i = 0; i < tmpdieNode.Count; i++) { checks.Add(tmpdieNode[i]); timeSpans.Add(batch.KeyTimeToLiveAsync(GetNodeHeartKey(tmpdieNode[i].ModuleName, tmpdieNode[i].nodeNum))); } batch.Execute(); await Task.WhenAll(timeSpans); for (int i = 0; i < checks.Count; i++) { if (timeSpans[i].Result == null) { Debug.ImportantLog($"模块掉线:{checks[i].ModuleName}-{checks[i].nodeNum}"); checks[i].isDie = true; RemoveDie(checks[i].type, checks[i].nodeNum); GlobalDispatcher.Instance.DispatchNextFrame(GlobalMsgId.ModuleNodeCrash,checks[i].ModuleName); } else { checks[i].ExpireTime = TimeInfo.Instance.FrameTime + (long)timeSpans[i].Result.Value.TotalMilliseconds; //Debug.Info($"过期时间:{checks[i].ModuleName} {checks[i].nodeNum} {checks[i].ExpireTime}"); } } } /// /// 添加模块 /// /// /// public void AddModule(int moduleId,string moduleName,int nodeNum) { try { ClusterInfo ci = Servers.GetOrAdd(moduleId, new ClusterInfo() { type = moduleId, ModuleName = moduleName }); NodeInfo node = ci.nodes.GetOrAdd(nodeNum, new NodeInfo() { type = moduleId, ModuleName = moduleName, nodeNum = nodeNum, isDie = false }); node.ExpireTime = TimeInfo.Instance.FrameTime + dieTime; } catch (Exception e) { Debug.Error("1AD0CEC8-1ACB-4CEC-AB11-EB86C8A63C75:" + e.ToString()); } } // /// // /// 处理心跳 // /// // /// // /// // public void DoHeart(int moduleId, int nodeNum) { // //心跳 // try // { // ClusterInfo ci = Servers.GetOrAdd(moduleId, new ClusterInfo() // { // type = moduleId // }); // // NodeInfo node = ci.nodes.GetOrAdd(nodeNum, new NodeInfo() // { // type = moduleId, // nodeNum = nodeNum // }); // // node.LastActivityTime = TimeInfo.Instance.FrameTime; // } catch (Exception e) { // Debug.Error("1AD0CEC8-1ACB-4CEC-AB11-EB86C8A63C75:" + e.ToString()); // } // } // /// // /// 来心跳 // /// // /// // /// // public Task DoHeartAsync(int moduleId, int nodeNum) { // return Task.Run( // () => DoHeart(moduleId, nodeNum) // ); // } /// /// 某个模块是否死亡 /// public Task IsDieAsync(int moduleId, int nodeNum) { return Task.Run(() => IsDie(moduleId, nodeNum)); } /// /// 检查节点死否死亡 /// /// /// /// public bool IsDie(int moduleId, int nodeNum) { bool result = true; try { if (Servers.TryGetValue(moduleId, out ClusterInfo ci)) { if (ci.nodes.TryGetValue(nodeNum, out NodeInfo node)) { result = node.isDie; if (result) RemoveDie(moduleId, nodeNum); } } } catch (Exception e) { Debug.Error("FC703541-D8AB-4641-9618-9CB0F090FFE4:" + e.ToString()); } return result; } /// /// 检查该模块是否没有一个在线 /// /// /// public bool IsDie(int moduleId) { bool result = true; try { if (Servers.TryGetValue(moduleId, out ClusterInfo ci)) { foreach (var node in ci.nodes.Values) { if (!node.isDie) { result = false; break; } } } } catch (Exception e) { Debug.Error("78A9B0FC-9EFF-4E53-B921-F5EF328BCF5F:" + e.ToString()); } return result; } /// /// 输出日志 /// public string Log() { string str = string.Empty; str += id + "," + nodeNum + ":"; str += Environment.NewLine; try { foreach (var ser in Servers.Values) { foreach (NodeInfo node in ser.nodes.Values) { str += "在线的模块:" + node.type + "," + node.nodeNum +"," + Environment.NewLine; } } } catch (Exception e) { Debug.Error("E4BEB6EB-465A-428B-9C5D-85264A108429:" + e.ToString()); } //str += Environment.NewLine; return str; } /// /// 移除死亡的模块 /// /// /// public Task RemoveDie(int moduleId, int nodeNum, bool force = false) { //移除死掉的节点 return Task.Run(() => { bool r = false; try { ClusterInfo ci = null; if (Servers.TryGetValue(moduleId, out ci)) { NodeInfo node = null; if (ci.nodes.TryGetValue(nodeNum, out node)) { if (node.isDie && (moduleId != this.id || nodeNum != this.nodeNum)) { r = ci.nodes.TryRemove(nodeNum,out _); } else if (force) { r = ci.nodes.TryRemove(nodeNum,out _); } } } } catch (Exception e) { Debug.Error("3D910EC1-3682-44B2-A767-4FC8517BFD2D:" + e.ToString()); } if (r) { GlobalDispatcher.Instance.DispatchNextFrame(GlobalMsgId.ModuleNodeDie,new ServerNode(moduleId,nodeNum)); } }); } /// /// 获取所有的在线的节点 /// /// public List GetAllOnline() { List nodes = new List(); try { foreach (var ser in Servers.Values) { foreach (var node in ser.nodes.Values) { if (!node.isDie) nodes.Add(new ServerNode(node.type, node.nodeNum)); } } } catch (Exception e) { Debug.Error("F95EF643-7315-48A6-8D07-1606E16102D3:" + e.ToString()); } return nodes; } /// /// 获取所有的在线节点 /// /// public List GetAllOnline(int moduleId) { List nodes = new List(); try { ClusterInfo ci = null; if (Servers.TryGetValue(moduleId, out ci)) { foreach (var node in ci.nodes.Values) { if (!node.isDie) nodes.Add(new ServerNode(node.type, node.nodeNum)); } } } catch (Exception e) { Debug.Error("E1C6A85D-8F0C-47A9-8103-5AC3D7ACDF0B:" + e.ToString()); } return nodes; } /// /// 心跳 /// /// private Task HeartAsync() { return redisManager.db.StringSetAsync(nodeHeartKey, "online", heartTime); } private bool Heart() { return redisManager.db.StringSet(nodeHeartKey, "online", heartTime); } #region 心跳包发送处理 /// /// 发送心跳包定时器 /// private Timer heartTimer; /// /// 12秒钟发送一次心跳包 /// const int constheartTime = 12000; /// /// 心跳包 /// private string heartPack; /// /// 开始发送 /// /// public void BeginHeart(string heartPack) { this.heartPack = heartPack; heartTimer = new Timer(SendHeart, null, 0, constheartTime); } /// /// 更改心跳包 /// public void ChangeHeart(string heartPack) { this.heartPack = heartPack; } /// /// 发送心跳包 /// private void SendHeart(object state) { if (!IsSendHeart) { return; } GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(heartPack), true); } #endregion /// /// 集群信息 /// private class ClusterInfo { /// /// 模块id /// public int type; /// /// 模块名称 /// public string ModuleName; /// /// 节点id /// public ConcurrentDictionary nodes = new ConcurrentDictionary(); } /// /// 节点信息 /// private class NodeInfo { /// /// 类型 /// public int type; /// /// 模块名称 /// public string ModuleName; /// /// 节点 /// public int nodeNum; /// /// 过期的时间 /// public long ExpireTime; public bool isDie { get; set; } } } }