【转载】用 WCF 实现多层服务架构平台——业务适配器。
http://www.cnblogs.com/sofire/archive/2011/07/26/2117004.html
2010年,在企业工作的我开始意识到,“开发”与“业务”之间的冲突。一个系统开发员想要完美的展现一块功能的业务,这是一件难度较高的事情。精通业务的人,一般是直接的使用者。而一名开发员,仅仅在 DEBUG 时才会使用。这也突兀了一个问题:如何让开发与业务并行?
这是一个很纠结的问题。两个结论:要么让开发员熟悉业务,要么让业务员熟悉开发。同样,也是一个很无语的结果。
平台化系统迎应而生。这是去年的思想,在今年又发生了许多变化,所以将当时的想法写下来下,一是分享,二是记录,三是总结。
由于是企业信息化系统,所以当时直接选择了 .NET 3.5。后来升级到了 4.0。(最近,新版本缩水成了2.0囧~~)。
架构图(请多指教^.^):
·〉业务适配器
业务适配器可由一个或多个组成。该适配器服务公开两个服务。
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}");
}
}
业务层在向往适配层注册服务时,需要提供自己的“客户层应用程序名”,自己的“内外网地址”。这样,适配层在接收到客户层的申请时,便可以将注册的“连接地址”告诉客户层。
这样做的目的在于:保护实际业务的操作接口(虽然也是公开接口,但是客户端并不知道如何连接他,只能通过账号密码验证后,业务层发送过来的地址,客户端才可以连接)。
当然更重要的是,还可以实现一个应用多个“服务接口”,这样在分布式应用中是最有效缓解服务器压力的方法之一。
业务层需要注册信息已经确认了,那么便是将服务契约(适配层实现)。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary> 注册适配器的契约。
/// </summary>
[ServiceContract(CallbackContract = typeof(IRegisterCallback))]
public interface IRegisterContract
{
/// <summary> 将业务服务器注册到适配服务器。
/// </summary>
[OperationContract]
Result Register(RegisterInfo info);
[OperationContract(IsOneWay = true)]
void FromBusinessPing(object arg);
}
Callback契约如下(业务层实现):
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <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”,还有一个更加重要的作用。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary> 表示适配服务器发送至业务服务器的命令。
/// </summary>
public enum PingCommand
{
/// <summary> 表示这是一个普通的命令。
/// </summary>
None,
/// <summary> 表示这是一个业务服务器关闭的命令。
/// </summary>
Close,
/// <summary> 表示这是一个业务服务器更新的命令。
/// </summary>
Update,
}
通过上面的代码,园友便可以发现,在互动会话的过程中,可以做到“一方断线,多方支援”。而当业务层分布在10台不同的电脑上,更新业务层也是一个头疼的问题。所以,这个问题可以交给适配层来完成。适配层只要检测到本地“业务层”或“客户层”应用程序有版本变化,便会主动(客户层是被动)通知对方更新要求。这样客户层和业务层的更新便可以完美解决。
至此,业务层与适配层之间的交互,已经简单的告诉大家了。
2、客户层服务
我不知道先上代码会不会影响大家阅读,但是我认为阅读代码,比阅读文章更有效,更能吸收。客户层的契约如下:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/// <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套系统”的版本信息。一个是检测登陆界面的版本,一个是检测“业务系统”的版本。
客户层与适配层之间的交互是不多的,除了登陆凭证,就只有版本验证。但是适配层却起了至关重要。
邮箱:steven9801@163.com
QQ: 48039387