学习WCF已有近两年的时间,其间又翻译了Juval的大作《Programming WCF Services》,我仍然觉得WCF还有更多的内容值得探索与挖掘。学得越多,反而越发觉得自己所知太少,直到现在,我也认为自己不过是初窥WCF的门径而已。 学以致用”,如果仅仅是希望能够在项目中合理地应用WCF,那么对于程序员而言,可以有两种选择,一种是“知其然而不知其所以然”,只要掌握了WCF的基础知识,那么对于一般的应用就足够了。要做到这一点就很容易了,微软秉承了一贯的方式,将WCF这门技术优雅地呈现给开发者,封装了复杂的实现逻辑,提供了易于调用的类库和相关的工具,使得开发者能够快速地完成WCF程序的开发。另外一种方式自然就是深度挖掘WCF的内部实现了,这是对WCF专家提出的要求。如果我们要应用WCF实现SOA解决方案,就会遭遇许多WCF的高级应用,如何合理、有效地应用WCF,并根据项目实际情况对WCF进行扩展,就成为了WCF专家必须解决的难题。 因此,如果要学习WCF,你必须找准自己学习的动机与目标,然后合理地安排自己的学习进度表,这才是正确的学习方式。本文试图对WCF的一些基础概念作一些试探性的阐述与分析,并以问答的方式组织,希望能够部分解答一些希望学习WCF,但犹自徘徊在门外的开发者。 1、WCF是什么? 从WCF所处的位置来看,它是包含在.NET 3.0(也包括.NET 3.5)之中的。我们注意比较.NET 3.0与.NET 2.0,其实唯一的区别就是.NET 3.0包含了WCF、WPF、WF(或者还有CardSpace)而已。因此,我们认为WCF是.NET框架的一部分,似乎并不为过。尤为关键的是,WCF并不能脱离.NET框架而单独存在(但非WCF客户端可以调用WCF服务),因此,虽然WCF是微软用以应对SOA解决方案的开发需求而专门推出的,但它并不是例如Spring、Struts那样的框架,也不是像EJB那样的容器或者服务器。微软真正符合SOA企业应用服务器角色的,我想应该是Biztalk Server。 严格的说,WCF就是专门用于服务定制、发布与运行以及消息传递和处理的一组专门类的集合,也就是所谓的“类库”。这些类通过一定方式被组织起来,共同协作,并为开发者提供了一个统一的编程模式。WCF之所以特殊,是在于它所应对的场景与普通的.NET类库不同,它主要用于处理进程间乃至于机器之间消息的传递与处理,同时它引入了SOA的设计思想,以服务的方式公布并运行,以方便客户端跨进程和机器对服务进行调用。实际上,WCF就是微软对于分布式处理的编程技术的集大成者,它将DCOM、Remoting、Web Service、WSE、MSMQ集成在一起,从而降低了分布式系统开发者的学习曲线,并统一了开发标准。 WCF与其它类库还有不同的地方,则在于WCF充分地体现了运行时环境的概念。对于早期使用WCF的开发人员而言,就可能知道如果在.NET 2.0下要开发WCF,还需要专门下载一个Runtime Component 3.0版,其中就包含了WCF、WF等内容。在.NET中一贯存在所谓“宿主”的概念,整个.NET Framework(或者说是CLR)就可以认为是一个大的宿主,就像Java的虚拟机一样。由于WCF对服务有着专门的需求,对于服务端,需要发布和运行服务;对于客户端,则需要调用服务;因而对于开发者,就需要编写定义、发布、运行、调用服务的相关代码。而服务就只能运行在特定的宿主上,这些宿主可以是控制台应用程序进程、Windows或Web应用程序进程,也可以是Windows服务进程,或者为最常用的IIS宿主。在宿主内部,则封装了通道堆栈,其中又包含了对协议、编码、消息传输、代理的处理。而在通道层的顶部,还提供了一个高级运行时,以针对应用程序的开发人员。 因而,我们可以这样认为,WCF是.NET Framework 3.x的一部分,它包含了用于服务定制、发布与运行以及消息传递和处理的运行时环境以及相关类的集合,它提供了在Windows平台下开发和部署服务的SDK。大致组成如下图所示: 2、WCF是怎样运行的? 如果从宏观的角度来分析WCF的运行机制,它的实现并不复杂。WCF的体系架构是基于一种拦截机制来实现的,负责传递和拦截消息的组件为通道,在客户端发出对服务端服务的调用时,首先会通过一个服务代理对象,将调用方提供的对象序列化到消息中,然后该消息则通过通道进行传递。通道不只是包括一个,而是多个通道对消息进行处理,包括传输、消息编码、管理会话、传播事务等,但最底层的通道总是传输通道。这些通道的构成形成了一个通道堆栈。由于对象已经被序列化,因而此时通道传递的消息可以跨进程或机器进行传递,利用传输通道传递到服务端。服务端的构成与客户端基本相似,仍然是通过通道栈中最底层的传输通道接收消息,然后解析消息编码,并一层层地往上传输。在服务端的通道栈之上,则是一个分发器(Dispatcher,或者说是调度器),它会首先对消息进行检查,然后选择一个客户端要调用的操作。在这个过程中,消息会被反序列化。 下图说明了WCF的整个运行过程: 由于WCF通过通道的方式传递消息,整个通道同时担当了侦听器和拦截器的功能,它可以根据服务的定义,在方法执行的前或后执行不同的操作,例如事务、会话管理、安全等。这些操作在WCF中,大多数都可以以Attribute的方式应用到服务契约上,这样的实现方式,就类似于采用了AOP(面向服务编程)的方法为服务提供了大量的基础功能,有助于简化服务开发者的工作。 3、为什么我们要选用WCF?在Windows平台下,尤其是在.NET平台下开发面向服务的应用程序,或者开发分布式系统,最佳选择就是WCF。为什么呢?原因就在于WCF涵盖了之前微软推出的所有用于分布式开发的技术,包括Remoting、Web Services、WSE、MSMQ等,并以一种统一的编程模式来实现。 WCF既支持具有互操作性的Web服务,也能够实现.NET客户端与.NET服务端的通信,提供了分布式事务的支持,同时在安全性上,它完全遵循了WS-*的标准,此外,它还支持队列服务,可以非常方便地利用消息队列完成异步操作与脱机调用。而这些功能,以前的技术都只是部分的实现。如下表所示: 特性 Web Service .NET Remoting Enterprise Services WSE MSMQ WCF 具有互操作性的Web服务 支持 支持 .NET到.NET的通信 支持 支持 分布式事务 支持 支持 支持WS标准 支持 支持 消息队列 支持 支持 WCF同时也使得面向服务编程更加简单而统一了。如果采用旧有的技术,由于各种技术的编程模型完全不一致,使得程序的迁移非常的困难。例如,最初采用.NET Remoting技术开发的分布式系统,由于业务需求的变化,要求发布具有互操作性的Web服务,就需要重新定义服务。并且,客户端的调用方式也发生了变化,需要添加Web引用,通过UDDI去发现服务。 采用WCF则不然。WCF引入了用通道,它封装了消息的通信细节,例如编码、事务处理、安全等,然后又通过引入绑定的概念,封装了通道的组成顺序与处理细节。最后,引入了独有的Endpoint元素,集成了地址、绑定和契约之间的“三位一体”,以最简单的方式定义和发布服务。 每种绑定对应不同的传输协议、消息编码格式和版本以及安全、可靠性和事务模式。WCF也提供了扩展绑定的方式,例如通过CustomBinding或者定义派生与Binding的类。 WCF的契约包括服务契约、数据契约和消息契约(特别的,还包括了错误契约,用于异常的处理)。其中服务契约为面向服务应用程序的核心,通过它可以定义服务。数据契约则为服务所要传递的数据。由于服务的调用需要跨进程或机器进行通信,就需要服务数据必须能够被序列化和反序列化。虽然.NET本身提供了数据的序列化功能,但WCF的数据契约更加符合服务数据的定义习惯。至于消息契约,则可以将服务数据定义为消息,包括XML文本格式、MTOM(消息传输优化机制)格式和二进制格式。 绑定(Binding)、契约(Contract)与服务的地址(Address)组合在一起,则形成了终结点(Endpoint),如下图所示: Address是Endpoint的网络地址,它标记了消息发送的目的地。Binding描述的是如何发送消息,例如消息发送的传输协议(如TCP,HTTP),安全(如SSL,SOAP消息安全)。Contract则描述的是消息所包含的内容,以及消息的组织和操作方式,例如是单向,双向还是请求/响应方式。 引入终结点可以说是WCF的一个伟大创举,通过它使得我们能够更加容易的发布和管理服务,尤其是发布和管理多个服务。每个服务必须至少拥有一个终结点,而客户端正是通过终结点知道服务的相关信息,例如地址、消息编码格式、传输协议以及服务的内容,然后在进行正确的调用。最特别的是,同一个服务可以定义多个终结点,每个终结点可以是不同的地址、不同的绑定方式,以便于满足多个客户端的不同需要。而对于服务的发布者而言,我们只需要管理终结点的配置,就可以完成对服务的管理,这也为服务的托管提供了便利。 5、WCF主要包含哪些内容? Juval的《Programming WCF Services》一书基本已经涵盖了WCF技术的方方面面。概括来讲,主要包括绑定、服务契约、数据契约、消息传递、异常处理、实例模式、并发处理、事务处理、安全以及队列服务等。 绑定属于WCF基本的技术要素,是WCF进行通信处理的基础。了解绑定的相关知识,有助于开发WCF应用程序。因为在不同的业务需求下,可能对通信方式、协议、消息编码等多个方面会有不同的要求。在配制、发布和运行服务时,都需要对绑定进行操作。因而,我们必须掌握WCF内置绑定的相关属性,熟悉绑定元素的相关配置。此外,在一些高级应用上,我们还要掌握自定义绑定的方式,了解在自定义绑定时,添加绑定元素的方式与顺序,了解Binding基类的相关属性和方法。以及与绑定配置相关的类。 服务契约是WCF的主要处理对象,服务的定义和设置正是通过服务契约实现的。除了了解ServiceContract和OperationContract的基础应用之外,还需要比较WCF服务编程与普通的.NET编程之间的区别,例如服务的继承与多态是怎样实现的。最重要的是如何根据SOA的思想划分服务的边界,确定服务的粒度大小,这需要从系统的易用性、可扩展性、性能等多个方面进行权衡。 服务契约中关于操作的定义要受到很多约束,其中最重要的就是对数据的处理,这也是WCF引入数据契约的目的。由于WCF的特殊性,因而我们需要了解一些特殊数据类型的序列化方式,例如泛型类型、集合、DataSet等。 约束服务操作定义的还包括对异常的处理,WCF对异常有一套特殊的处理方式,可以根据实际的情况,确定异常消息是否需要进行通信,以及出现异常时,是否要求停止服务实例,或继续维持会话。 WCF为消息传递提供了非常大的灵活性。它提供了专门的Message类以及相关的读写器,例如XmlDictionaryReader和XmlDictionaryWriter等对消息进行读写,这其中包含了对消息版本、编码格式、消息标头和正文等的操作。同时,WCF还提供了消息与方法之间的映射。在提供系统的互操作性时,了解WCF的消息传递方式很有必要。 根据不同的需求,WCF将实例模式分为PerCall,Single和PerSession三种方式。通过设置服务行为的InstanceContextMode属性,来管理服务实例的生存周期,可以简化开发人员的工作。我们只需要了解这三种实例模式的特性即可。通常情况下,我建议服务采用PerCall模式,如果需要维持服务与客户端之间的会话,则可以采用PerSession模式。只有在对性能和可伸缩性没有太大要求的情况下,才可以采用Single模式。 关于事务处理、并发处理以及安全,主要都是通过相关的服务行为进行设置和管理。这些内容都是WCF高级应用所必需掌握的内容,尤其对于开发企业级应用系统而言。至于队列服务,主要是利用了消息队列,以实现系统可以脱机访问服务,并保持服务状态的同步。 6、WCF主用适用于哪些应用场景? WCF本身就是微软为了应对SOA战略所推出的一套基于.NET Framework下的SDK。当我们在Windows平台下,基于.NET框架开发一套系统,同时要求这套系统需要与其它平台例如WebLogic、WebSphere或者JBoss进行交互时,就需要采用WCF技术了。这是因为WCF的互操作性所决定的。 目前来看,WCF在企业应用中还没有得到大量的应用,但随着开发者对.NET 3.x的深入了解,WCF会逐渐深入人心。由于WCF集成了.NET编程开发的习惯,对于.NET开发人员而言,就大大地降低了学习曲线,此外,WCF保留了对旧有技术包括Web Service、.NET Remoting、MSMQ、WSE的支持,同时对于旧有技术到WCF的迁移也提供了非常好的解决方案。因而,如果.NET人员若要应用SOA的解决方案,那么WCF无疑就是最佳选择。单以开发而论,利用WCF定义服务、公开服务以及运行服务,利用WF实现工作流的设计、运行与管理、利用WPF作为具有丰富表现的富客户端,或者通过AJAX+SliverLight开发调用WCF服务的客户端,以及通过CardSpace完成对服务安全的设置与管理,就足以实施一整套的SOA解决方案。同时,WCF还可以结合Biztalk以及Sharepoint,使得SDK能够搭载于应用服务器之上,更大程度地满足企业应用的需要。 除了实施SOA解决方案之外,WCF对于开发分布式系统而言,也是得天独厚的。当我们的应用系统需要在多个服务器上运行不同服务,以支撑大量的访问负荷、大数据量处理、数据挖掘等工作时,就可以利用WCF开发不同的服务,然后托管在不同的服务器上,此时,整个系统就可以有效地利用服务器资源,减轻系统负荷。