ESFramework介绍之(17)―― 支持漫游用户和跨区域功能请求

    对于漫游用户的支持和跨区域功能请求的支持是ESFramework最基本的目的之一(回顾),在详细讲述解决方案之前,先了解一下关于这个问题的上下文。
    在我们前面讲述的4层C/S架构中,每个AS负责一块区域。比如上海AS负责处理所有目标城市为上海的功能请求和管理所有在上海AS上注册的用户(比如PDA用户或手机用户)。如果一个本是在上海注册的用户出差来到了武汉,最方便的,他会连上武汉的AS,这样对于武汉AS来说,这个用户就是漫游用户了。
    如果上海的用户登陆上了上海的AS,但是他需要请求目标城市为武汉的服务,这个请求就是跨区域的请求,上海AS处理不了,需要转发给武汉AS处理。
    还有一种情况,就是即时消息,比如登录到上海AS的用户需要和登录到武汉AS的用户进行即时通讯,这些即时消息都需要在不同的AS之间进行中转,然后最终转发给目标用户。
    ESFramework对上面的各种情况都给予了充分完善的支持。首先,我们解决第一个问题――支持漫游用户。
    单纯的支持漫游用户,非常容易做到。重要的是我们需要为即时通讯信息作准备。当一个用户想查询他的某个“好友”是否在线时,如果目标好友没有漫游,那么,只需要询问其注册的的AS就可以知道答案,但是如果其漫游到异地AS了了?难道我们需要去轮询成千上万个AS中的每一个吗?这样的效率是无法忍受的。ESFramework是通过引入IRamblesManager接口解决这个问题。

