对于漫游用户的支持和跨区域功能请求的支持是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
NetMessageDealRequestMessage(NetMessagereqMsg)
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_4AsasRemotingAccesser
=
(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
NetMessagehookedMsg
=
this
.netMessageHook.CaptureBeforeSendMsg(reqMsg);
21
22
//
目标AS处理跨区域请求
23
return
asRemotingAccesser.DealRequestMessage(hookedMsg);
24
}
25
catch
(Exceptionee)
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,NetMessagemsg);
//
返回DataSendResult的常量
}
ESFramework中的IToClientSender实现是ToClientSender,它可以包含一组具体的Sender,每次发送消息时,ToClientSender会按照指定的顺序调用每个Sender,知道有一个发送成功为止。ToClientSender实现如下:
public
class
ToClientSender:IToClientSender
{
private
IListsenderList
=
new
ArrayList();
public
ToClientSender()
{
}
#region
property
public
IListSenderList
{
set
{
this
.senderList
=
value;
}
}
#endregion
#region
IToClientSender成员
public
int
HookAndSendMessage(
string
userID,NetMessagemsg)
{
int
theResult
=
DataSendResult.UserIsOffLine;
foreach
(IToClientSendersender
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,NetMessagemsg)
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,NetMessagemsg)
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_4AsasRemotingAccesser
=
(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
NetMessagehookedMsg
=
this
.netMessageHook.CaptureBeforeSendMsg(msg);
24
return
asRemotingAccesser.HookAndSendMessage(userID,msg);
25
}
26
catch
(Exceptionee)
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 可复用的通信框架(序)