无论我们采用何种通信框架来构建我们的分布式系统,在服务端进行用户管理都是非常重要的一个环节。然而用户管理是否应该隶属于通信框架了?这个并不一定,通常来说,用户管理是与具体应用紧密相关的,应该是由应用解决的部分,因为不同的应用程序对用户管理的需求是不尽相同的。但是,如果我们对大多数应用中的用户管理任务进行分析,我们发现它们都会关注一些最基础的用户管理需求(如用户状态监控)。如果能在通信框架中内置一种简洁的、灵活的、可扩展的用户管理组件,定会为大多数应用程序提供非常多的方便。

ESFramework抽象了最基本的用户管理任务,定义了统一的用户管理的基础接口,并提供了默认的实现,它们能与前面介绍的网络通信引擎无缝地集成。对于许多不是特别复杂的分布式通信应用,ESFramework内置的用户管理功能几乎就可以满足要求。另外,ESFramework和ESPlus以及ESPlatform的许多扩展特性和功能也需要基于用户管理组件的抽象接口来展开。

一.用户管理器接口IUserManager

  用户管理器是用在服务端的,所以IUserManager接口的定义位于ESFramework.Server.UserManagement命名空间,IUserManager不仅可以用于基于TCP的服务端、也可以用于基于UDP的服务端。

我们首先看看其类图:

IUserManager继承自ICoreUserManager接口,ICoreUserManager是更基础的用户管理接口,其主要被用于ESPlatform平台,否则,其中定义的内容可以完全合并到IUserManager接口中。下面我把这两个接口的定义源码贴出来,里面较详细的注释说明可以让我少敲些字。

public interface ICoreUserManager
    {
        /// <summary>
        /// 在线用户的数量。
        /// </summary>
        int UserCount { get; }

/// <summary>
        /// 获取目标在线用户的基础信息。
        /// </summary>
        /// <param name="userID">目标用户的ID</param>
        /// <returns>如果目标用户不在线,则返回null</returns>
        UserData GetUserData(string userID);

/// <summary>
        /// 获取所有在线用户的ID列表。
        /// </summary>        
        List<string> GetOnlineUserList();

/// <summary>
        /// 如果是基于tcp引擎,则当tcp连接上接收或发送数据抛出异常时,将关闭连接,并触发此事件。
        /// </summary>
        event CbGeneric<UserData> SomeOneDisconnected;

/// <summary>
        /// 如果是基于tcp引擎,当接收到新连接上的第一个消息时,将触发此事件。
        /// </summary>
        event CbGeneric<UserData> SomeOneConnected;
    }

