feat: initial commit - HJHA game server full source

6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

View File

@ -0,0 +1,23 @@
/*
* 由SharpDevelop创建。
* 用户: Administrator
* 日期: 2018-12-22
* 时间: 13:56
*
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
*/
using System;
namespace Server
{
/// <summary>
/// 投递结果
/// </summary>
public class DeliveryResult
{
/// <summary>
/// 0表示没有找到这个模块 1表示投递成功 2表示投递失败
/// </summary>
public int style;
}
}

View File

@ -0,0 +1,112 @@
/*
* 由SharpDevelop创建。
* 用户: Administrator
* 日期: 2018-10-30
* 时间: 16:00
*
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
*/
using System;
using GameData;
using MrWu.Debug;
using MrWu.RabbitMQ;
using Server.Pack;
namespace Server.MQ {
/// <summary>
///
/// </summary>
public class GameFilter : IMQFilter {
string IMQFilter.Name {
get {
return "GameFilter";
}
}
/// <summary>
///
/// </summary>
public Action<PackHead, string> SocketMessageHandler {
get;
}
/// <summary>
///
/// </summary>
public readonly ServerNode myUtil;
/// <summary>
///
/// </summary>
/// <param name="socketMessageHandler"></param>
/// <param name="myUtil"></param>
public GameFilter(Action<PackHead, string> socketMessageHandler, ServerNode myUtil) {
SocketMessageHandler = socketMessageHandler;
this.myUtil = myUtil;
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
public bool Filter(Message message, Recv2ReturnDescription msgDesc) {
var jsondata = JsonPack.GetPackHead(message.bodystr, out PackHead head);
if (head == null)
return false;
if (myUtil.Equals(head.gameNode)) { //是本游戏 直接发送
return true;
} else { //不是本游戏 转发给别的Socket
Debug.Error("这个包没带游戏标志:" + head.packlx);
return false;
}
//return head?.packlx == GamePackAgreement.TransFerPack;
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
public object HandleMessage(Message message, Recv2ReturnDescription msgDesc) {
var jsondata = JsonPack.GetPackHead(message.bodystr, out PackHead head);
head.packlx = head.transfer;
SocketMessageHandler(head, jsondata);
return null;
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
public bool CallFilter(string msg, Recv2ReturnDescription msgDesc) {
return false;
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
public object HandleCallMessage(string msg, Recv2ReturnDescription msgDesc) {
throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public bool FilterCallback(Message message) {
return false;
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void HandleCallbackMessage(Message message) {
throw new NotImplementedException();
}
}
}

1359
GlobalSever/MQ/GlobalMQ.cs Normal file
View File

@ -0,0 +1,1359 @@
/*
* 由SharpDevelop创建。
* 用户: Administrator
* 日期: 2018-10-30
* 时间: 16:00
*
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using GameData;
using GameDAL;
using MrWu.Debug;
using MrWu.RabbitMQ;
using RabbitMQ.Client;
using Server.Data.Module;
using Server.Module;
using Server.MQ;
using Server.Pack;
namespace Server.MQ
{
/*
* 设计方案
*
* exit_game_1 : 提示玩家网络故障,踢出玩家所在的游戏
*
*
*
* */
/// <summary>
/// 消息管理 工厂
/// </summary>
public abstract class IGlobalFactory
{
/// <summary>
/// 创建消息管理
/// </summary>
public abstract void CreateGlobalMQ();
}
/// <summary>
/// rabbit消息投递管理类
/// </summary>
public class GlobalMQ : IGlobalMQ
{
/// <summary>
/// 消息管理工厂
/// </summary>
public class Factory : IGlobalFactory
{
/// <summary>
/// 创建消息管理类
/// </summary>
public override void CreateGlobalMQ()
{
if (instance == null)
instance = new GlobalMQ();
}
}
/// <summary>
/// rabbit消息投递管理类单例
/// </summary>
public static GlobalMQ instance { get; protected set; }
/// <summary>
/// 失败原因的 key 消息未发送成功(502) 消息未被路由(503) 消息被扔弃(504)
/// </summary>
public const string failCode = "failCode";
/// <summary>
/// 应该由谁来处理这个未被路由或被扔弃的包
/// </summary>
public const string doFailKey = "doFail";
/// <summary>
/// 应该由哪个节点来处理扔弃的包
/// </summary>
public const string doFailNodeNum = "doFailNodeNum";
/// <summary>
/// 处理失败的标记-有标记的才会处理,没有的会被扔弃
/// </summary>
public const string failTagKey = "failTag";
/// <summary>
/// 发送成功要做的事
/// </summary>
public const string successTagKey = "successTag";
/// <summary>
/// 构造
/// </summary>
protected GlobalMQ()
{
}
/// <summary>
/// 消息管理
/// </summary>
protected RabbitManager rabbit { get; private set; }
/// <summary>
/// rpc客户端
/// </summary>
public RpcClient rpcClient { get; private set; }
/// <summary>
///
/// </summary>
/// <returns></returns>
public int GetNoAckCount()
{
if (rabbit == null)
return 0;
return rabbit.NoAckCount;
}
/// <summary>
/// 发送完成事件
/// </summary>
/// <param name="msg"></param>
public virtual bool SendOK(Message msg)
{
return true;
}
/// <summary>
/// 发送失败
/// </summary>
/// <param name="msg"></param>
public virtual void SendFail(Message msg)
{
return;
if (msg.userData == null || !msg.userData.ContainsKey(failCode)
|| !msg.userData.ContainsKey(failTagKey) || !msg.userData.ContainsKey(doFailKey))
{
if (!msg.noAck && msg.deliveryTag > 0)
msg.Ack();
return;
}
try
{
string dofailTag = msg.userData[failTagKey].ToString();
//switch(dofailTag){
// };
msg.Ack();
Debug.Log("处理发送失败的消息:" + dofailTag + "," + msg);
return;
}
catch (Exception e)
{
Debug.Error("处理发送失败消息时出现错误,msg:{0},exception:{1}", msg, e.ToString());
msg.NAck();
return;
}
}
/// <summary>
/// 链接阻塞
/// </summary>
public void ConnectionBlocked()
{
//if (rabbit == null)
// return;
//rabbit.ConnectionBlocked();
}
/// <summary>
/// 解除链接阻塞
/// </summary>
public void ConnectionUnBlocked()
{
//if (rabbit == null)
// return;
//rabbit.ConnectionUnBlocked();
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="msg">消息</param>
/// <param name="isAutoFree">是否自动释放</param>
public virtual void SendMessage(Message msg, bool isAutoFree)
{
//CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.One_Void);//
rabbit.BasicPublish(msg, isAutoFree);
}
/// <summary>
/// 发送消息并要得到发送结果
/// </summary>
/// <param name="msg"></param>
///<param name="isAutoFree">是否自动释放</param>
public virtual bool SendMessage_Result(Message msg, bool isAutoFree)
{
// return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.One_Bool);//
return rabbit.BasicPublish_Result(msg, isAutoFree);
}
private ModuleUtile myUtile;
/// <summary>
/// rabbit消息管理启动
/// </summary>
/// <param name="config">rabbit配置</param>
/// <param name="utile">模块</param>
/// <param name="DelExists">是否删除已存在的路由,大升级要使用到,路由规则已改</param>
/// <returns>启动成功还是失败</returns>
public bool StartModule(RabbitConfig config, ModuleUtile utile, bool DelExists = false)
{
if (rabbit != null)
return false;
myUtile = utile;
RountingManager.instance.Start((ModuleType)myUtile.id, myUtile.nodeNum);
AddConfig(config, utile, DelExists);
rabbit = new RabbitManager();
rabbit.SendResult += SendResult;
rabbit.notRoutingOk += NotRoutingOk;
rabbit.Start(config);
rpcClient = rabbit.CreateRpcClient();
CreateConsumer();
return true;
}
/// <summary>
/// 添加配置
/// </summary>
/// <param name="config">配置</param>
/// <param name="utile">当前模块</param>
/// <param name="delExists">是否声明前先删除模块</param>
public virtual void AddConfig(RabbitConfig config, ModuleUtile utile, bool delExists)
{
string exbak = "Expired"; //过期的交换机 过期的队列
ExchangeInfo[] exchangeInfos = new ExchangeInfo[3];
ExchangeInfo info = new ExchangeInfo();
info.name = RountingManager.instance.clubsterRouteName;
info.type = "topic";
info.durable = true;
info.autodelete = false;
info.delExists = delExists;
// info.alternate_exchange = exbak;
exchangeInfos[0] = info;
info = new ExchangeInfo();
info.name = RountingManager.instance.clusterAllRouteName;
info.type = "fanout";
info.durable = true;
info.autodelete = false;
info.delExists = delExists;
// info.alternate_exchange = exbak;
exchangeInfos[1] = info;
info = new ExchangeInfo();
info.name = RountingManager.instance.clusterNodeOne;
info.type = "direct";
info.durable = true;
info.autodelete = false;
info.delExists = delExists;
// info.alternate_exchange = exbak;
exchangeInfos[2] = info;
config.exchanges = exchangeInfos;
QueueInfo[] queueinfos = new QueueInfo[2];
QueueInfo queue = new QueueInfo();
queue.name = RountingManager.instance.queueNode;
queue.durable = true;
queue.exclusive = false;
queue.startClear = false;
queue.delExists = delExists;
queue.autodelete = false;
queue.x_message_ttl = 0;
// queue.x_dead_letter_exchange = exbak;
// queue.x_dead_routing_key = "expire";
queueinfos[0] = queue;
queue = new QueueInfo();
queue.name = RountingManager.instance.queueEveryOne;
queue.durable = true;
queue.exclusive = false;
queue.startClear = false;
queue.delExists = delExists;
queue.autodelete = false;
queue.x_message_ttl = 0;
// queue.x_dead_letter_exchange = exbak;
// queue.x_dead_routing_key = "expire";
queueinfos[1] = queue;
config.queues = queueinfos;
BindInfo[] bindInfos = new BindInfo[6];
BindInfo bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.queueNode;
bindInfo.source = RountingManager.instance.clusterAllRouteName;
bindInfo.routingkey = RountingManager.instance.routeallkey;
bindInfo.bindType = "queue";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[0] = bindInfo;
bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.queueNode;
bindInfo.source = RountingManager.instance.clusterNodeOne;
bindInfo.routingkey = RountingManager.instance.nodeRouteKey;
bindInfo.bindType = "queue";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[1] = bindInfo;
bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.queueEveryOne;
bindInfo.source = RountingManager.instance.clubsterRouteName;
bindInfo.routingkey = RountingManager.instance.routeEveryOneKey;
bindInfo.bindType = "queue";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[2] = bindInfo;
bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.clubsterRouteName;
bindInfo.source = RountingManager.AllServerRouteName;
bindInfo.routingkey = RountingManager.instance.routeallkey;
bindInfo.bindType = "exchange";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[3] = bindInfo;
bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.clusterAllRouteName;
bindInfo.source = RountingManager.instance.clubsterRouteName;
bindInfo.routingkey = RountingManager.instance.routeallkey;
bindInfo.bindType = "exchange";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[4] = bindInfo;
bindInfo = new BindInfo();
bindInfo.destination = RountingManager.instance.clusterNodeOne;
bindInfo.source = RountingManager.instance.clubsterRouteName;
bindInfo.routingkey = RountingManager.instance.nodeRouteMatch;
bindInfo.bindType = "exchange";
Debug.Info("绑定信息,目标:" + bindInfo.destination + ",源:" + bindInfo.source + ",路由键:" + bindInfo.routingkey);
bindInfos[5] = bindInfo;
config.binds = bindInfos;
}
/// <summary>
/// 是否开启接收消息日志
/// </summary>
public static bool openMsgLog;
/// <summary>
/// 接收到消息
/// </summary>
/// <param name="msg"></param>
public virtual void ReciveMessage(Message msg)
{
if (openMsgLog)
Debug.Log("接收到消息:" + msg.ToString());
if (msg.userData != null && msg.userData.ContainsKey(failCode))
{
//有错误信息的消息,处理错误
SendFail(msg);
MessagePool.PutMessage(ref msg);
}
else
{
//正常收包
AddMessage(msg);
}
}
/// <summary>
/// 未被路由的消息 事件过来的
/// </summary>
/// <param name="msg">消息</param>
protected void NotRoutingOk(Message msg)
{
return;
DoFail(msg, 503);
}
/// <summary>
/// 分发处理失败的消息
/// </summary>
/// <param name="msg"></param>
/// <param name="FailCode"></param>
protected void DoFail(Message msg, int FailCode)
{
try
{
if (!msg.userData.ContainsKey(failTagKey)) //没有标记不处理
return;
if (msg.userData.ContainsKey(failCode))
{
//已经有错误标记,说明已经进入了此流程-并投递给别人使用,当时又出了问题避免死循环不再处理,记录日志
Debug.Error("消息发送失败,并未被处理.failCode:{0},msg:{1}", msg.userData[failCode], msg);
return;
}
//先赋值失败原因 未发送成功
msg.userData[failCode] = FailCode;
bool isMyFail = false;
if (msg.userData.ContainsKey(doFailKey))
{
//没赋值处理失败的人就是自己处理
object tmp = msg.userData[doFailKey];
int id = (int)tmp;
if (id == myUtile.id)
{
//是本模块处理的
if (msg.userData.ContainsKey(doFailNodeNum))
{
//如果指定了处理的节点
int nodenum = (int)msg.userData[doFailNodeNum];
if (nodenum == myUtile.id)
isMyFail = true;
}
else
isMyFail = true;
if (isMyFail) //处理发送失败的错误
SendFail(msg);
}
}
if (!isMyFail)
{
//投递给处理失败的模块队列
//待补待补
//处理错误模块的id
int nodeid = (int)msg.userData[doFailKey];
bool isNAck = false;
if (msg.userData.ContainsKey(doFailNodeNum))
{
//指定了节点-投递给指定节点
int nodenum = (int)msg.userData[doFailNodeNum];
msg.SendResult = null;
if (!DeliveryOne_Result(new ServerNode(nodeid, nodenum), msg, false))
{
//发不出去就拒收,下次再处理
isNAck = true;
}
}
else
{
//没指定节点,投递给任意节点
msg.SendResult = null;
if (!DeliveryEveryOne_Result(nodeid, msg, false))
{
isNAck = true;
}
}
if (!msg.noAck && msg.deliveryTag > 0)
{
if (isNAck)
msg.NAck();
else
msg.Ack();
}
}
Debug.Error("发送消息失败:" + msg.ToString());
}
catch (Exception e)
{
Debug.Error("分发处理任务错误时失败:" + e.ToString());
}
}
/// <summary>
/// 发送结果 事件过来的
/// </summary>
/// <param name="msg">消息</param>
protected void SendResult(Message msg)
{
if (msg.success)
{
//发送成功
if (msg.userData == null || !msg.userData.ContainsKey(successTagKey))
{
//没有标记 不处理
return;
}
SendOK(msg);
}
else
{
//发送失败
DoFail(msg, 502);
}
}
/// <summary>
/// 单独的消费者
/// </summary>
private Consumer exclusiveConsumer;
/// <summary>
/// 共享的消费者
/// </summary>
private Consumer sharedConsumer;
/// <summary>
/// 处理错误消息的消费者
/// </summary>
private Consumer doFailConsumer;
/// <summary>
/// 过期的消息队列
/// </summary>
private const string exipreQueue = "expired";
/// <summary>
/// 创建消费者
/// </summary>
public virtual void CreateConsumer()
{
//消费者有2 独有消息与共有消息
Consumer consumer = new Consumer();
consumer.noAck = false;
consumer.noLoacl = false;
consumer.queue = RountingManager.instance.queueNode;
consumer.exclusive = true;
consumer.Receive = ReciveMessage;
exclusiveConsumer = consumer;
rabbit.AddConsumer(consumer);
consumer = new Consumer();
consumer.noAck = false;
consumer.noLoacl = false;
consumer.queue = RountingManager.instance.queueEveryOne;
consumer.exclusive = false;
consumer.Receive = ReciveMessage;
//consumer.prefetchCount = 3;
sharedConsumer = consumer;
rabbit.AddConsumer(sharedConsumer);
}
/// <summary>
/// 添加错误消息的消费者
/// </summary>
public void AddFailConsumer()
{
return;
Consumer consumer = new Consumer();
consumer.noAck = false;
consumer.noLoacl = false;
consumer.queue = exipreQueue;
consumer.exclusive = false;
consumer.Receive = NotRoutingOk;
doFailConsumer = consumer;
rabbit.AddConsumer(doFailConsumer);
}
/// <summary>
/// 移出处理失败消息的消费者
/// </summary>
public void RemoveFailConsumer()
{
if (doFailConsumer != null)
rabbit.RemoveConsumer(doFailConsumer);
doFailConsumer = null;
}
/// <summary>
/// 关闭模块
/// </summary>
public void Close()
{
if (rabbit == null)
return;
rabbit.Close();
}
/// <summary>
/// 等待处理的消息
/// </summary>
private readonly ConcurrentQueue<WaitBeMsg> _waitBeMessages = new ConcurrentQueue<WaitBeMsg>();
public int GteMessageCnt()
{
return _waitBeMessages.Count;
}
/// <summary>
/// 拿一条消息
/// </summary>
/// <returns></returns>
public WaitBeMsg GetMessage()
{
if (_waitBeMessages.Count > 0 && _waitBeMessages.TryDequeue(out WaitBeMsg msg))
{
return msg;
}
return null;
}
/// <summary>
/// 添加消息
/// </summary>
/// <param name="msg"></param>
public void AddMessage(Message msg)
{
WaitBeMsg tmp = WaitBeMsgPool.GetWaiteBeMsg(msg);
_waitBeMessages.Enqueue(tmp);
}
/// <summary>
/// 添加消息
/// </summary>
/// <param name="msg"></param>
public void AddMessage(WaitBeMsg msg)
{
_waitBeMessages.Enqueue(msg);
}
/// <summary>
/// 创建消息
/// </summary>
/// <param name="jsonmessage">json整包</param>
/// <param name="failTag">失败标记</param>
/// <param name="doFail">谁来处理失败标记</param>
/// <param name="doFailNode">处理失败标记的节点</param>
/// <param name="successTag">成功标记</param>
/// <param name="userData"></param>
/// <returns></returns>
public static Message CreateMessage(string jsonmessage, string failTag = null, int doFail = -1,
int doFailNode = -1, string successTag = null, IDictionary<string, object> userData = null)
{
Message msg = MessagePool.GetMessage();
msg.bodystr = jsonmessage;
IDictionary<string, object> dic = new Dictionary<string, object>();
if (doFail != -1)
dic[doFailKey] = doFail;
if (failTag != null)
dic[failTagKey] = failTag;
if (doFailNode != -1)
dic[doFailNodeNum] = doFailNode;
if (successTag != null)
dic[successTagKey] = successTag;
msg.userData = dic;
msg.mandatory = true;
msg.userData = userData;
msg.durable = false;
return msg;
}
/// <summary>
/// 创建消息
/// </summary>
/// <param name="head">包头</param>
/// <param name="otherstring">包体字符串</param>
/// <param name="failTag">发送失败标记</param>
/// <param name="doFail">处理失败的模块</param>
/// <param name="doFailNode">处理失败的节点</param>
/// <param name="successTag">处理成功的标记</param>
/// <param name="userData"></param>
/// <returns>消息</returns>
public static Message CreateMessage(PackHead head, string otherstring, string failTag = null, int doFail = -1,
int doFailNode = -1, string successTag = null, IDictionary<string, object> userData = null)
{
head.sendutil = GlobalMQ.instance.myUtile;
string message = JsonPack.GetJsonByPack_head(head, otherstring);
return CreateMessage(message, failTag, doFail, doFailNode, successTag, userData);
}
/// <summary>
/// 创建消息
/// </summary>
/// <param name="head">包头</param>
/// <param name="data">包数据</param>
/// <param name="failTag">发送失败的标记</param>
/// <param name="doFail">处理失败的模块</param>
/// <param name="doFailNode">处理失败的节点</param>
/// <param name="successTag">处理成功的标记</param>
/// <param name="userData"></param>
/// <returns></returns>
public static Message CreateMessage(PackHead head, object data = null, string failTag = null, int doFail = -1,
int doFailNode = -1, string successTag = null, IDictionary<string, object> userData = null)
{
head.sendutil = GlobalMQ.instance.myUtile;
string message = JsonPack.GetJsonByPack(head, data);
var msg = CreateMessage(message, failTag, doFail, doFailNode, successTag, userData);
return msg;
}
#region message
/// <summary>
/// 投递消息给一个模块 并需要知道投递结果-只能知道是否投递成功,无法知道是否到达队列
/// </summary>
/// <param name="target">目标</param>
/// <param name="msg">消息</param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
/// <returns>true 表示在线并投递成功 false 表示投递失败或者目标不在线</returns>
public bool DeliveryOne_Result(ServerNode target, Message msg, bool isAutoFree, bool durable = false)
{
if (msg == null)
return false;
if (target == null)
{
Debug.Warning("target is null msg:{0}", msg);
if (isAutoFree)
MessagePool.PutMessage(ref msg);
return false;
}
if (myUtile.Equals(target))
{
//发给自己的
GlobalMQ.instance.AddMessage(msg);
return true;
}
msg.exchange = RountingManager.instance.GetClusterRoute(target.id);
msg.routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum);
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机或路由键为空6666:" + msg.bodystr);
return false;
}
//return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.One_Bool);//
return rabbit.BasicPublish_Result(msg, isAutoFree);
}
/// <summary>
/// 投递消息给一个模块 不管结果
/// </summary>
/// <param name="target"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
public virtual void DeliveryOne(ServerNode target, Message msg, bool isAutoFree, bool durable = false)
{
if (target == null)
{
Debug.Warning("target is null msg:{0}", msg);
return;
}
if (myUtile.Equals(target))
{
//发给自己的
GlobalMQ.instance.AddMessage(msg);
return;
}
msg.exchange = RountingManager.instance.GetClusterRoute(target.id);
msg.routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum);
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机或路由键为空5555:" + msg.bodystr);
return;
}
//CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.One_Void);
rabbit.BasicPublish(msg, isAutoFree);
}
/// <summary>
/// 群发给任意模块信息
/// </summary>
/// <param name="moduleId"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
/// <returns></returns>
public bool DeliveryEveryOne_Result(int moduleId, Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.instance.GetClusterRoute(moduleId);
msg.routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId);
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机或路由键为空444:" + moduleId + "," + msg.bodystr);
return false;
}
//return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree, Recv2ReturnDescription.EveryOne_Bool);
return rabbit.BasicPublish_Result(msg, isAutoFree);
}
/// <summary>
/// 群发给任意模块信息
/// </summary>
/// <param name="moduleId">目标模块id</param>
/// <param name="msg">消息内容</param>
/// <returns>没有绑定该模块则为false 绑定则为true</returns>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
public void DeliveryEveryOne(int moduleId, Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.instance.GetClusterRoute(moduleId);
msg.routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId);
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机或路由键为空333:" + msg.bodystr);
return;
}
//CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.EveryOne_Void);
rabbit.BasicPublish(msg, isAutoFree);
}
/// <summary>
/// 投递这个模块的所有节点
/// </summary>
/// <param name="moduleId"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
public bool DeliveryAll_Result(int moduleId, Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.instance.GetClusterRoute(moduleId);
msg.routingkey = RountingManager.instance.routeallkey;
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机与路由键为空!111" + msg.bodystr);
return false;
}
return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree,
Recv2ReturnDescription.All_Bool); //rabbit.BasicPublish_Result(msg, isAutoFree);
}
/// <summary>
/// 投递给这类所有模块
/// </summary>
/// <param name="moduleId">目标类模块id</param>
/// <param name="msg">消息内容</param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
public void DeliveryAll(int moduleId, Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.instance.GetClusterRoute(moduleId);
msg.routingkey = RountingManager.instance.routeallkey;
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机与路由键为空!222:" + msg.bodystr);
return;
}
//CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, isAutoFree, Recv2ReturnDescription.All_Void);
rabbit.BasicPublish(msg, isAutoFree);
}
/// <summary>
/// 投递给所有模块
/// </summary>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化处理</param>
public bool DeliverAll_Result(Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.AllServerRouteName;
msg.routingkey = RountingManager.instance.routeallkey;
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
// Debug.Error("消息的交换机或路由键为空!7777" + msg.bodystr);
return false;
}
return CheckFiltersAndPublish(rabbit.BasicPublish_Result, msg, isAutoFree,
Recv2ReturnDescription.All_Bool); //rabbit.BasicPublish_Result(msg, isAutoFree);
}
/// <summary>
/// 投递给所有模块
/// </summary>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <param name="durable">持久化</param>
public void DeliveryAll(Message msg, bool isAutoFree, bool durable = false)
{
msg.exchange = RountingManager.AllServerRouteName;
msg.routingkey = RountingManager.instance.routeallkey;
msg.durable = durable;
if (string.IsNullOrEmpty(msg.exchange) || string.IsNullOrEmpty(msg.routingkey))
{
Debug.Error("消息的交换机或路由键为空!8888:" + msg.bodystr);
return;
}
//CheckFiltersAndPublishNoReturn(rabbit.BasicPublish, msg, true, Recv2ReturnDescription.All_Void);
rabbit.BasicPublish(msg, isAutoFree);
}
/// <summary>
/// RPC Call 方法
/// </summary>
/// <param name="target">目标</param>
/// <param name="message">消息</param>
/// <param name="waitTime">等待多久</param>
/// <returns></returns>
public string Call(ServerNode target, string message, int waitTime = 3000)
{
if (target == null)
{
Debug.Warning("target is null msg:{0}", message);
return null;
}
string exchange = RountingManager.instance.GetClusterRoute(target.id);
string routingkey = RountingManager.instance.GetNodeRouteKey(target.id, target.nodeNum);
if (string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey))
{
Debug.Error("消息的交换机或路由键为 call 5555:" + message);
return null;
}
return CheckFiltersAndCall(() => rpcClient.Call(exchange, routingkey, message, waitTime), message,
Recv2ReturnDescription.Call_One_String);
}
/// <summary>
/// RPC Call 任意一个模块
/// </summary>
/// <param name="moduleId">目标id</param>
/// <param name="message">消息</param>
/// <param name="waitTime">等待多久</param>
/// <returns></returns>
public string Call_EveryOne(int moduleId, string message, int waitTime = 3000)
{
string exchange = RountingManager.instance.GetClusterRoute(moduleId);
string routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId);
if (string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey))
{
Debug.Error("消息交换或路由键为 call 6666:" + moduleId + "," + message);
return null;
}
var ret = CheckFiltersAndCall(() => rpcClient.Call(exchange, routingkey, message, waitTime), message,
Recv2ReturnDescription.Call_EveryOne_String);
return ret;
}
/// <summary>
///
/// </summary>
/// <param name="target"></param>
/// <param name="head"></param>
/// <param name="data"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public string Call(ServerNode target, PackHead head, object data = null, int waitTime = 3000)
{
string message = JsonPack.GetJsonByPack(head, data);
return Call(target, message, waitTime);
}
/// <summary>
/// Rpc call
/// </summary>
/// <param name="target"></param>
/// <param name="head"></param>
/// <param name="jsonmessage"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public string Call(ServerNode target, PackHead head, string jsonmessage, int waitTime = 3000)
{
string message = JsonPack.GetJsonByPack_head(head, jsonmessage);
return Call(target, message, waitTime);
}
/// <summary>
/// call 方法
/// </summary>
/// <param name="moduleId"></param>
/// <param name="head"></param>
/// <param name="data"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public string Call_EveryOne(int moduleId, PackHead head, object data = null, int waitTime = 3000)
{
string message = JsonPack.GetJsonByPack(head, data);
return Call_EveryOne(moduleId, message, waitTime);
}
/*
/// <summary>
///
/// </summary>
/// <param name="moduleId"></param>
/// <param name="head"></param>
/// <param name="data"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public System.Threading.Tasks.Task<string> AsyncCall_EveryOne(int moduleId, ServerHead head, object data = null, int waitTime = 3000) {
string message = JsonPack.GetJsonByPack(head, data);
return AsyncCall_EveryOne(moduleId, message, waitTime);
}
*/
/*
/// <summary>
///
/// </summary>
/// <param name="moduleId"></param>
/// <param name="message"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public System.Threading.Tasks.Task<string> AsyncCall_EveryOne(int moduleId, string message, int waitTime = 3000) {
string exchange = RountingManager.instance.GetClusterRoute(moduleId);
string routingkey = RountingManager.instance.GetEveryOneRouteKey(moduleId);
if(string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingkey)) {
Debug.Error("消息交换或路由键为 call 6666:" + message);
return null;
}
var ret = rpcClient.AsyncCall(exchange, routingkey, message, waitTime);
return ret;
}
*/
/// <summary>
/// call 方法
/// </summary>
/// <param name="moduleId"></param>
/// <param name="head"></param>
/// <param name="jsonmessage"></param>
/// <param name="waitTime"></param>
/// <returns></returns>
public string Call_EveryOne_str(int moduleId, PackHead head, string jsonmessage = null, int waitTime = 3000)
{
string message = JsonPack.GetJsonByPack_head(head, jsonmessage);
return Call_EveryOne(moduleId, message, waitTime);
}
/// <summary>
/// RPC回复
/// </summary>
/// <param name="ibp"></param>
/// <param name="message"></param>
/// <param name="userData"></param>
public void CallBack(IBasicProperties ibp, object message, IDictionary<string, object> userData)
{
Message msg = MessagePool.GetMessage();
msg.exchange = string.Empty;
msg.routingkey = ibp.ReplyTo;
msg.propertis = rabbit.GetBasicProperties();
msg.propertis.CorrelationId = ibp.CorrelationId;
msg.bodystr = JsonPack.GetJson(message);
msg.userData = userData;
Debug.Log("RPC回复:" + msg.propertis.CorrelationId);
CheckFiltersCallbackAndPublishNoReturn(rabbit.BasicPublish, msg, true, Recv2ReturnDescription.CallBack);
//rabbit.BasicPublish(msg, true);
}
#endregion
/// <summary>
/// AddFilter
/// </summary>
/// <param name="filter"></param>
public static void AddFilter(IMQFilter filter)
{
Filters.Add(filter);
}
/// <summary>
///
/// </summary>
/// <param name="filter"></param>
public static void RemoveFilter(IMQFilter filter)
{
if (Filters.Contains(filter))
{
Filters.Remove(filter);
}
}
/// <summary>
///
/// </summary>
public static ICollection<IMQFilter> Filters { get; } =
new System.Collections.ObjectModel.Collection<IMQFilter>();
/// <summary>
///
/// </summary>
/// <param name="defaultPublisher"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree"></param>
/// <param name="msgDesc"></param>
protected static void CheckFiltersAndPublishNoReturn(Action<Message, bool> defaultPublisher, Message msg,
bool isAutoFree, Recv2ReturnDescription msgDesc)
{
var filer = Filters.FirstOrDefault(p => p.Filter(msg, msgDesc));
if (filer != null)
{
filer.HandleMessage(msg, msgDesc);
MessagePool.PutMessage(ref msg);
}
else
{
defaultPublisher(msg, isAutoFree);
}
}
/// <summary>
///
/// </summary>
/// <param name="defaultPublisher"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree"></param>
/// <param name="msgDesc"></param>
protected static void CheckFiltersCallbackAndPublishNoReturn(Action<Message, bool> defaultPublisher,
Message msg, bool isAutoFree, Recv2ReturnDescription msgDesc)
{
var filer = Filters.FirstOrDefault(p => p.FilterCallback(msg));
if (filer != null)
{
filer.HandleCallbackMessage(msg);
MessagePool.PutMessage(ref msg);
}
else
{
defaultPublisher(msg, isAutoFree);
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="defaultPublisher"></param>
/// <param name="msg"></param>
/// <param name="isAutoFree"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
protected static T CheckFiltersAndPublish<T>(Func<Message, bool, T> defaultPublisher, Message msg,
bool isAutoFree, Recv2ReturnDescription msgDesc)
{
var filer = Filters.FirstOrDefault(p => p.Filter(msg, msgDesc));
if (filer != null)
{
var ret = filer.HandleMessage(msg, msgDesc);
if (ret is T value)
{
MessagePool.PutMessage(ref msg);
return value;
}
throw new Exception(
$"消息过滤器: {filer.Name} 返回值类型无效, 期待类型:{typeof(T)}, 当前类型: {ret?.GetType()}, returnResult: {msgDesc}");
}
else
{
return defaultPublisher(msg, isAutoFree);
}
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="defaultPublisher"></param>
/// <param name="msg"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
protected static T CheckFiltersAndCall<T>(Func<T> defaultPublisher, string msg, Recv2ReturnDescription msgDesc)
{
var filer = Filters.FirstOrDefault(p => p.CallFilter(msg, msgDesc));
if (filer != null)
{
var ret = filer.HandleCallMessage(msg, msgDesc);
if (ret is T value)
{
return value;
}
throw new Exception(
$"消息过滤器: {filer.Name} 返回值类型无效, 期待类型:{typeof(T)}, 当前类型: {ret?.GetType()}, returnResult: {msgDesc}");
}
else
{
return defaultPublisher();
}
}
}
/// <summary>
/// 外发到rabbit消息的过滤器
/// </summary>
public interface IMQFilter
{
/// <summary>
/// 过滤器名称
/// </summary>
string Name { get; }
/// <summary>
/// 检查此消息是否符合过滤条件
/// </summary>
/// <param name="message"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
bool Filter(Message message, Recv2ReturnDescription msgDesc);
/// <summary>
/// FilterCallback
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
bool FilterCallback(Message message);
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
void HandleCallbackMessage(Message message);
/// <summary>
/// 处理此消息
/// </summary>
/// <param name="message"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
object HandleMessage(Message message, Recv2ReturnDescription msgDesc);
/// <summary>
/// 检查RPC Call 消息是否符合过滤条件
/// </summary>
/// <param name="msg"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
bool CallFilter(string msg, Recv2ReturnDescription msgDesc);
/// <summary>
/// 处理 RPC Call 消息
/// </summary>
/// <param name="msg"></param>
/// <param name="msgDesc"></param>
/// <returns></returns>
object HandleCallMessage(string msg, Recv2ReturnDescription msgDesc);
}
/// <summary>
/// ReturnResult
/// </summary>
public enum Recv2ReturnDescription : byte
{
/// <summary>
/// EveryOne, 无返回
/// </summary>
EveryOne_Void = 1,
/// <summary>
/// EveryOne, 返回 bool
/// </summary>
EveryOne_Bool = 2,
/// <summary>
/// EveryOne, 返回 string
/// </summary>
EveryOne_String = 3,
/// <summary>
/// CallBack
/// </summary>
CallBack = 10,
/// <summary>
/// All, 无返回
/// </summary>
All_Void = 20,
/// <summary>
/// All, 返回 bool
/// </summary>
All_Bool = 21,
/// <summary>
/// One 无返回
/// </summary>
One_Void = 30,
/// <summary>
/// One, 返回 bool
/// </summary>
One_Bool = 31,
/// <summary>
/// Call EveryOne, 返回 string
/// </summary>
Call_EveryOne_String = 40,
/// <summary>
/// Call One, 返回 string
/// </summary>
Call_One_String = 41,
}
}

View File

@ -0,0 +1,44 @@
using System;
using MrWu.RabbitMQ;
namespace Server.MQ {
/// <summary>
/// 消息处理
/// </summary>
public interface IGlobalMQ {
/// <summary>
/// 发送成功
/// </summary>
/// <param name="msg">成功的消息</param>
bool SendOK(Message msg);
/// <summary>
/// 发送失败
/// </summary>
/// <param name="msg">发送失败的消息</param>
void SendFail(Message msg);
/// <summary>
/// 发送消息
/// </summary>
/// <param name="msg">消息</param>
/// <param name="isAutoFree">是否自动释放</param>
void SendMessage(Message msg,bool isAutoFree);
/// <summary>
/// 发送消息并返回发送结果
/// </summary>
/// <param name="msg"></param>
/// <param name="isAutoFree">是否自动释放</param>
/// <returns></returns>
bool SendMessage_Result(Message msg,bool isAutoFree);
/// <summary>
/// 接收消息
/// </summary>
/// <param name="msg"></param>
void ReciveMessage(Message msg);
}
}

View File

@ -0,0 +1,249 @@
/*
* 作者:吴隆健
* 日期: 2019-04-16
* 时间: 14:42
*
*/
using System;
using Server.Data.Module;
using GameData;
using Server.DB.Redis;
using System.Collections.Concurrent;
namespace Server {
/// <summary>
/// 路由管理
/// </summary>
public class RountingManager : ServerNode {
/// <summary>
///
/// </summary>
public static RountingManager instance { get; } = new RountingManager();
/// <summary>
///
/// </summary>
public string moduleName {
get;
private set;
}
/// <summary>
/// 所有模块的名称_用来发送用的
/// </summary>
public readonly ConcurrentDictionary<int, string> moduleNames = new ConcurrentDictionary<int, string>();
//------------------------------路由名称---------------------------------
/// <summary>
/// 系统群发路由
/// </summary>
public const string AllServerRouteName = "ServerAll";
private string m_clubsterRouteName;
/// <summary>
/// 集群主路由-发给本模块都需要经过此路由器
/// </summary>
public string clubsterRouteName {
get {
if (m_clubsterRouteName == null)
m_clubsterRouteName = moduleName + "." + id;
return m_clubsterRouteName;
}
}
private string m_clusterAllRouteName;
/// <summary>
/// 集群群发路由-集群路由转发而来
/// </summary>
public string clusterAllRouteName {
get {
if (m_clusterAllRouteName == null)
m_clusterAllRouteName = clubsterRouteName + ".all";
return m_clusterAllRouteName;
}
}
private string m_clusterNodeOne;
/// <summary>
/// 集群单点路由
/// </summary>
public string clusterNodeOne {
get {
if (m_clusterNodeOne == null)
m_clusterNodeOne = clubsterRouteName + ".one";
return m_clusterNodeOne;
}
}
//-------------------------队列名称----------------------------------
/// <summary>
/// 群发所有的路由键
/// </summary>
public string routeallkey = "all";
private string m_routeEveryOneKey;
/// <summary>
/// 发送给集群中的任意一个-这个通常谁处理都可以-升级时候有大用处-负载均衡
/// </summary>
public string routeEveryOneKey {
get {
if (m_routeEveryOneKey == null)
m_routeEveryOneKey = clubsterRouteName + ".everyOne";
return m_routeEveryOneKey;
}
}
private string m_nodeRouteKey;
/// <summary>
/// 路由到本模块队列的路由键
/// </summary>
public string nodeRouteKey {
get {
if (m_nodeRouteKey == null)
m_nodeRouteKey = clubsterRouteName + ".one." + nodeNum;
return m_nodeRouteKey;
}
}
private string m_nodeRouteMatch;
/// <summary>
/// 路由匹配键
/// </summary>
public string nodeRouteMatch {
get {
if (m_nodeRouteMatch == null) {
m_nodeRouteMatch = clubsterRouteName + ".one.*";
}
return m_nodeRouteMatch;
}
}
//------------------------------队列名--------------------------
private string m_queueEveryOne;
/// <summary>
/// 任意队列
/// </summary>
public string queueEveryOne {
get {
if (m_queueEveryOne == null) {
m_queueEveryOne = clubsterRouteName + ".everyOne";
}
return m_queueEveryOne;
}
}
private string m_queueNode;
/// <summary>
/// 本节点的队列名
/// </summary>
public string queueNode {
get {
if (m_queueNode == null)
m_queueNode = clubsterRouteName + ".node." + nodeNum;
return m_queueNode;
}
}
//-----------------------------其他模块路由-----------------------
private readonly ConcurrentDictionary<int, string> otherClusterRouteKey = new ConcurrentDictionary<int, string>();
/// <summary>
/// 获取某集群路由
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
public string GetClusterRoute(int moduleId) {
string key = null;
if (!otherClusterRouteKey.TryGetValue(moduleId, out key)) {
key = GetModuleName(moduleId);
if (!string.IsNullOrEmpty(key)) {
key = key + "." + moduleId;
otherClusterRouteKey.TryAdd(moduleId, key);
}
}
return key;
}
//----------------------------=其他模块的路由键-------------------
private readonly ConcurrentDictionary<int, string> otherEveryOneRouteKey = new ConcurrentDictionary<int, string>();
/// <summary>
/// 任意节点路由键
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
public string GetEveryOneRouteKey(int moduleId) {
string key = null;
if (!otherEveryOneRouteKey.TryGetValue(moduleId, out key)) {
key = GetModuleName(moduleId);
if (!string.IsNullOrEmpty(key)) {
key = key + "." + moduleId + ".everyOne";
otherEveryOneRouteKey.TryAdd(moduleId, key);
}
}
return key;
}
private readonly ConcurrentDictionary<int, string> otherNodeRouteKey = new ConcurrentDictionary<int, string>();
/// <summary>
/// 获取某个节点的路由键
/// </summary>
/// <param name="moduleId"></param>
/// <param name="nodeNum"></param>
/// <returns></returns>
public string GetNodeRouteKey(int moduleId, int nodeNum) {
string key = null;
int pwd = moduleId * 1000 + nodeNum;
if (!otherNodeRouteKey.TryGetValue(pwd, out key)) {
key = GetModuleName(moduleId);
if (!string.IsNullOrEmpty(key)) {
key = key + "." + moduleId + ".one." + nodeNum;
otherNodeRouteKey.TryAdd(pwd, key);
}
}
return key;
}
/// <summary>
/// 启动
/// </summary>
public void Start(ModuleType type, int nodeNum) {
this.id = (int)type;
this.nodeNum = nodeNum;
this.moduleName = Enum.GetName(type.GetType(), type);
AddModule(id, moduleName);
}
/// <summary>
/// 注册添加模块
/// </summary>
public void AddModule(int moduleId, string moduleName) {
if (!moduleNames.ContainsKey(moduleId)) {
moduleNames.TryAdd(moduleId, moduleName);
}
}
/// <summary>
/// 获取模块名称
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
public string GetModuleName(int moduleId) {
string result;
if (!moduleNames.TryGetValue(moduleId, out result)) {
result = redisManager.db.HashGet(RedisDictionary.moduleNames, moduleId);
}
return result;
}
}
}

View File

@ -0,0 +1,571 @@
/*
* 作者:吴隆健
* 日期: 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.发给某类模块的任意一个
* */
//拦截多开与检查断线
//提供的服务就是判断在不在线,
/// <summary>
/// 服务器生命检查
/// </summary>
public class ServerHeart : ServerNode
{
/// <summary>
///
/// </summary>
public static ServerHeart instanece {
get;
private set;
}
public bool IsSendHeart = false;
/// <summary>
///
/// </summary>
protected ServerHeart() { }
static ServerHeart() {
instanece = new ServerHeart();
}
/// <summary>
/// 模块类型
/// </summary>
public ModuleType myType {
get;
private set;
}
/// <summary>
/// 模块名称
/// </summary>
public string moduleName {
get;
private set;
}
/// <summary>
/// 未收到心跳的最长时间,超过这个事件认为是掉线
/// </summary>
private const int MaxNoGetHeart = 40;
/// <summary>
/// 1分钟不更新认为自己死亡
/// </summary>
private readonly TimeSpan heartTime = new TimeSpan(0, 0, MaxNoGetHeart);
/// <summary>
/// 1分钟不更新认为死亡 单位秒
/// </summary>
private const int dieTime = MaxNoGetHeart * 1000;
/// <summary>
/// 所有的服务器->这个数据
/// </summary>
private readonly ConcurrentDictionary<int, ClusterInfo> Servers = new ConcurrentDictionary<int, ClusterInfo>();
private string m_nodeHeartKey;
/// <summary>
/// 节点信息 key
/// </summary>
/// <returns></returns>
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}";
}
/// <summary>
/// 定时器 _ 线程定时器
/// </summary>
private Timer timer;
DistributedLock disLock = new DistributedLock(10);
/// <summary>
///
/// </summary>
/// <param name="type"></param>
/// <param name="nodeNum"></param>
/// <returns></returns>
//启动 -> 因为操作同一个库,所以必须分布式锁 -> 从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;
}
/// <summary>
/// 正常关闭模块
/// </summary>
public void Close() {
timer.Dispose();
if (heartTimer != null)
heartTimer.Dispose();
disLock.LockRun(nodeHeartKey, () => {
if (redisManager.db.KeyExists(nodeHeartKey))
redisManager.db.KeyDelete(nodeHeartKey);
});
}
private List<NodeInfo> tmpdieNode = new List<NodeInfo>();
/// <summary>
/// 定时器
/// </summary>
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<NodeInfo> checks = new List<NodeInfo>();
List<Task<TimeSpan?>> timeSpans = new List<Task<TimeSpan?>>();
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}");
}
}
}
/// <summary>
/// 添加模块
/// </summary>
/// <param name="moduleId"></param>
/// <param name="nodeNum"></param>
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());
}
}
// /// <summary>
// /// 处理心跳
// /// </summary>
// /// <param name="moduleId"></param>
// /// <param name="nodeNum"></param>
// 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());
// }
// }
// /// <summary>
// /// 来心跳
// /// </summary>
// /// <param name="moduleId"></param>
// /// <param name="nodeNum"></param>
// public Task DoHeartAsync(int moduleId, int nodeNum) {
// return Task.Run(
// () => DoHeart(moduleId, nodeNum)
// );
// }
/// <summary>
/// 某个模块是否死亡
/// </summary>
public Task<bool> IsDieAsync(int moduleId, int nodeNum) {
return Task.Run(() => IsDie(moduleId, nodeNum));
}
/// <summary>
/// 检查节点死否死亡
/// </summary>
/// <param name="moduleId"></param>
/// <param name="nodeNum"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 检查该模块是否没有一个在线
/// </summary>
/// <param name="moduleId"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 输出日志
/// </summary>
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;
}
/// <summary>
/// 移除死亡的模块
/// </summary>
/// <param name="moduleId"></param>
/// <param name="nodeNum"></param>
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));
}
});
}
/// <summary>
/// 获取所有的在线的节点
/// </summary>
/// <returns></returns>
public List<ServerNode> GetAllOnline() {
List<ServerNode> nodes = new List<ServerNode>();
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;
}
/// <summary>
/// 获取所有的在线节点
/// </summary>
/// <returns></returns>
public List<ServerNode> GetAllOnline(int moduleId) {
List<ServerNode> nodes = new List<ServerNode>();
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;
}
/// <summary>
/// 心跳
/// </summary>
/// <returns></returns>
private Task<bool> HeartAsync() {
return redisManager.db.StringSetAsync(nodeHeartKey, "online", heartTime);
}
private bool Heart()
{
return redisManager.db.StringSet(nodeHeartKey, "online", heartTime);
}
#region
/// <summary>
/// 发送心跳包定时器
/// </summary>
private Timer heartTimer;
/// <summary>
/// 12秒钟发送一次心跳包
/// </summary>
const int constheartTime = 12000;
/// <summary>
/// 心跳包
/// </summary>
private string heartPack;
/// <summary>
/// 开始发送
/// </summary>
/// <param name="heartPack"></param>
public void BeginHeart(string heartPack) {
this.heartPack = heartPack;
heartTimer = new Timer(SendHeart, null, 0, constheartTime);
}
/// <summary>
/// 更改心跳包
/// </summary>
public void ChangeHeart(string heartPack) {
this.heartPack = heartPack;
}
/// <summary>
/// 发送心跳包
/// </summary>
private void SendHeart(object state) {
if (!IsSendHeart)
{
return;
}
GlobalMQ.instance.DeliveryAll(GlobalMQ.CreateMessage(heartPack), true);
}
#endregion
/// <summary>
/// 集群信息
/// </summary>
private class ClusterInfo
{
/// <summary>
/// 模块id
/// </summary>
public int type;
/// <summary>
/// 模块名称
/// </summary>
public string ModuleName;
/// <summary>
/// 节点id
/// </summary>
public ConcurrentDictionary<int, NodeInfo> nodes = new ConcurrentDictionary<int, NodeInfo>();
}
/// <summary>
/// 节点信息
/// </summary>
private class NodeInfo
{
/// <summary>
/// 类型
/// </summary>
public int type;
/// <summary>
/// 模块名称
/// </summary>
public string ModuleName;
/// <summary>
/// 节点
/// </summary>
public int nodeNum;
/// <summary>
/// 过期的时间
/// </summary>
public long ExpireTime;
public bool isDie
{
get;
set;
}
}
}
}

