用 WCF 实现多层服务架构平台——业务适配器。

·〉写在前面

09年08月,我开始着手第一个公用 DLL 封装。时至今日,历经了四个大版本。随着工作年限不断增加,不断的推翻,不断的重写。如今,它已是我日常开发中不可缺少的一组套件。

2010年,在企业工作的我开始意识到,“开发”与“业务”之间的冲突。一个系统开发员想要完美的展现一块功能的业务,这是一件难度较高的事情。精通业务的人,一般是直接的使用者。而一名开发员,仅仅在 DEBUG 时才会使用。这也突兀了一个问题:如何让开发与业务并行?

这是一个很纠结的问题。两个结论:要么让开发员熟悉业务,要么让业务员熟悉开发。同样,也是一个很无语的结果。

平台化系统迎应而生。这是去年的思想,在今年又发生了许多变化,所以将当时的想法写下来下,一是分享,二是记录,三是总结。

由于是企业信息化系统,所以当时直接选择了 .NET 3.5。后来升级到了 4.0。(最近,新版本缩水成了2.0囧~~)。

架构图(请多指教^.^):

  ABC Pattern

·〉业务适配器

业务适配器可由一个或多个组成。该适配器服务公开两个服务。

1、业务层服务

业务层可以通过连接到“业务适配器”进行注册。将自身的“公共地址”注册给服务层,并且注册自己所对应的“应用程序名”。这有点像这样:

    /// <summary> 业务服务器的注册信息。
/// </summary>
[Serializable]
public class RegisterInfo
{
protected string _BusinessID;
public string BusinessID
{
get
{
return this._BusinessID;
}
}

protected string _ApplicationName;
public string ApplicationName
{
get
{
return this._ApplicationName;
}
}

protected string _PrivateIP;
public string PrivateIP
{
get
{
return this._PrivateIP;
}
}

protected string _PublicIP;
public string PublicIP
{
get
{
return this._PublicIP;
}
}

protected string _AddressFormat;
public string AddressFormat
{
get
{
return this._AddressFormat;
}
}

public RegisterInfo(string bussineID, string applicationName, string privateIP, string publicIP)
{
this._BusinessID = bussineID;
this._ApplicationName = applicationName;
this._PrivateIP = privateIP;
this._PublicIP = publicIP;
}

public void SetAddressFormat(string address)
{
this._AddressFormat = address.Replace("localhost", "{0}");
}

}

业务层在向往适配层注册服务时,需要提供自己的“客户层应用程序名”,自己的“内外网地址”。这样,适配层在接收到客户层的申请时,便可以将注册的“连接地址”告诉客户层

这样做的目的在于:保护实际业务的操作接口(虽然也是公开接口,但是客户端并不知道如何连接他,只能通过账号密码验证后,业务层发送过来的地址,客户端才可以连接)。

当然更重要的是,还可以实现一个应用多个“服务接口”,这样在分布式应用中是最有效缓解服务器压力的方法之一。

业务层需要注册信息已经确认了,那么便是将服务契约(适配层实现)。

IRegisterContract.cs
    /// <summary> 注册适配器的契约。
/// </summary>
[ServiceContract(CallbackContract = typeof(IRegisterCallback))]
public interface IRegisterContract
{
/// <summary> 将业务服务器注册到适配服务器。
/// </summary>
[OperationContract]
Result Register(RegisterInfo info);

[OperationContract(IsOneWay
= true)]
void FromBusinessPing(object arg);

}

Callback契约如下(业务层实现):

IRegisterCallback.cs
    /// <summary> 注册适配器的回调契约。
/// </summary>
public interface IRegisterCallback
{
/// <summary> 当适配器接收登录验证时的回调函数。
/// </summary>
[OperationContract]
Result ClientLogin(
string uid, string pwd);

/// <summary> 激活业务服务器的状态。
/// </summary>
[OperationContract(IsOneWay = true)]
void FromAdapterPing(PingCommand command, object arg);
}