public interface IUserManager : ICoreUserManager
    {
        /// <summary>
        /// 用户管理器依赖该属性显示所有在线用户的状态信息。    
        /// </summary>
        IUserDisplayer UserDisplayer { set; }

/// <summary>
        /// 在线用户的心跳检测器。
        /// </summary>
        IHeartBeatChecker HeartBeatChecker { set; }

/// <summary>
        /// 重登陆模式。
        /// </summary>
        RelogonMode RelogonMode { get; set; }

/// <summary>
        /// 初始化用户管理器。
        /// </summary>
        void Initialize();

/// <summary>
        /// 清除所有的在线用户。
        /// </summary>
        void Clear();  
          
        /// <summary>
        /// 激活心跳。
        /// </summary>        
        void ActivateUser(string userID);

/// <summary>
        /// 目标用户是否在线。
        /// </summary>       
        bool IsUserOnLine(string userID);

/// <summary>
        /// 从在线列表中移除目标用户(依据用户的地址)。
        /// </summary>        
        void UnregisterUser(IUserAddress address);

/// <summary>
        /// 从在线列表中移除目标用户。
        /// </summary>
        void UnregisterUser(string userID);

/// <summary>
        /// 根据在线用户的地址获取用户ID。
        /// </summary>        
        string GetUserID(IUserAddress address);

/// <summary>
        /// GetTimeLogon 获取目标在线用户的登录时间。
        /// </summary>       
        DateTime GetTimeLogon(string userID);

/// <summary>
        /// 用于统计发送给用户的消息。如果用户还未注册,则忽略。
        /// </summary>        
        void AfterSentMessageToUser(string userID, IUserAddress address, IMessage msg);

/// <summary>
        /// 如果用户不在线,返回null    
        /// </summary>  
        IUserAddress GetUserAddress(string userID);

/// <summary>
        /// 当用户已经注册过,如果RelogonMode为IgnoreNew,则不会更改其address,而是直接返回;如果RelogonMode为ReplaceOld,则使用新的连接取代旧的连接。
        /// </summary>       
        void RegisterUser(string userID, IUserAddress address); 
        
        /// <summary>
        /// 旧连接被挤掉。如果RelogonMode为ReplaceOld,并且当从另外一个新连接上收到一个同名ID用户的消息时将触发此事件。

/// </summary>
        event CbGeneric<UserData> SomeOneBeingPushedOut;

/// <summary>
        /// 新连接被忽略。如果RelogonMode为IgnoreNew,并且当从一个新连接上收到一个同名ID用户的消息时将触发此事件。   
        /// </summary>
        event CbGeneric<string ,IUserAddress> NewConnectionIgnored;

/// <summary>
        /// 用户超时。只有在该事件处理完毕后,才将其从用户列表中删除。
        /// 但是,用户对应的TCP连接可能并没有被释放 -- 所以,在该事件处理函数中最好主动关闭TCP连接(将触发SomeOneDisconnected事件)。       
        /// </summary>
        event CbGeneric<UserData> SomeOneTimeOuted;

/// <summary>
        /// 当在线用户数发生变化时,触发此事件。
        /// </summary>
        event CbGeneric<int> UserCountChanged;
    }

(1)默认情况下,IUserManager会从客户端发过来的第一条消息中取出消息头的UserID属性的值,并将其与对应的用户地址IUserAddress对应起来,并触发SomeOneConnected事件。有一点我们是要特别注意,那就是同一个客户端每次给服务器发消息时,要保证每个消息的Header中UserID是一致的。而用户管理器只会认定第一个消息的Header中的UserID的值。

(2)在用户管理器中,UserAddress与UserID是一一对应的,一个UserAddress实例只能对应一个UserID,同样的,一个UserID最多存在一个UserAddress对象。我们可以通过GetUserID方法通过UserAddress获取对应的UserID,也可以通过GetUserData方法根据UserID得到对应的UserAddress对象。UserAddress对象中包括了客户端与服务器进行通信的IP地址和Port信息。

(3)如果从不同的连接上接收到相同UserID的信息,IUserManager采取的策略取决于RelogonMode属性的设置,其有可能触发SomeOneBeingPushedOut事件(RelogonMode为ReplaceOld)、也有可能触发NewConnectionIgnored事件(RelogonMode为IgnoreNew)。关于重登陆方面的更多信息,可以参见ESFramework 4.0 快速上手 -- 重登陆模式。

(4)在处理SomeOneBeingPushedOut事件时要注意:只有在该事件处理完毕后,IUserManager才会真正使用新的地址取代旧的地址。所以,我们必须在该事件的处理函数中,关闭旧的连接(TCP)或Session(UDP)以释放资源(将触发SomeOneDisconnected事件)。但是在关闭之前,可以将相关情况通知给旧连接/Session的客户端。

(5)在处理NewConnectionIgnored事件时也要注意:我们必须在该事件的处理函数中,关闭新的连接或Session,同样的,在关闭之前,可以将相关情况通知给该连接/Session的客户端。

(6)当通信引擎检测到底层连接断开时(针对TCP),IUserManager会触发SomeOneDisconnected事件。

(7)IUserManager借助于IHeartBeatChecker对在线用于进行心跳检测,如果用户因为心跳超时,则会触发SomeOneTimeOuted事件。关于ESFramework中的TCP掉线与心跳机制的更多信息,可以参见ESFramework 4.0 快速上手 -- 玩的就是“心跳”

二.用户状态跟踪及显示

(1)当用户上线时,IUserManager会记录其上线时间和地址UserAddress。

