/*
* 作者:吴隆健
* 日期: 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;
}
}
}
}