因为是模拟和试验,很多地方我都没有做得很完善,能省则省了,因为我采用的是对等结构,这里讲一下计算节点的结构,一个计算接点包含三个基本的功能:
1)外部任务请求管理:负责接收外部请求的计算任务,并根据客户提供的路由信息,转发给目标计算节点的内部任务请求管理功能;
2)内部任务请求管理:负责接收计算节点发过来的任务或命令,并调用本地的任务执行功能;当然。
3)任务执行管理:这个负责具体的任务执行,包括代码部署,代码执行等.由于代码的加载执行涉及到安全和卸载的问题,如果对客户的代码采用宿主外新建进程来执行,虽然可以解决代码执行完后的*问题,但控制和通信都比较麻烦,而且不利于客户代码利用宿主提供的服务;但如果直接在宿主进程中执行,就会引发安全问题(用户代码可以访问整个进程空间,而线程是没有隔离功能的),而且执行完后,用户的代码也没法卸载。因此,为了简化问题,这里利用了CLR的应用程序域(AppDomain)管理功能,可以相对比较完美的解决上述问题。
除了上述三个基本的功能外,计算节点还包括心跳服务,由于节点多了后,如果每个节点都向对方发布心跳信息,效率当然是非常低下的,因此这里我简单的设置了一个中心节点(相当于Master节点),大家都向中心节点发,然后从中心节点获取其它节点的状态。这种方式下,如果中心节点挂了,影响肯定非常大。但实际上要解决这个问题,可以设置2-3个这样的中心节点,如果第1个挂了,第2个中心节点就承担这种状态转发的责任。
每个节点由IP,端口和服务名唯一确定,但为了简化管理,用IP和端口唯一确定一个节点,每个节点的服务名保持一致会比较好。
节点间的通信,我直接用的是WCF,当然,自己写这一层也是可以的,不过我觉得没必要。
下面来看一下外部任务请求管理:
1)对外服务接口:
/// <summary> /// 用于提供给客户进行公共服务.
/// </summary>
[ServiceContract(ProtectionLevel= System.Net.Security.ProtectionLevel.None)]
public interface IComputingService
{
/// <summary>
/// 任务程序集部署服务
/// </summary>
/// <param name="TaskCommd"></param>
/// <returns></returns>
[OperationContract]
List<ExecResult> DeployTaskAssembely(TaskCommand TaskCommd);
/// <summary>
/// 任务或命令执行服务,并进行结果合并。
/// </summary>
/// <param name="TaskCommd"></param>
/// <returns></returns>
[OperationContract]
ExecResult ExecuteTaskCommand(TaskCommand TaskCommd);
/// <summary>
/// 任务或命令执行服务,但不合并结果.
/// </summary>
/// <param name="TaskCommd"></param>
/// <returns></returns>
[OperationContract]
List<ExecResult> ExecuteTaskCommandEx(TaskCommand TaskCommd);
/// <summary>
/// 提供在执行的计算命令查询服务
/// </summary>
/// <returns></returns>
[OperationContract]
List<CalcCommand> GetCalcCmmds();
/// <summary>
/// 提供当前家在的程序集清单服务
/// </summary>
/// <returns></returns>
[OperationContract]
List<CalcCommand> GetCurrAssemblies();
/// <summary>
/// 获取计算节点信息服务
/// </summary>
/// <returns></returns>
[OperationContract(ProtectionLevel = System.Net.Security.ProtectionLevel.None)]
List<CalcNodeInfo> GetCalcNodeInfos();
// TODO: 在此添加您的服务操作
}
这是对外服务,相应的客户端调用代理在DCSComputingSvcProxy类中实现。对于对外请求任务管理,核心的实现在DCSComputingDispather类中.
这个类负责根据用户提供的路由信息将任务转发到目标计算节点或者调用本地内部请求任务管理来执行自己该负责的任务。这些代码太长,就不贴了,关键技术点就是多线程处理。
内部请求任务管理的对计算节点服务接口如下:
/// <summary> /// 内部分布式计算服务,不对外公开. /// </summary> [ServiceContract] public interface IDistributedComputingService { /// <summary> /// 任务代码部属服务 /// </summary> /// <param name="CalcCommd"></param> /// <returns></returns> [OperationContract] ExecResult DeployTaskAssembely(CalcCommand CalcCommd); /// <summary> /// 任务执行服务 /// </summary> /// <param name="CalcCommd"></param> /// <returns></returns> [OperationContract] ExecResult ExecuteTaskCommand(CalcCommand CalcCommd); /// <summary> /// 提供当前节点正在执行的命令的清单服务 /// </summary> /// <returns></returns> [OperationContract] List<CalcCommand> GetCalcCmmds(); /// <summary> /// 获取当前程序集清单服务 /// </summary> /// <returns></returns> [OperationContract] List<CalcCommand> GetCurrAssemblies(); /// <summary> /// 计算节点信息清单. /// </summary> /// <returns></returns> [OperationContract] List<CalcNodeInfo> GetCalcNodeInfos(); /// <summary> /// 服务节点注册。 /// </summary> /// <param name="NodeInfo"></param> /// <returns></returns> [OperationContract] string RegisterCalcNodeInfo(CalcNodeInfo NodeInfo); }}
功能上跟对外服务类似,但简单很多。
明天继续...