(2)当服务端每次向客户端发送数据之后,IUserManager会通过AfterSentMessageToUser方法来进行记录,以此可以统计每个用户请求服务的次数和下载的数据量。

(3)我们可以通过IUserManager接口的GetUserData方法获取在线用户的实时状态。GetUserData返回UserData对象,从该对象的类图可以看出其包含了哪些状态信息:

(4)IUserManager接口还可以注入一个IUserDisplayer属性,通过IUserDisplayer可以在UI上显示每个用户的实时状态。

public interface IUserDisplayer 
    { 
        /// <summary>
        /// 清除所有。通常是通信引擎停止时被调用。
        /// </summary>
        void ClearAll() ;

/// <summary>
        /// 当用户状态更新时被调用。
        /// </summary>
        /// <param name="userID">用户ID</param>
        /// <param name="justServiceType">刚刚发送给用户的消息类型</param>
        /// <param name="totalDataLen">该用户下载的总的数据量</param>
        /// <param name="userAddress">用户的地址</param>
        /// <param name="totalReqCount">总的请求次数</param>
        void SetOrUpdateUserItem(string userID, int justServiceType, long totalDataLen, string userAddress, long totalReqCount);

/// <summary>
        /// 移除用户。通常在用户下线时被调用。
        /// </summary>       
        void RemoveUser(string userID ,string cause);
    }

IUserManager借助于IUserDisplayer来展现每个在线用户的状态,我们可以实现该接口,以我们想要的方式显示这些状态数据。

如果没有特别需求,可以直接使用ESPlus提供的ESPlus.Widgets.UserDisplayer控件,它显示的效果如下所示:

一定要提醒读者的是,如果服务器处理的是巨大并发量的任务,就不要使用任何形式的UserDisplayer控件,甚至,服务端最好都不要有UI,而是以一个service的形式运行。因为在这种巨大并发量的系统中,在线用户数量巨大(数万计),而且状态更新频繁,会导致大量的CPU时间浪费在UI的更新上。经验之谈,谨记之。

三.与通信引擎集成

ESFramework通过ESFramework.Server.UserManagement.UserManagerBridge将用户管理器IUserManager与服务端引擎IServerEngine桥接起来:

  在UserManagerBridge的Initialize方法中,它会将IServerEngine的相关事件(如MessageReceived事件、MessageSent事件,对于TCP服务端引擎还有SomeOneDisconnect事件)传递给IUserManager的对应方法去处理,这样用户管理器就被驱动起来了。

最后,我们实例化一个用户管理器并就将其与服务端引擎集成起来作为示范:

StreamTcpEngine streamTcpEngine = ......;

IHeartBeatChecker heartBeatChecker = new ESBasic.Threading.Application.HeartBeatChecker(1, 30);//心跳超时设为30秒
    heartBeatChecker.Initialize();
    IUserManager userManager = new UserManager();
    userManager.HeartBeatChecker = heartBeatChecker;
    userManager.RelogonMode = RelogonMode.ReplaceOld;//设置重登陆模式
    userManager.Initialize();

UserManagerBridge userManagerBridge = new UserManagerBridge();
    userManagerBridge.UserManager = userManager;
    userManagerBridge.ServerEngine = streamTcpEngine;
    userManagerBridge.Initialize();

  关于如何实例化一个TCP服务端引擎,可以参见ESFramework 4.0 进阶(04)-- 驱动力:通信引擎(下)一文中的代码示例。

转载于:https://www.cnblogs.com/sylone/p/6096943.html

