Asp.NET MVC 之心跳/长连接

0x01 在线用户类,我的用户唯一性由ID和类型识别(因为在不同的表里)

public class UserIdentity : IEqualityComparer<UserIdentity>
    {
        internal UserIdentity() { }
        public UserIdentity(int userId, int userType)
        {
            this.UserID = userId;
            this.UserType = userType;
        }
        public int UserID { get; private set; }
        public int UserType { get; private set; }

        public bool Equals(UserIdentity x, UserIdentity y)
        {
            return x.UserID == y.UserID && x.UserType == y.UserType;
        }

        public int GetHashCode(UserIdentity obj)
        {
            return obj.UserID * (obj.UserType + 10);
        }
    }
    public class AliveUser
    {
        private AliveUser() { }
        public AliveUser(string sessionID, int userID, int userType)
        {
            this.SessionID = sessionID;
            this.StartAt = DateTime.Now;
            this.User = new UserIdentity(userID, userType);
        }

        public void SetExpires(DateTime expires)
        {
            this.Expires = expires;
        }
        public string SessionID { get; private set; }
        public DateTime StartAt { get; private set; }
        public DateTime Expires { get; private set; }
        public UserIdentity User { get; private set; }
    }

0x02 会话管理,线程+字典

public class AliveSessionHelper
    {
        static AliveSessionHelper()
        {
            thread.IsBackground = true;
            thread.Start();
        }
        public static int TimeoutInMinutes = 1;
        private static object syncObject = new object();
        public static Dictionary<string, AliveUser> OnlineUsers = new Dictionary<string, AliveUser>();
        public static Dictionary<UserIdentity, List<AliveUser>> GetOnlineUsers()
        {
            lock (syncObject)
            {
                var list = OnlineUsers.Values.ToList();
                var grouping = list.GroupBy(x => x.User,new UserIdentity());
                var dict = new Dictionary<UserIdentity, List<AliveUser>>(grouping.Count());
                foreach (var item in grouping)
                {
                    dict.Add(item.Key, item.ToList());
                }
                return dict;
            }
        }

        public static void AddAliveUser(string sessionID, int userID, int userType)
        {
            AddAliveUser(new AliveUser(sessionID, userID, userType));
        }

        public static void AddAliveUser(AliveUser user)
        {

            lock (syncObject)
            {
                var aliveUser = OnlineUsers.ContainsKey(user.SessionID) ? OnlineUsers[user.SessionID] : null;
                if (aliveUser == null)
                {
                    OnlineUsers.Add(user.SessionID, user);
                }

                var expires = DateTime.Now.AddMinutes(TimeoutInMinutes);
                OnlineUsers[user.SessionID].SetExpires(expires);
            }
        }

        private static Thread thread = new Thread(ThreadStartBody);
        private static void ThreadStartBody()
        {
            while (true)
            {
                lock (syncObject)
                {
                    var delList = new List<Guid>();
                    var list = OnlineUsers.Values.ToList();
                    foreach (var item in list)
                    {
                        if (item.Expires.Subtract(DateTime.Now).TotalSeconds < 5)
                        {
                            OnlineUsers.Remove(item.SessionID);
                        }
                    }
                }
                Thread.Sleep(29);
            }
        }
    }

0x03 控制器实现

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
    [Authorize]
    public class ApiController : AsyncController
    {
        public async Task<ActionResult> KeepAlive()
        {
            if (!User.Identity.IsAuthenticated) {
                return null;
            }
            AliveSessionHelper.AddAliveUser(Session.SessionID, AdminUserService.Instance.CurrentUserID, 0);
            return await Task.Delay(TimeSpan.FromSeconds(30)).ContinueWith(task =>
            {
                return JavaScript("$.getScript('/admin/api/keepalive');");
            });
        }
    }   

注意

1、[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]是必须的,否则刷新(重复调用)会锁死,原理熟手自己领会,新手照做即可。

2、/admin/api/keepalive 调用的Action是自身。

3、服务端控制请求的释放间隔。这里设置的30s。如果要更长时间,如超过1分钟可能导致Action超时异常,需要加AsyncTimeoutAttribute特性设置Action超时时间大于释放间隔。

0x04 前端代码,需要jQuery。就是触发首次调用,然后就靠服务端返回js自动执行。

$(function () {
         //KeepAlive
         $.getScript('/admin/api/keepalive');
}

是不是感觉代码很简单?注重简单实用好用是博主一贯的追求。

拷贝请注明来源博客园,道木先生。.Net软件交流群283590657

  

原文地址:https://www.cnblogs.com/mrtiny/p/5170286.html