1     public interface IRamblesManager
2     {
3         int     GetResideCityCode(string userID) ; //用户注册(手机号码)所属地
4         int     GetUserCurrCityCode(string userID) ; //用户当前所在地
5         void    RemoveRambles(string userID) ;
6         void    RegisterRambles(string userID ,int cityCode) ;
7     }

    IRamblesManager接口用于管理所有的漫游用户,它记录了每个漫游用户的当前所在区域。这个接口在每个AS发现漫游用户时调用。比如,当某个用户登录到上海AS,上海AS发现这个用户不是在上海注册的,于是,它调用IRamblesManager接口把该用户添加到漫游记录表中。这个漫游记录表可能位于某个公共的数据库中、也可能位于某台7*24小时运行的服务器的内存中。IRamblesManager接抽象了这些具体的物理实现。每个AS通过与IRamblesManager交互就可以知道某个用户是否漫游了、以及漫游到了何地等信息。

    解决了漫游用户后,我们现在转向第二个问题,如何支持跨区域的功能请求?
    前面的系列文章已经讲到,所有的功能请求都是通过消息处理器来处理的,对于跨区域的功能请求,ESFramework提供了跨区域的功能处理器来处理。其仍然实现了IDataDealer接口。下面列出跨区域功能处理器的DealRequestMessage方法的实现代码:

 1         public NetMessage DealRequestMessage(NetMessage reqMsg)
 2         {
 3             try
 4             {
 5                 //从IRAS获取目标AS的地址
 6                 string serverIp = this.irasRemotingAccesser.GetAppServerIp(reqMsg.Header.TypeKey) ;    
 7                 if(serverIp == null)
 8                 {
 9                     return this.contractHelper.GetResponseByServiceResultType(reqMsg ,ServiceResultType.ServiceStopped) ;
10                 }
11                 
12                 //获取目标AS发布的远程服务句柄
13                 IAsRemotingService_4As asRemotingAccesser = (IAsRemotingService_4As)NetHelper.GetRemotingHanler(this.myConfiguration.RemotingChannelTypeStr ,serverIp ,this.myConfiguration.AsRemotingPort ,this.myConfiguration.AsRemoting4AsServiceName ,typeof(IAsRemotingService_4As)) ;
14                 if(asRemotingAccesser == null)
15                 {
16                     return this.contractHelper.GetResponseByServiceResultType(reqMsg ,ServiceResultType.ServiceStopped) ;
17                 }
18 
19                 //Hook
20                 NetMessage hookedMsg = this.netMessageHook.CaptureBeforeSendMsg(reqMsg) ;
21 
22                 //目标AS处理跨区域请求
23                 return asRemotingAccesser.DealRequestMessage(hookedMsg) ;
24             }
25             catch(Exception ee)
26             {
27                 ee = ee ;
28                 this.esbLogger.Log(ee.Message ,"ESFramework.Architecture.LBS.FourTier.ForeignDealer" ,ErrorLevel.Standard) ;
29                 return this.contractHelper.GetResponseByServiceResultType(reqMsg ,ServiceResultType.ServiceStopped) ;
30             }
31         }

    上面的代码和注释已经非常清楚了说明了跨区域请求是如何被处理的,这里就不赘言了。只是需要注意,所有的功能请求都是通过远程服务接口提供服务的。
    
    最后,说一说上面描述上下文时提到的第三种情况,AS是如何处理即时通讯消息的?即时通讯消息在ESFramework种称为P2PMessage,对于P2PMessage当然也有对应的消息处理器P2PMessageDealer,它负责将P2PMessage转发给指定的目标用户。P2PMessageDealer的工作方式如下,如果发现目标用户就在本地AS上,则直接转发。如果目标用户是异地用户或者已经漫游到异地,则将消息转发到目标AS上。P2PMessageDealer的实现借助了IToClientSender接口,IToClientSender接口向应用屏蔽了底层的消息转发过程,其定义如下:

    /// <summary>
    /// ToClientSender 将数据(一个完整的请求--header+body)转发给目标用户。隐藏了目标用户的位置
    /// 目标用户可能连接在其它服务器节点上    
    /// </summary>
    public interface IToClientSender
    {
        int HookAndSendMessage(string userID ,NetMessage msg) ;    //返回DataSendResult的常量
    }

    ESFramework中的IToClientSender实现是ToClientSender,它可以包含一组具体的Sender,每次发送消息时,ToClientSender会按照指定的顺序调用每个Sender,知道有一个发送成功为止。ToClientSender实现如下:

    public class ToClientSender :IToClientSender
    {
        private IList senderList = new ArrayList() ;        

        public ToClientSender()
        {            
        }

        #region property
        public IList SenderList
        {
            set
            {
                this.senderList = value ;
            }
        }
        #endregion

        #region IToClientSender 成员
        public int HookAndSendMessage(string userID ,NetMessage msg)
        {
            int theResult = DataSendResult.UserIsOffLine ;            

            foreach(IToClientSender sender in this.senderList)
            {
                int res = sender.HookAndSendMessage(userID ,msg) ;
                if(res == DataSendResult.Succeed)
                {
                    return DataSendResult.Succeed ;
                }    
            
                if(res != DataSendResult.UserIsOffLine)
                {
                    theResult = res ;
                }
            }

            return DataSendResult.UserIsOffLine ;
        }

        #endregion

    }

    当我们在配置应用时,ToClientSender的SenderList通常只包含两个Sender――ToLocalClientSender和ToForeignClientSender。ToLocalClientSender在AS内部传递要转发的消息,而ToForeignClientSender跨AS传递要转发的消息。ToLocalClientSender的实现非常简单:

 1         public int HookAndSendMessage(string userID ,NetMessage msg)
 2         {
 3             bool onLine = this.tcpUserManager.IsUserOnLine(userID) ;
 4             if(onLine)
 5             {
 6                 int connectID = this.tcpUserManager.GetUserConnectID(userID) ;
 7                 this.hookSender.HookAndSendNetMessage(connectID ,msg) ;
 8                 return DataSendResult.Succeed ;
 9             }    
10         
11             return DataSendResult.UserIsOffLine ;
12         }

    tcpUserManager是用户管理者组件,它管理了所有的在线用户。关于它的详细描述会在后文中给出。
    ToForeignClientSender通过借助IRamblesManager和AS发布的远程服务接口也实现了跨区域消息的转发。

 1         public int HookAndSendMessage(string userID, NetMessage msg)
 2         {
 3             try
 4             {
 5                 int userCurCityCode = this.ramblesManager.GetUserCurrCityCode(userID) ;
 6                 if(this.myConfiguration.CityCode == userCurCityCode)
 7                 {
 8                     return DataSendResult.UserIsOffLine ;
 9                 }
10 
11                 string destAppServerIp = this.irasRemotingAccesser.GetAppServerIp(userCurCityCode) ;
12                 if(destAppServerIp == null)
13                 {
14                     return DataSendResult.UserIsOffLine ;
15                 }
16 
17                 IAsRemotingService_4As asRemotingAccesser = (IAsRemotingService_4As)NetHelper.GetRemotingHanler(this.myConfiguration.RemotingChannelTypeStr ,destAppServerIp ,this.myConfiguration.AsRemotingPort ,this.myConfiguration.AsRemoting4AsServiceName ,typeof(IAsRemotingService_4As)) ;
18                 if(asRemotingAccesser == null)
19                 {
20                     return DataSendResult.UserIsOffLine ;
21                 }
22 
23                 NetMessage hookedMsg = this.netMessageHook.CaptureBeforeSendMsg(msg) ;
24                 return asRemotingAccesser.HookAndSendMessage(userID ,msg) ;    
25             }
26             catch(Exception ee)
27             {
28                 ee = ee ;
29                 this.esbLogger.Log(ee.Message ,"ESFramework.Architecture.LBS.FourTier.ToForeignClientSender" ,ErrorLevel.Standard) ;
30                 return DataSendResult.FailByOtherCause ;
31             }
32         }
33 

    关于“支持漫游用户和跨区域功能请求”,本文只是对主要的部分作了讲述,还有很多小的细节无法在这里全面展开。如果只是要了解并使用ESFramework框架来帮你搭建应用,了解这些就足够了:)感谢关注!