在线用户管理--ESFramework 4.0 进阶(05)相关推荐

  1. Sangfor_AC用户不在线但在“在线用户管理”里有显示

    现象:用户实际不在线,但是在"在线用户管理"里有看到,而且在线时间很长. 分析:用户通过IP上线以后,只要IP地址在线或者下线时间不超过"无流量自动注销的时间" ...

  2. SM04在线用户管理

    管理员在SAP系统中,使用事物码SM04对当前登录在线用户(User)进行管理,可查看服务器全部客户端(Client)的用户的在线状态,并可以结束指定用户的会话状态,也就是强制踢出用户(当然SM12也 ...

  3. SM04 在线用户管理(踢人事务)

    管理员在SAP系统中,使用事物码SM04对当前登录在线用户(User)进行管理,可查看服务器全部客户端(Client)的用户的在线状态,并可以结束指定用户的会话状态,也就是强制踢出用户. 双击查看该用 ...

  4. 监控自定义信息 —— ESFramework 4.0 快速上手(10)

    在ESFramework 4.0 进阶(02)-- 核心:消息处理的骨架流程一文中,我们介绍了通过挂接IMessageSpy到骨架流程,我们就可以监控到所有收发的消息.由于Rapid引擎已经为我们组装 ...

  5. AX向在线用户发送消息

    在AX3.0中管理员可以通过在线用户管理对在线用户进行发送信息,是通过ClientSessions::sendMessage(int _sessionId, str _title, str _mess ...

  6. 用redis+jwt保存在线用户和获得在线用户列表、踢出用户示例

    文章目录 redis工具类 用户实体类 token配置 service层保存和查询在线用户 工具类 获得用户浏览器等其他信息 controller层 redis工具类 import org.sprin ...

  7. ASP.NET 2.0用户管理数据库的注册

    在ASP.NET 2.0中提供了许多新功能,这些功能都需要使用Provider提供对数据库的访问.通过Provider,不需要再编写ADO.NET去访问数据库,就可以进行用户.角色等的管理. 要使用. ...

  8. JavaWeb-网站在线用户信息、网站访问量以及管理踢出用户实例

    转载请注明出处: http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 这个稍微比上个版本做得完善了一点,用了JavaBean来封装用户信息,添加了一个管 ...

  9. 后台业务管理系统原型模板/在线教育后台管理系统/客服系统/财务管理/用户管理/订单管理/教育业务后台管理/课程管理/教师管理/活动管理/文章管理/Axure高保真在线教育行业原型/Axure后台管理

    Axure原型演示地址:https://www.pmdaniu.com/storages/123396/095623b2ab90c911d2b6e7a7b83b37c8-93733/start.htm ...

最新文章

  1. java 数组越界异常_数组越界异常 求解决!!!
  2. 详细!快速入门指南!Docker!
  3. 揭秘丨“北京八分钟”里中国制造的科技力量
  4. 使用thrift进行跨语言调用(php c# java)
  5. Python之pandas:pandas的get_dummies函数简介(将分类变量转为哑变量)及其使用方法之详细攻略
  6. python标准输入输出用来干什么_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...
  7. boot返回码规范 spring_SpringBoot 系列 web 篇之自定义返回 Http Code 的 n 种姿势
  8. TensorFlow 2.X中的动手NLP深度学习模型准备
  9. Python timedelta total_seconds()方法与示例
  10. matlab常用的代码,matlab常用代码
  11. MySQL数据库(3)_MySQL数据库表记录操作语句
  12. nginx 禁止某个IP访问
  13. Git基本用法(一)
  14. centos7 wps安装
  15. 并查集详解(C/C++)
  16. java中File流转Base64
  17. php 单笔转账到支付宝账户,php之支付宝转账或发红包到指定账户(提现功能)
  18. python给word添加换行换页符
  19. 狗头人与地下世界冒险模式通关记录 [20171224]
  20. windows操作系统服务器 网卡速度关联项

热门文章

  1. Java对象序列化详解
  2. typedef struct 先声明后定义_C++模版和C#泛型求同存异录(二)typedef
  3. 课时 17-深入理解 etcd:etcd 性能优化实践(陈星宇)
  4. python 短进程优先算法_黄哥Python:图深度优先算法(dfs)
  5. Python单元测试之pytest
  6. learnpython_LearnPython_week1
  7. java多个数求和_Java:多个数求和
  8. shell命令获取按键值_linux shell获取键盘输入
  9. linux deb文件安装_如何在 Ubuntu 上安装 VirtualBox | Linux 中国
  10. 你绝对能懂的“机器学习”(二)