/* Sog 游戏基础库 2016 by zouwei */ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Org.BouncyCastle.Ocsp; using ProtoCSStruct; using Sog; namespace Chat { public static class ChatCacheOp { private static int ChannalCacheMax = 100; //单频道消息缓存数量限制 private static int SaveFileCdSec = 300; //存盘cd 单位秒 private static int SaveMaxDay = 14; //起服只读X天以内数据 public static void InitChatCacheConfig(ChatServerConfig serverConfig) { if (serverConfig == null) { TraceLog.Error("ChatCacheOp.InitChatCacheConfig error"); return; } ChannalCacheMax = serverConfig.ChannalCacheMax; SaveFileCdSec = Math.Max(300, serverConfig.SaveFileCdSec); //不要低于5分钟 SaveMaxDay = serverConfig.SaveMaxDay; } public static string GetSaveFilePath() { return "./ChannalChatCacheData_" + ChatServerUtils.GetApp().ServerID.ToString() + ".fdb"; } public static void WriteChatCacheStat() { ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; TraceLog.Stat("ChatCacheStruct total {0} used {1} rate {2}%" , chatCache.m_cacheStrutChatReq.GetTotalCount() , chatCache.m_cacheStrutChatReq.GetUsedCount() , chatCache.m_cacheStrutChatReq.GetUsedPercent()); } public static void ChatCacheTick(long nowSec) { ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; long saveCdSec = SaveFileCdSec; //停服时5s if (ChatServerUtils.GetApp().IsStopping) { saveCdSec = 5; } if (chatCache.m_lastSaveTime + saveCdSec < nowSec) { SaveCacheToFile(); chatCache.m_lastSaveTime = nowSec; WriteChatCacheStat(); } } public static int AddChatCache(ref CSChatRes res, ChatChannelType type = ChatChannelType.None) { long channelId = ChatServerData.GetChatChannelId(res.ChatChannelType, res.ChannelParam); ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; chatCache.m_needSave = true; if (channelId <= 0) { TraceLog.Error("ChatCacheOp.AddChatCache channelId {0} ChatChannelType {1} ChannelParam:{2}", channelId, res.ChatChannelType, res.ChannelParam); return -1; } if (chatCache.m_channalCacheMap.ContainsKey(channelId)) { //看有没有到单Id上限 var list = chatCache.m_channalCacheMap[channelId]; if (list.Count < ChannalCacheMax) { ref ChatCacheDataStruct cache = ref chatCache.m_cacheStrutChatReq.Malloc(); if (!cache.IsNull()) { cache.cacheData.CopyFrom(ref res); list.AddLast(cache.GetObjectID()); return 0; } } //需要顶掉老的缓存 if (list.Count > 0) { //取最早的覆盖数据后放到末尾 int index = list.First.Value; list.RemoveFirst(); ref ChatCacheDataStruct cache = ref chatCache.m_cacheStrutChatReq.GetByIndex(index); if (!cache.IsNull()) { cache.cacheData.CopyFrom(ref res); list.AddLast(index); return 0; } else { //下标有问题 TraceLog.Error("ChatCacheOp.AddChatCache not find index:{0}", index); return -1; } } TraceLog.Error("ChatCacheOp.AddChatCache list count error {0} {1}", channelId, list.Count); return -1; } else { ref ChatCacheDataStruct cache = ref chatCache.m_cacheStrutChatReq.Malloc(); if (!cache.IsNull()) { cache.cacheData.CopyFrom(ref res); LinkedList linkList = new LinkedList(); linkList.AddLast(cache.GetObjectID()); chatCache.m_channalCacheMap.Add(channelId, linkList); return 0; } else { //空间不够 TraceLog.Error("ChatCacheOp.AddChatCache not space"); return -1; } } } //获取对应id的缓存数据 public static void OnGetChannelCacheData(PlayerOnChat player, ChatChannelType channel, long channelParam, long seq) { long channelId = ChatServerData.GetChatChannelId(channel, channelParam); ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; CSChannelCacheDataRes channelChatCache = new CSChannelCacheDataRes() { ChatChannelType = channel, ChannelParam = channelParam, }; Dictionary dicChatBlack = ChatSvc.GetPlayerDicChatBlack(player); TraceLog.Trace("ChatCacheOp.OnGetChannelCacheData player {0} channel {1} {2} seq {3} dicChatBlackCount {4}", player.UserID, channel, channelParam, seq, dicChatBlack.Count); if (chatCache.m_channalCacheMap.ContainsKey(channelId)) { if (chatCache.m_channalCacheMap[channelId].Count > 0) { var list = chatCache.m_channalCacheMap[channelId]; var node = list.Last; while (node != null) { ref var cache = ref chatCache.m_cacheStrutChatReq.GetByIndex(node.Value); if (!cache.IsNull() && cache.cacheData.ChatSeq > seq && channelChatCache.ChatCache.Count < channelChatCache.ChatCache.GetMaxCount()) { long chatBlackTime = 0; if (dicChatBlack.TryGetValue(cache.cacheData.Uid, out chatBlackTime)) //找聊天缓存的话,聊天屏蔽黑名单中的,并且是屏蔽时间之后的,才进行过滤 { if (chatBlackTime > cache.cacheData.SendTime) { channelChatCache.ChatCache.Add(ref cache.cacheData); if (channelChatCache.ChatCache.Count >= channelChatCache.ChatCache.GetMaxCount()) { break; } } else { TraceLog.Trace("ChatCacheOp.OnGetChannelCacheData filter user {0} chatBlackUid {1} chatBlackTime {2} SendTime {3}", player.UserID, cache.cacheData.Uid, chatBlackTime, cache.cacheData.SendTime); } } else { channelChatCache.ChatCache.Add(ref cache.cacheData); if (channelChatCache.ChatCache.Count >= channelChatCache.ChatCache.GetMaxCount()) { break; } } } node = node.Previous; } } } PlayerPacketSender.SendToPlayer(player, (int)CSGameMsgID.ChannelCacheDataRes, ref channelChatCache); TraceLog.Trace("ChatCacheOp.OnGetChannelCacheData player {0} channel {1} {2} seq {3} realcount {4}", player.UserID, channel, channelParam, seq, channelChatCache.ChatCache.Count); } //占居内存大的话不适用 /*public static void SaveChatCache(bool forceSave = false) { ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; if ((!chatCache.m_needSave) && (!forceSave)) { return; } int needSaveNum = chatCache.m_cacheStrutChatReq.GetUsedCount(); TraceLog.Trace("ChatCache.SaveChatCache save file {0} begin time {1} needSave {2}", saveFile, AppTime.GetNowSysMs(), needSaveNum); ChannelChatCacheData needSaveData = new ChannelChatCacheData(); foreach (var idQueueKv in chatCache.m_channalCacheMap) { foreach (int index in idQueueKv.Value) { ref var dataOne = ref chatCache.m_cacheStrutChatReq.GetByIndex(index); if (!dataOne.IsNull()) { needSaveData.ChatList.Add(ref dataOne.cacheData); } } } int iRet = Sog.IO.FileDBSave.SaveToFile(ref needSaveData, saveFile); if (iRet != 0) { TraceLog.Error("ChatCache.SaveChatCache save file {0} error, ret {1}", saveFile, iRet); } if (needSaveData.ChatList.Count != needSaveNum) { TraceLog.Error("ChatCache.SaveChatCache save less need:{0} have:{1}", needSaveNum, needSaveData.ChatList.Count); } TraceLog.Trace("ChatCache.SaveChatCache save file {0} success time {1}", saveFile, AppTime.GetNowSysMs()); }*/ /*public static void LoadChatCache() { TraceLog.Trace("ChatCache.LoadChatCache load file:{0} begin time:{0}", saveFile, AppTime.GetNowSysMs()); ChannelChatCacheData data = new ChannelChatCacheData(); if (File.Exists(saveFile) == false) { TraceLog.Debug("ChatCache.LoadChatCache file not exist {0}", saveFile); return; } bool success = Sog.IO.FileDBSave.ReadFromFile(ref data, saveFile); if ( !success) { TraceLog.Error("ChatCache.LoadChatCache failed! file:{0}", saveFile); return; } for(int i = 0; i < data.ChatList.Count; ++i) { ref var chatRes = ref data.ChatList[i]; AddChatCache(ref chatRes); ChatServerUtils.GetChatServerData().SetChannalChatSeq(chatRes.ChatSeq, chatRes.ChatChannelType, chatRes.ChannelParam); } TraceLog.Trace("ChatCache.LoadChatCache load file:{0} success time:{0}", saveFile, AppTime.GetNowSysMs()); SaveChatCache(true); }*/ public static void SaveCacheToFile() { ref var chatCache = ref ChatServerUtils.GetChatServerData().m_chatCacheInfo; if (chatCache.m_needSave == false) { return; } int haveSave = 0; string saveFile = GetSaveFilePath(); TraceLog.Trace("ChatCacheOp.SaveCacheToFile save file {0} begin time {1}", saveFile, AppTime.GetNowSysMs()); try { FileStream fileStream = File.OpenWrite(saveFile); //清空文件 fileStream.SetLength(0); BinaryWriter sw = new BinaryWriter(fileStream); foreach (var item in chatCache.m_channalCacheMap) { foreach (int index in item.Value) { ref var dataOne = ref chatCache.m_cacheStrutChatReq.GetByIndex(index); if (!dataOne.IsNull()) { int size = dataOne.cacheData.CalculateSize(); byte[] dataByte = new byte[size]; CodedOutputStream ouput = new CodedOutputStream(dataByte); dataOne.cacheData.WriteTo(ouput); sw.Write(size); //写入长度 sw.Write(dataByte); //写入数据 sw.Flush(); ++haveSave; } } } sw.Dispose(); fileStream.Dispose(); chatCache.m_needSave = false; } catch (Exception ex) { TraceLog.Error("ChatCacheOp.SaveCacheToFile write file failed! {0}", saveFile); TraceLog.Exception(ex); return; } TraceLog.Trace("ChatCacheOp.SaveCacheToFile save file {0} num {1} success time {2}", saveFile, haveSave, AppTime.GetNowSysMs()); } public static bool LoadCacheFromFile() { string saveFile = GetSaveFilePath(); TraceLog.Trace("ChatCacheOp.LoadCacheFromFile load file {0} begin time {1}", saveFile, AppTime.GetNowSysMs()); var chatServerData = ChatServerUtils.GetChatServerData(); ref var chatCache = ref chatServerData.m_chatCacheInfo; if (File.Exists(saveFile) == false) { TraceLog.Trace("ChatCacheOp.LoadCacheFromFile file not exist {0}", saveFile); return true; } long nowSec = ChatServerUtils.GetTimeSecond(); long needSaveTime = nowSec - SaveMaxDay * 24 * 3600; try { FileStream fileStream = File.OpenRead(saveFile); BinaryReader sw = new BinaryReader(fileStream); long lenth = fileStream.Length; long pos = 0; while (pos < lenth) { int size = sw.ReadInt32(); //读长度 byte[] data = sw.ReadBytes(size); //读数据 pos += (sizeof(Int32) + size); if (pos <= lenth) { CodedInputStream input = new CodedInputStream(data); CSChatRes chatRes = new CSChatRes(); chatRes.ReadFrom(input); //XX天以前的消息就不存了 if (chatRes.SendTime > needSaveTime) { AddChatCache(ref chatRes); chatServerData.SetChannalChatSeq(chatRes.ChatSeq, chatRes.ChatChannelType, chatRes.ChannelParam); } } } sw.Dispose(); fileStream.Dispose(); } catch (Exception ex) { TraceLog.Error("ChatCacheOp.LoadCacheFromFile parse message failed! {0}", saveFile); TraceLog.Exception(ex); return false; } TraceLog.Trace("ChatCacheOp.LoadCacheFromFile load file {0} success time {1}", saveFile, AppTime.GetNowSysMs()); return true; } } }