上一篇文章:ESFramework介绍之(16)―― Tcp数据自动发送器ITcpAutoSender

转到  :ESFramework 可复用的通信框架(序) 

 

时间: 2024-08-02 05:25:08

ESFramework介绍之(17)―― 支持漫游用户和跨区域功能请求的相关文章

ESFramework介绍之(18)―― Tcp用户管理器组件

    当我们的应用中客户端与AS之间是通过Tcp进行通信的时候,通常,应用也要求管理所有在线的用户.这种管理至少包含以下几点:(1) 当用户上线时,记录上线时间(2) 当用户请求服务时,记录请求服务的时间.服务的类型.本次服务下载的数据量(3) 当用户下线时,记录下线时间.并把本次用户登录.请求服务过程中的所有信息持久化保存(如记录到数据库)     在ESFramework中,实现这种管理的是ITcpUserManager组件,通常,该组件由AS使用(因为在AS.FS.IRAS中只有AS是必

基于C/S的4层架构 —— ESFramework介绍之(6)

    ESFramework的4层结构的4层分别是:客户端(Client).应用服务器(AS).功能服务器(FS).数据库服务器.它们之间的联系图示意如下:     FS (FunctionServer),功能服务器,处理并且仅处理所有的功能性请求,不参与用户管理.状态保持等,提供最纯粹的功能服务.    AS (ApplicationServer),应用服务器,转发所有的功能请求给FS,并处理所有的非功能请求,并管理终端用户.进行状态保持.日志记录等.    上图中的功能服务器FS的个数可能

ESFramework介绍之(15)-- IRAS

    每个城市都对应着自己的AS,每个AS都有一组FS为之服务,而所有的AS都由一个IRAS联系/管理起来(回顾).前面我们已经提到,所有的FS都可以是动态添加/移除的,并且FS的地址也是自由可变的.同样,所有AS也都是可以动态添加/移除的,并且AS的地址也是可变的(这里AS与IRAS的机制同上文介绍的FS与AS之间的机制一样).但是,唯一不能随便变化的是IRAS的地址.这是因为,所有终端连的第一个服务器就是IRAS,然后从IRAS获取目的AS的地址,这样才去连接目的AS请求服务.所以,如果I

ESFramework介绍之(27)-- 支持OverdueMessage

   (本文适用于ESFramework V0.2+)     QQ上,你给好友发消息,如果对方不在线,则服务器会把这个消息持久化存起来,等好友下次上线时,再转发给他.像这样的消息在ESFramework中称为OverdueMessage.显然,MSN没有支持OverdueMessage. 当然了,ESFramework中的OverdueMessage不仅仅是文字消息,而是可以为任何类型的消息,比如,音频.视频.文件等等.     ESFramework中对所有P2P的消息的处理都是由P2PMe

ESFramework介绍之(26)-- 支持复杂插件(InnerDealer 和 InnerDispatcher)

    (本文内容适合于 ESFramework V0.2+)    通常,最单纯的情况是一个插件对应某一特定类型的功能请求,但是,在有的应用中也会出现这样的情况,有多种类型的功能请求相互关联.并且可能交叉,如果是这样,对应每种类型的请求都开发一个插件可能会非常困难,因为这可能会牵涉到插件之间的相互引用/访问,这违背了插件的"自治"性.最好的办法还是将它们放在一个插件中,通过ServiceItemIndex(你一定还记得消息头定义中除了ServiceKey外还有个ServiceItem

ESFramework介绍之(8)-- 客户端插件IPassiveAddin

    前文已经提到了,在IServerAgent的基础上,客户端也可以采用插件的结构形式,客户端插件需要实现IPassiveAddin接口.    我的想法是,当客户端主程序加载一个新的PassiveAddin时,可以在某个菜单的子Items上添加一项,当双击这个子菜单项时,则弹出该客户端插件提供的"业务操作窗体".这只是使用客户端插件的可行方式之一,你完全可以根据你的应用来决定使用形式.IPassiveAddin接口定义如下:  1     /// <summary> 

ESFramework介绍之(7)-- 服务器代理IServerAgent

    (本文原作于2006.03.15,第一次修正于2006.06.06,修正后适用于ESFramework V0.3+)     (本文是ESFramework对客户端开发的支持特性之一 ,如果要按顺序阅读,请转到ESFramework介绍(序))         分布式系统的构建一般有两种模式,一是基于消息(如Tcp,http等),一是基于方法调用(如RPC.WebService.Remoting).深入想一想,它们其实是一回事.如果你了解过.NET的Proxy,那么你会发现,方法调用和消

ESFramework介绍之(20)―― 插件自动升级

    当我们的服务平台搭建成功后,所需要做的主要事情就是开发服务端功能插件(IFunAddin)和客户端插件(IPassiveAddin),每个插件对(AddinPair)实现了一组相似或相近的需求/功能.     好了,我们已经开发了十多对插件对,然后分别XCopy到了各个服务器节点上,"整个系统"已经投入了运行.通过前面的介绍(回顾),相信大家对我们的"整个系统"有了个大致的映像.我们的IRAS服务器通常只存在于一个节点上,而我们的AS和对应的多个FS通常分布

ESFramework介绍之(30)―― 消息侦察者 INetMessageSpy

    (本文适用于ESFramework V0.2+)     现在我们回想一下,当网络组件(Tcp/Udp组件)接收到一个消息后,这个消息会流经哪些组件,然后再通过网络组件发送出去了.如果你研究过ESFramework V0.1,你会发现,消息"行走"的路线模型可以用下图表示出来:    请求消息(路径由黑线表示)经过网络组件后,会被Hook链中的各个Hook按照特定的顺序处理,然后到达消息处理器,消息处理器处理请求消息,并给出回复消息(路径由红线表示),回复消息同样再经过Hook