You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

393 lines
12 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Sog;
using ProtoCSStruct;
namespace World
{
public class WaitAckDiamondHolderOneObj
{
public WaitAckDiamondHolderOne Data;
public long LastSendTime;
}
public class WaitAckMutexOrderID
{
public string OrderID;
public long CreateTime;//保护10秒
}
/// <summary>
/// 一定需要等待回应的消息发送器
/// 邮件之类需要严格处理逻辑的可以使用
/// </summary>
public class WaitAckDiamondHolderReqSender : Singleton<WaitAckDiamondHolderReqSender>
{
private const int TimeoutDeleteTimeSecond = 24*3600;//默认24小时超时,会强制删除
private int m_sendCountThisTick = 0; //频率控制
private long m_lastSaveTime = 0;
private bool m_bDirty;
//uid 互斥列表
private Dictionary<long, WaitAckMutexOrderID> m_mutexMap = new Dictionary<long, WaitAckMutexOrderID>();
private Dictionary<string, WaitAckDiamondHolderOneObj> m_sendAllPacket = new Dictionary<string, WaitAckDiamondHolderOneObj>();
public void Init()
{
ReadFromFile();
}
public int CheckMutex(PlayerInfoWorld player)
{
if(m_mutexMap.ContainsKey(player.UserID))
{
string orderId = m_mutexMap[player.UserID].OrderID;
TraceLog.Error("WaitAckDiamondHolderReqSender.CheckMutex uid {0} orderId {1}", player.UserID, orderId);
return -1;
}
return 0;
}
public void AddToMutex(long uid, string orderId)
{
if(m_mutexMap.ContainsKey(uid))
{
return;
}
WaitAckMutexOrderID mutexOrderID = new WaitAckMutexOrderID();
mutexOrderID.OrderID = orderId;
mutexOrderID.CreateTime = WorldServerUtils.GetTimeSecond();
m_mutexMap.Add(uid, mutexOrderID);
}
public void RemoveMutexOrderId(long uid, string orderId)
{
if (m_mutexMap.ContainsKey(uid))
{
string mutexOrderId = m_mutexMap[uid].OrderID;
if (mutexOrderId == orderId)
{
m_mutexMap.Remove(uid);
TraceLog.Debug("WaitAckDiamondHolderReqSender.RemoveMutexOrderId orderid {0} uid {1} success"
, orderId, uid);
}
}
}
public void BeginSendAdd(ref SSPayDiamondHolderAddReq req, PlayerInfoWorld player)
{
TraceLog.Debug("WaitAckDiamondHolderReqSender.BeginSendAdd orderId {0} uid {1} add {2}"
, req.OrderId, req.Uid, req.Add);
WaitAckDiamondHolderOneObj obj = new WaitAckDiamondHolderOneObj();
obj.Data.Uid = player.UserID;
obj.Data.RealmId = player.RealmID;
obj.Data.Account = req.Account;
obj.Data.Qqpayinfo = req.Qqpayinfo;
obj.Data.OrderId = req.OrderId;
obj.Data.ReqType = (int)SSGameMsgID.PayDiamondHolderAddReq;
obj.Data.Value = req.Add;
obj.Data.Mutex = req.Mutex;
obj.Data.ReasonId = req.ReasonId;
string orderId = req.OrderId.GetString();
m_sendAllPacket.Add(orderId, obj);
if(req.Mutex)
{
AddToMutex(player.UserID, orderId);
TraceLog.Debug("WaitAckDiamondHolderReqSender.BeginSendAdd orderId {0} add mutex uid {1}"
, orderId, player.UserID);
}
SendReq(obj);
m_bDirty = true;
}
public void OnReceiveSuccess(long uid, string orderId)
{
if(m_sendAllPacket.ContainsKey(orderId))
{
RemoveByOrderId(orderId);
TraceLog.Debug("WaitAckDiamondHolderReqSender.OnReceiveSuccess orderId {0} , removed"
, orderId);
}
else
{
TraceLog.Debug("WaitAckDiamondHolderReqSender.OnReceiveSuccess no orderId {0}", orderId);
}
//有可能orderId不在m_sendAllPacket里,只在m_mutexMap里,这是有可能的
RemoveMutexOrderId(uid, orderId);
}
public void Tick(long nowSecond)
{
TickSender(nowSecond);
TickMutex(nowSecond);
TickSave(nowSecond);
}
private void TickSender(long nowSecond)
{
m_sendCountThisTick = 0;
List<string> deleteList = null;
foreach (var pair in m_sendAllPacket)
{
var one = pair.Value;
//timeout delete
if (nowSecond - one.Data.CreateTime > TimeoutDeleteTimeSecond)
{
if (deleteList == null)
{
deleteList = new List<string>();
}
deleteList.Add(one.Data.OrderId.GetString());
//需要记录bill日志,表示超时失败
TraceLog.Error("WaitAckDiamondHolderReqSender.TickSender timeout, Deleted, uid {0} orderId {1} type {2} value {3}"
, one.Data.Uid
, one.Data.OrderId
, one.Data.ReqType
, one.Data.Value);
continue;
}
//动态计算重试时间
//前5分钟每分钟尝试一次,如果还失败,每5分钟尝试一次
int resenderTimeOut = 60;
if(one.Data.Mutex)
{
resenderTimeOut = 30; //互斥的快点处理
}
if (nowSecond - one.Data.CreateTime > 300)
{
resenderTimeOut = 300;
}
//失败了重新尝试发送
if (nowSecond - one.LastSendTime > resenderTimeOut)
{
one.LastSendTime = nowSecond;
//发送
SendReq(one);
TraceLog.Error("WaitAckDiamondHolderReqSender orderid {0} MsgID {1} uid {2} timeout ,resend it"
, one.Data.OrderId, one.Data.ReqType, one.Data.Uid);
m_sendCountThisTick++;
//控制1秒最多100个请求,防止雪崩
if (m_sendCountThisTick > 100)
{
break;
}
}
}
if (deleteList != null)
{
foreach (var key in deleteList)
{
RemoveByOrderId(key);
}
m_bDirty = true;
}
}
private void TickMutex(long nowSecond)
{
List<long> deleteList = null;
foreach(var pair in m_mutexMap)
{
if(nowSecond - pair.Value.CreateTime >= 30)
{
if(deleteList == null)
{
deleteList = new List<long>();
}
deleteList.Add(pair.Key);
}
}
if (deleteList != null)
{
foreach (var key in deleteList)
{
m_mutexMap.Remove(key);
}
}
}
private void RemoveByOrderId(string orderId)
{
WaitAckDiamondHolderOneObj obj;
if (m_sendAllPacket.TryGetValue(orderId, out obj))
{
m_sendAllPacket.Remove(orderId);
if(obj.Data.Mutex)
{
RemoveMutexOrderId(obj.Data.Uid, orderId);
}
}
}
private void SendReq(WaitAckDiamondHolderOneObj one)
{
if(one.Data.ReqType == (int)SSGameMsgID.PayDiamondHolderAddReq)
{
SSPayDiamondHolderAddReq req = new SSPayDiamondHolderAddReq();
req.RealmId = one.Data.RealmId;
req.OrderId = one.Data.OrderId;
req.Uid = one.Data.Uid;
req.Account = one.Data.Account;
req.Qqpayinfo = one.Data.Qqpayinfo;
req.Add = one.Data.Value;
req.Mutex = one.Data.Mutex;
req.ReasonId = one.Data.ReasonId;
//先实现托管给数据库,没问题了再实现托管给米大师
uint dbServerID = DBServerIDUtils.GetGameDBServerID(req.Uid);
WorldServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PayDiamondHolderAddReq, ref req, req.Uid);
}
}
private void TickSave(long nowSecond)
{
if(m_bDirty == false)
{
return;
}
//1分钟尝试保存一下
if (nowSecond - m_lastSaveTime < 60)
{
return;
}
m_lastSaveTime = nowSecond;
SaveToFile();
m_bDirty = false;
}
public string GetFileDBName()
{
return "../bin/" + "world_waitackdiamondhandler.fdb";
}
public void SaveToFile()
{
string filepath = GetFileDBName();
try
{
byte[] dataByte = new byte[1024];
FileStream fileStream = File.OpenWrite(filepath);
//清空文件
fileStream.SetLength(0);
BinaryWriter sw = new BinaryWriter(fileStream);
foreach (var onereq in m_sendAllPacket)
{
int size = onereq.Value.Data.CalculateSize();
CodedOutputStream ouput = new CodedOutputStream(dataByte);
onereq.Value.Data.WriteTo(ouput);
sw.Write(size);
sw.Write(dataByte,0,size);
}
sw.Flush();
sw.Dispose();
fileStream.Dispose();
}
catch (Exception ex)
{
TraceLog.Error("WaitAckDiamondHolderReqSender.SaveToFile write file failed! {0}", filepath);
TraceLog.Exception(ex);
}
}
private void ReadFromFile()
{
string filepath = GetFileDBName();
if (File.Exists(filepath) == false)
{
//这个不能算错误
TraceLog.Debug("WaitAckDiamondHolderReqSender.ReadFromFile file not exist {0}", filepath);
return ;
}
try
{
byte[] data = File.ReadAllBytes(filepath);
if(data.Length < 4)
{
//TraceLog.Error("WaitAckDiamondHolderReqSender.ReadFromFile length invalid {0}", filepath);
return;
}
int startIndex = 0;
while(startIndex < data.Length - 10)
{
int oneDataLength = BitConverter.ToInt32(data, startIndex);
//orderid挺长的,其实肯定不止10个长度
if (oneDataLength < 10)
{
break;
}
startIndex += 4;
ProtoCSStruct.CodedInputStream input = new ProtoCSStruct.CodedInputStream(data, startIndex, oneDataLength);
startIndex += oneDataLength;
WaitAckDiamondHolderOneObj one = new WaitAckDiamondHolderOneObj();
one.Data.ReadFrom(input);
string orderId = one.Data.OrderId.GetString();
if(m_sendAllPacket.ContainsKey(orderId) == false)
{
m_sendAllPacket.Add(orderId, one);
}
}
TraceLog.Debug("WaitAckDiamondHolderReqSender.ReadFromFile read WaitAckDiamondHolderOne count {0}", m_sendAllPacket.Count);
return ;
}
catch (Exception ex)
{
TraceLog.Error("WaitAckDiamondHolderReqSender.ReadFromFile parse message failed! {0}", filepath);
TraceLog.Exception(ex);
return ;
}
}
}
}