这两个接口用于业务层与适配层之间的会话。每当有一个新的客户端连接到适配层时,适配层会向其索要登录凭证,然后转发给对应的应用名的业务层。当业务层校验成功,适配层才会将这个“隐藏于公众”的地址抛给客户端。IRegisterContract.FromBusinessPing 是适配层接收到业务层的“激活测试”,业务层要告诉适配层:“我正在运行,可以将客户给我”。IRegisterCallback.FromAdapterPing 也是同样的道理。这样双方互 PING,可以确保业务层和适配层的在线情况。

当然,这么繁琐的“双方互PING”,还有一个更加重要的作用。

PingCommand.cs
    /// <summary> 表示适配服务器发送至业务服务器的命令。
/// </summary>
public enum PingCommand
{
/// <summary> 表示这是一个普通的命令。
/// </summary>
None,
/// <summary> 表示这是一个业务服务器关闭的命令。
/// </summary>
Close,
/// <summary> 表示这是一个业务服务器更新的命令。
/// </summary>
Update,
}

通过上面的代码,园友便可以发现,在互动会话的过程中,可以做到“一方断线,多方支援”。而当业务层分布在10台不同的电脑上,更新业务层也是一个头疼的问题。所以,这个问题可以交给适配层来完成。适配层只要检测到本地“业务层”或“客户层”应用程序有版本变化,便会主动(客户层是被动)通知对方更新要求。这样客户层和业务层的更新便可以完美解决。

至此,业务层与适配层之间的交互,已经简单的告诉大家了。


2、客户层服务

我不知道先上代码会不会影响大家阅读,但是我认为阅读代码,比阅读文章更有效,更能吸收。客户层的契约如下:

ILoginContract.cs
    /// <summary> 客户层登录契约。
/// </summary>
[ServiceContract]
public interface ILoginContract
{
/// <summary> 获取当前服务器的所有业务应用系统。
/// </summary>
string[] Applications { [OperationContract] get; }

/// <summary> 指定应用名以及网络,登录服务器。如果登录成功,则返回一个业务服务器的访问地址。
/// </summary>
[OperationContract]
ClientLoginResult ClientLogin(
string applicationName, bool privateNetwork, string uid, string pwd);

/// <summary> [登录限制] - 获取当前客户端的版本。
/// </summary>
string ClientVersion { [OperationContract] get; }
/// <summary> [登录限制] - 读取当前最新版本的客户端压缩包流的总长度。
/// </summary>
int ClientArchiveLength { [OperationContract]get; }
/// <summary> [登录限制] - 读取当前最新版本的客户端压缩包。
/// </summary>
[OperationContract]
byte[] ReadClientArchive(int index);

/// <summary> [登录限制] - 获取当前业务应用系统的版本。
/// </summary>
string ApplicationVersion { [OperationContract] get; }
/// <summary> [登录限制] - 读取当前最新版本的业务应用系统压缩包流的总长度。
/// </summary>
int ApplicationArchiveLength { [OperationContract]get; }
/// <summary> [登录限制] - 读取当前最新版本的业务应用系统压缩包。
/// </summary>
[OperationContract]
byte[] ReadApplicationArchive(int index);
}

"Applications"属性是用于告诉客户端,我(适配层)拥有多少个“终端应用程序”,这样,客户端便可以实现“一个登陆界面,N套系统”的理念了。

“ClientLogin()”函数就不用多解释了,主要是验证成功后,“ClientLoginResult”的返回值包含了(“业务层名称”和“业务层地址”)。

而剩下的东西,便是前面所提到的“一个登陆界面,N套系统”的版本信息。一个是检测登陆界面的版本,一个是检测“业务系统”的版本。

客户层与适配层之间的交互是不多的,除了登陆凭证,就只有版本验证。但是适配层却起了至关重要。

这样,我们便可以实现:客户层是和一个“不知道是谁”的业务层进行“乱搞”了。这是不是有点像无间道?:)

最后,感谢您阅读此文。

SOFIRE

posted @ 2011-07-26 11:55  Treenew Lyn  阅读(3230)  评论(16编辑  收藏  举报