1.2 C/S模型的演变
经典的基于网络消息的客户机/服务器模型如下图所示:
图1-1 基于消息的C/S模型
在这个模型中,服务器首先启动并开始侦听客户机的远程连接请求,当客户机发起连接请求并被服务器接受后,就可以向服务器依次发送预定义好的请求消息。服务器在收到客户机的请求消息后就可以解析这些消息,然后执行所请求的操作,最后构造并向客户机发送应答消息,客户机通过解析应答消息就可以知道请求是否执行成功以及执行的结果。这些消息可能是二进制结构的字节流,也可能是文本格式的字节流,如XML文档消息,等等。
在一个C/S系统中,通常服务器要能同时为多个客户机服务,小到几个、几十个,大到几千、几万个。任何一个时刻,在服务器和各个客户机之间都可能同时有消息进行传递,如下图所示:
图1-2 客户机/服务器系统
如果让用户自己编写代码来实现客户机和服务器之间往来业务消息的封装、发送以及接收、解析、分发和处理等所有任务,将是一个很艰巨、很繁琐和很耗时的工作,而且很容易出错。特别是二进制结构的消息,除了消息本身结构不清晰和不易读外,还需要在发送前将多字节数据从本地机器的字节序转换为网络序,以及在接收后进行相反的转换。特别是服务器程序,还要能够同时处理多个客户机的连接请求和服务请求,编写服务器程序底层的通信连接管理和消息收发服务又是一个很大的挑战。特别地,如果用户每开发一个C/S应用系统,都要将这些工作重来一次的话,不仅费时费力费钱,而且没有必要;即使用户这样做了,也会存在很多问题。首先,消息格式是自定义的非标准格式,可能不会考虑可扩展性等问题;其次,用户可能仅仅针对当前使用的平台进行开发,不会考虑跨平台可移植性,所以与操作系统API和网络通信协议绑定得很死,不能适应将来的移植和扩展要求;最后,如果没有很好的规划和隔离,如果没有设计好软件的架构,客户机和服务器之间通信的代码、消息解析和分发的代码就会和上层业务处理的代码纠缠在一起,每当新增业务功能及新增消息时,都将导致大面积的代码修改。
随着面向对象思想和编程技术的出现及发展,又出现了面向对象的C/S模型。从面向对象的角度看,一个对象A调用另一个对象B的某个操作,对象A就可以被看作一个客户对象,而对象B也可以被看作一个服务对象。同理,对于跨进程、跨网络的客户机和服务器来说,也可以用对象访问的方式进行建模,如下图所示:
图1-3 面向对象的C/S模型
面向对象的模型特别适合于创建分布式C/S应用。特别是大规模的复杂应用系统,往往需要网络中的多台主机和多个应用程序协同工作以均衡负载,而这些主机之间并没有明确的客户机和服务器角色分工(客户机/服务器的角色被淡化了)。使用面向对象的架构,就可以让设计和开发人员直接从用户的需求出发来考虑问题,并将这些需求直接映射为客户机和服务器之间的面向对象接口(例如C++抽象类或Java的Interface),使开发人员直接面向远程对象的服务接口进行开发,完全屏蔽了底层系统之间消息传递和消息处理的细节,CPU硬件结构、操作系统I/O以及网络协议的差异都可以用中间件来适配,甚至不需要去关心客户机和服务器在网络中的位置、它们之间如何建立连接以及如何进行通信等问题。目前这样的分布式面向对象系统有很多,比如CORBA、COM/DCOM、ACE-TAO、J2EE-RMI等。COM/DCOM是面向组件的C/S模型,它们是面向对象C/S模型的更高级形式。
下面简单介绍一下CORBA、COM/DCOM和ACE,其他分布式面向对象中间件所遵循的原理和技术是类似的。