257
GlobalSever/MQ/WaitBeMsg.cs Normal file
View File

@ -0,0 +1,257 @@
/*
* 由SharpDevelop创建。
* 用户: Administrator
* 日期: 2019-01-05
* 时间: 14:08
*
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
*/
using MrWu.Debug;
using MrWu.RabbitMQ;
using RabbitMQ.Client;
using Server.Pack;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using GameData;
namespace Server.MQ {
/// <summary>
/// 等待处理的消息
/// </summary>
public class WaitBeMsg {
///// <summary>
///// 查慢的原因用的,日志
///// </summary>
//public DateTime logTime;
// private ServerHead mhead;
/// <summary>
/// 消息内容
/// </summary>
public PackHead head;
/// <summary>
/// 包体数据
/// </summary>
public string jsondata;
/// <summary>
/// 包头+包体
/// </summary>
public string jsonmessage {
get {
return message.bodystr;
}
}
/// <summary>
/// 其他参数
/// </summary>
public List<object> pms;
/// <summary>
/// 步骤
/// </summary>
public int step = 0;
/// <summary>
/// 消息
/// </summary>
public Message message;
/// <summary>
/// 是否处理完自动确认 如果不是自动确认的话,消息需要手动确认,请清理消息内存
/// </summary>
public bool AutoAck;
/// <summary>
/// 交换机
/// </summary>
public string exchange {
get {
return message.exchange;
}
}
/// <summary>
/// 路由键
/// </summary>
public string routingkey {
get {
return message.routingkey;
}
}
/// <summary>
/// 消息属性
/// </summary>
public IBasicProperties propertis {
get {
return message.propertis;
}
}
/// <summary>
/// 用户数据
/// </summary>
public IDictionary<string, object> userData {
get {
return message.userData;
}
}
/// <summary>
/// 是否不需要确认
/// </summary>
public bool noAck {
get {
return message.noAck;
}
}
/// <summary>
/// 消息唯一id
/// </summary>
public string id {
get {
return message.id;
}
}
/// <summary>
/// 消息投递标签
/// </summary>
public ulong deliveryTag {
get {
return message.deliveryTag;
}
}
/// <summary>
/// 是否被重复接收过
/// </summary>
public bool redelivered {
get {
return message.redelivered;
}
}
/// <summary>
/// 是否确认过
/// </summary>
public bool isAck {
get;
private set;
}
/// <summary>
/// 是否要继续添加到消息列表中处理
/// </summary>
public bool isAdd;
/// <summary>
/// 数据
/// </summary>
public object[] datas { get; private set; } = new object[10];
/// <summary>
/// 上次的任务
/// </summary>
public Task task;
/// <summary>
/// 确认消息
/// </summary>
/// <param name="mulitple"></param>
/// <returns></returns>
public bool Ack(bool mulitple = false) {
if (isAck) {
Debug.Warning("消息已被确认过,请勿重复确认!");
return false;
}
isAck = true;
return message.Ack(mulitple);
}
/// <summary>
/// 是否被拒绝确认
/// </summary>
/// <param name="mulitple">是否多条消息</param>
/// <param name="requeue">是否重新入列</param>
/// <returns></returns>
public bool NAck(bool mulitple = false, bool requeue = false) {
if (isAck) {
Debug.Warning("消息已被确认过,请勿重复确认!");
return false;
}
isAck = true;
return message.NAck(mulitple, requeue);
}
/// <summary>
/// 清空数据
/// </summary>
internal void Clear() {
MessagePool.PutMessage(ref message);
isAck = false;
step = 0;
isAdd = false;
this.head = null;
this.jsondata = null;
this.pms = null;
this.task = null;
int len = datas.Length;
for (int i = 0; i < len; i++) {
datas[i] = null;
}
}
/// <summary>
///
/// </summary>
public WaitBeMsg() {
//this.message = msg;
//jsondata = JsonPack.GetPackHead(msg.bodystr, out head);
}
///// <summary>
/////
///// </summary>
///// <param name="msg"></param>
///// <param name="ack"></param>
//public WaitBeMsg(BasicGetResult msg, bool ack) {
// this.deliverytag = msg.DeliveryTag;
// jsonmessage = Encoding.UTF8.GetString(msg.Body);
// jsondata = JsonPack.GetPackHead(jsonmessage, out head);
// this.exchange = msg.Exchange;
// this.routingKey = msg.Exchange;
// this.ack = ack;
//}
///// <summary>
/////
///// </summary>
///// <param name="deliverytag"></param>
///// <param name="body"></param>
///// <param name="exchange"></param>
///// <param name="routingkey"></param>
///// <param name="ack"></param>
//public WaitBeMsg(ulong deliverytag, byte[] body, string exchange, string routingkey, bool ack) {
// this.deliverytag = deliverytag;
// jsonmessage = Encoding.UTF8.GetString(body);
// jsondata = JsonPack.GetPackHead(jsonmessage, out head);
// this.exchange = exchange;
// this.routingKey = routingkey;
// this.ack = ack;
//}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Concurrent;
using MrWu.RabbitMQ;
using Server.Pack;
using GameData;
namespace Server.MQ {
/// <summary>
/// 消息池
/// </summary>
public class WaitBeMsgPool {
static WaitBeMsgPool() {
instace = new WaitBeMsgPool();
}
private WaitBeMsgPool() {
}
private static WaitBeMsgPool instace;
/// <summary>
/// 所有的消息
/// </summary>
private ConcurrentQueue<WaitBeMsg> msgs = new ConcurrentQueue<WaitBeMsg>();
/// <summary>
/// 获取消息
/// </summary>
/// <returns></returns>
public static WaitBeMsg GetWaiteBeMsg(Message msg) {
WaitBeMsg tmp = null;
if (!instace.msgs.TryDequeue(out tmp)) {
tmp = new WaitBeMsg();
}
tmp.message = msg;
tmp.AutoAck = true;
//ServerHead head = null;
tmp.jsondata = JsonPack.GetPackHead(msg.bodystr, out tmp.head);
//tmp.head = head;
return tmp;
}
/// <summary>
/// 处理消息
/// </summary>
/// <returns></returns>
public static void PutWaiteBeMsg(ref WaitBeMsg msg) {
if (msg == null)
return;
msg.Clear();
instace.msgs.Enqueue(msg);
msg = null;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB