/// <summary> /// 若用户在线则推送消息,不在线返回false /// </summary> /// <param name="p_userID"></param> /// <param name="p_msg"></param> /// <returns>true 已在线推送,false不在线</returns> public static async Task <bool> PushIfOnline(long p_userID, MsgInfo p_msg) { if (Online.All.TryGetValue(p_userID, out var ls) && ls != null && ls.Count > 0) { // 本地在线推送 ls[0].AddMsg(p_msg.GetOnlineMsg()); return(true); } // 查询所有其他副本 int cnt = await Kit.GetSvcReplicaCount(); if (cnt > 1) { string key = $"msg:PushIfOnline:{p_userID}:{Guid.NewGuid().ToString().Substring(0, 6)}"; Kit.RemoteMulticast(new OnlinePushEvent { PrefixKey = key, Receivers = new List <long> { p_userID }, Msg = p_msg.GetOnlineMsg(), PushFirstSession = true }); // 等待收集 int total, retry = 0; var sc = new StringCache(key); do { await Task.Delay(_delayMilli); total = await sc.Get <int>("cnt"); retry++; }while (total < cnt && retry < _maxRetry); // 删除统计总数 await sc.Delete("cnt"); // 存在键值表示在线 if (await sc.Delete(null)) { return(true); } } return(false); }
/// <summary> /// 多副本推送 /// </summary> /// <param name="p_userIDs">用户列表</param> /// <param name="p_msg">待推送信息</param> /// <param name="p_onlines"></param> /// <param name="p_offlines"></param> /// <param name="p_cnt"></param> static async Task PushMultipleReplicas(List <long> p_userIDs, string p_msg, List <long> p_onlines, List <long> p_offlines, int p_cnt) { /* * Msg服务多副本运行时,有以下两种处理方案: * 1. 由当前副本通知所有副本,每个副本都检查会话是否存在,存在时推送并做已推送标志,再由当前副本汇总离线列表 * 2. 将所有副本的会话信息缓存在redis,当前副本查询缓存获取会话所在的副本,再通过事件总线推送到指定副本 * 当前采用方案1,因方案2在某个副本非正常退出时,缓存的会话未能与实际同步,造成冗余的推送! * 方案1的缺点是所有副本都需要检查会话是否存在 */ // 推送结果的前缀键 string prefixKey = "msg:Push:" + Guid.NewGuid().ToString().Substring(0, 6); // 通知所有副本推送 Kit.RemoteMulticast(new OnlinePushEvent { PrefixKey = prefixKey, Receivers = p_userIDs, Msg = p_msg }); // 等待收集 int total, retry = 0; var sc = new StringCache(prefixKey); do { await Task.Delay(_delayMilli); total = await sc.Get <int>("cnt"); retry++; }while (total < p_cnt && retry < _maxRetry); // 删除统计总数 await sc.Delete("cnt"); foreach (long id in p_userIDs) { // 有记录的表示在线推送成功 if (await sc.Delete(id)) { p_onlines.Add(id); } else { p_offlines.Add(id); } } }
/// <summary> /// 注销客户端 /// 1. 早期版本在客户端关闭时会造成多个无关的ClientInfo收到Abort,只能从服务端Abort,升级到.net 5.0后不再出现该现象!!! /// 2. 使用客户端 response.Dispose() 主动关闭时,不同平台现象不同,服务端能同步收到uwp关闭消息,但android ios上不行, /// 直到再次推送时才发现客户端已关闭,为了保证客户端在线状态实时更新,客户端只能调用该方法!!! /// </summary> /// <param name="p_userID"></param> /// <param name="p_sessionID">会话标识,区分同一账号多个登录的情况</param> /// <returns></returns> public async Task <bool> Unregister(long p_userID, string p_sessionID) { var ci = Online.GetSession(p_userID, p_sessionID); if (ci != null) { await ci.Close(); return(true); } // 查询所有其他副本 int cnt = await Kit.GetSvcReplicaCount(); if (cnt > 1) { string key = $"msg:Unregister:{p_userID}:{Guid.NewGuid().ToString().Substring(0, 6)}"; Kit.RemoteMulticast(new UnregisterEvent { CacheKey = key, UserID = p_userID, SessionID = p_sessionID }); // 等待收集 int total, retry = 0; var sc = new StringCache(key); do { await Task.Delay(_delayMilli); total = await sc.Get <int>("cnt"); retry++; }while (total < cnt && retry < _maxRetry); // 删除统计总数 await sc.Delete("cnt"); // 存在键值表示在线 if (await sc.Delete(null)) { return(true); } } return(false); }
/// <summary> /// 实时获取所有副本的在线用户总数 /// </summary> /// <returns>Dict结构:key为副本id,value为副本会话总数</returns> public async Task <Dict> GetOnlineCount() { Dict result = null; int cnt = await Kit.GetSvcReplicaCount(); if (cnt > 1) { // 所有副本 string key = "msg:OnlineCount:" + Guid.NewGuid().ToString().Substring(0, 6); Kit.RemoteMulticast(new OnlineCountEvent { CacheKey = key }); // 等待收集 int total, retry = 0; var sc = new StringCache(key); do { await Task.Delay(_delayMilli); total = await sc.Get <int>("cnt"); retry++; }while (total < cnt && retry < _maxRetry); // 删除统计总数 await sc.Delete("cnt"); var hc = new HashCache(key); var hash = await hc.GetAll(null); if (hash != null && hash.Length > 0) { await hc.Delete(null); result = hash.ToDict(); } } else { // 当前单副本 result = new Dict { { Kit.SvcID, Online.TotalCount } }; } return(result); }
/// <summary> /// 查询所有副本,获取某账号的所有会话信息 /// </summary> /// <param name="p_userID"></param> /// <returns>会话信息列表</returns> public async Task <List <Dict> > GetAllSessions(long p_userID) { List <Dict> result = new List <Dict>(); int cnt = await Kit.GetSvcReplicaCount(); if (cnt > 1) { // 查询所有副本 string key = $"msg:Sessions:{p_userID}:{Guid.NewGuid().ToString().Substring(0, 6)}"; Kit.RemoteMulticast(new UserSessionsEvent { CacheKey = key, UserID = p_userID }); // 等待收集 int total, retry = 0; var sc = new StringCache(key); do { await Task.Delay(_delayMilli); total = await sc.Get <int>("cnt"); retry++; }while (total < cnt && retry < _maxRetry); // 删除统计总数 await sc.Delete("cnt"); var hc = new HashCache(key); var hash = await hc.GetAll(null); if (hash != null && hash.Length > 0) { await hc.Delete(null); var dt = hash.ToDict(); foreach (var item in dt) { var ss = Kit.Deserialize <List <Dict> >((string)item.Value); if (ss != null && ss.Count > 0) { result.AddRange(ss); } } } } else { // 当前单副本 var ls = Online.GetSessions(p_userID); if (ls != null && ls.Count > 0) { foreach (var ci in ls) { result.Add(new Dict { { "userid", ci.UserID }, { "svcid", Kit.SvcID }, { "starttime", ci.StartTime.ToString() }, { "platform", ci.Platform }, { "version", ci.Version }, { "devicename", ci.DeviceName }, { "devicemodel", ci.DeviceModel }, }); } } } return(result); }