原来就想写一些关于分布式的设计。正好目前开发的GIX4项目中,需要对客户端的许多操作进行记录。这个功能的设计或多或少能说明,一般情况下,多层的分布式系统应该如何设计。现在我就对这个功能进行描述。
功能描述
GIX4项目中有个审核日志记录功能,它需要以下功能:
需要把客户的所有操作记录下来,以便支持审计。如,当用户进行:登录、打开模块、查看数据、点击按钮……等操作时,把相关的数据全部在服务器的数据库中记录下来,这些数据有:操作类型、操作时间、操作内容、操作者、客户端机器名等。
在服务端对某些特定的事件进行记录,如:计算出错。
设计目标
这个模块需要支持以下目标:
*为客户端代码、服务器端代码都提供简单统一的接口。
*需要异步记录。
*如果扩展为多级物理层时,也需要使用相同的接口。
设计
先把设计完成的图贴出来,然后再逐一描述:
图1 全部类图
通用部分
首先,API设计之初,先根据需求,定出使用的接口。它们包含两个类:一个是贫血数据类AuditLogItem,一个是对AuditLogItem进行操作的静态类AuditLogService。AuditLogService暂时公布两个静态方法,一个记录日志方法Log(),另一个方法是异步记录LogAsync()。如下:
由于各物理层都使用AuditLogService的方法,而实现不同。这里把使用了Provider模式,提取接口IAuditLogProvider,这里就不再需要异步方法了:
/// <summary> /// 审计功能提供程序 /// </summary> public interface IAuditLogProvider { /// <summary> /// 记录指定的日志 /// </summary> /// <param name="log"></param> void Log(AuditLogItem log); }
除了最终与数据库通信的服务器外,其它节点都默认使用ClientAuditLogProvider作为提供程序。此提供程序使用了ICommunication,而ICommunication则是负责向“下一节点”通信并提交日志记录功能。
GIX4实现部分
这部分主要是两个类:ServerAuditLogProvider 和 CSLACommandCommunication。
ServerAuditLogProvider 使用CSLA的类库把AuditLogItem转换为相应的数据库模型存入数据库中:
public class ServerAuditLogProvider : IAuditLogProvider { public void Log(AuditLogItem log) { var dbItem = AuditItem.New(); dbItem.Title = log.Title; dbItem.Content = log.Content; dbItem.User = log.User; dbItem.MachineName = log.MachineName; dbItem.Type = log.Type; dbItem.LogTime = log.LogTime; dbItem.Save(); } }
CSLACommandCommunication 则是使用WCF和CSLA命令模式作为基础框架,让当前节点把请求发送给下一节点,下一节点接到请求后,使用本节点的提供程序Provider来处理请求。其中重要的代码在内部类AuditServerCommand中,如下:
[Serializable] public class AuditServerCommand : Csla.CommandBase { private AuditLogItem _logItem; public AuditServerCommand(AuditLogItem logItem) { if (logItem == null) throw new ArgumentNullException("logItem"); this._logItem = logItem; } protected override void DataPortal_Execute() { //server log AuditLogService.Log(this._logItem); } }
整个过程
整个过程其实很简单,一图胜千言:
后记
其实本次设计过程的思路很简单:分析要提供的API(场景驱动)、分析分布式实现的差异、编写基础部分、编码与重构。
其实熟悉CSLA的人应该知道,它里面的通信机制也是采用了类似的方式实现的。所以这应该算是一种分布式的设计模式吧。 :)