转眼微软的WCF已走过十个年头,它是微软通信框架的集大成者,将之前微软所有的通信框架进行了整合,提供了统一的应用方式。记得从自己最开始做MFC时,就使用过Named Pipe命名管道,之后做Winform时,使用过Remoting,再之后做B/S架构时,就会经常使用.NET平台下的Web Service,直到使用上WCF。看上去有了一些WCF的使用经验,实则不然,比如对安全、分布式事务、可靠会话等主题仍然接触甚少,因而决定重新回顾学习一下相关知识,尤其是对WCF框架的理解(已于2015年开源,可下载源码,https://github.com/dotnet/wcf/)。很多大公司都构建了自己的SOA框架,不过基本上都是以WCF框架为基础,对其进行了相应的简化和微调。因此学习该框架,可以触类旁通,对应用和搭建自有的SOA架构也有很大的帮助。当然,个人认为WCF已足够强大,并且其管道模式有极强的扩展性,可以通过自定义绑定满足绝大部分的需求。整个学习过程将参考蒋金楠大师的《WCF全面解析》一书,本章主要介绍WCF的基本概念和传说中的"ABC",Let go。
在介绍WCF之前,不得不提一个称为SOA(Service Orientation Architecture)的概念,也就是我们常说的面向服务的架构,这是一个很老的概念了。即使如此,如果要以SOA为题,写一遍2000字的论文,感觉仍然很难下手,说明对概念理解还不够深刻(之后打算专门撰文一篇,为软考做准备)。实际上,其是构建大型软件应用的一种重要理念,并不是什么具体的技术或者平台。这个提法的出现其实有一个过程,就是在过去软件的架构说到底是基于数据库的(至于什么基于组件、基于领域等概念,其实是在应用范畴的,而不是架构范畴的概念),比如不同的两个系统的交互,往往是通过公用同一个数据库,或者通过Job等方式同步两个应用各自的数据,最终都是以数据为中心的。这种架构的优点是开发快速,与数据库紧密相连,事务性很好,适用于中小系统;缺点是因为各个系统都可以直接和数据库连接,层次不清晰,当系统越来越庞大时,运维成本越来越大,此外,其可控性、安全性、扩展性也相对较差。而SOA是以上缺点的一个很合适的解决方案,比如:基于开放的标准,使得可以跨平台调用(.NET, J2EE…);基于自治的服务,便于安全性的控制和服务限流;基于契约,将各个子系统解耦。
接下来,详细回顾一下微软的所有分布式通信技术,包括如下4种具体技术。
COM和DCOM:COM基于组件设计,通过GUID唯一标识、IKnown与其他接口进行互操作,例如ActiveX,DCOM是COM的分布式版本,提供了可靠传输、安全等支持。
.NET Remoting:其基于信道栈的"管道式"消息处理和传输机制,支持TCP,UDP等传输协议。
Web Service:其提供跨平台的互操作性,构建在ASP.NET平台上,基于一系列开放的标准,包括XML、XSD、SOAP和WSDL等。此外,微软还通过WSE(Web Service Enhancement)组件为Web服务提供WS-*规范的支持。
MSMQ(Message Queuing):MSMQ通过异步通信的方式,解耦了服务的提供者和调用者,为系统提供了可观的伸缩性和可用性,并支持可靠信息传输、错误处理和对事务的支持。
Tip:
J2EE架构其实也有相对应的技术,例如官方的Java RPC,WebService,JMS,第三方的Axis,RabbitMQ等。
本节最后通过一个非常简单的自寄宿的WCF示例来熟悉WCF的应用以及引入传说中的三要素"ABC",Address服务地址、Binding服务绑定、Contract服务契约,之后将分节进行详细介绍
1 Contract: 2 [ServiceContract] 3 public interface IAddService 4 { 5 [OperationContract] 6 CompositeType Add(CompositeType a, CompositeType b); 7 } 8 [DataContract] 9 public class CompositeType 10 { 11 [DataMember] 12 public int PartA { get; set; } 13 [DataMember] 14 public string PartB { get; set; } 15 } 16 17 public class AddService : IAddService 18 { 19 public CompositeType Add(CompositeType a, CompositeType b) 20 { 21 return new CompositeType() { PartA = a.PartA + b.PartA, PartB = a.PartB + b.PartB }; 22 } 23 } 24 25 Host: 26 static void Main(string[] args) 27 { 28 using (var host = new ServiceHost(typeof(AddService))) 29 { 30 host.Opened += (target, eventArgs) => Console.WriteLine("AddService已经启动,请按任意键终止服务!"); 31 host.Open(); 32 Console.Read(); 33 } 34 } 35 36 Config: 37 <system.serviceModel> 38 <behaviors> 39 <serviceBehaviors> 40 <behavior name="metadataBehavior"> 41 <serviceMetadata httpGetEnabled="True" httpGetUrl="http://127.0.0.1:9901/addservice/metadata"/> 42 </behavior> 43 </serviceBehaviors> 44 </behaviors> 45 <services> 46 <service name="Sory.Entertainment.WCF.AddService" behaviorConfiguration="metadataBehavior"> 47 <endpoint address="http://127.0.0.1:9901/addservice" binding="wsHttpBinding" contract="Sory.Entertainment.WCF.IAddService"/> 48 </service> 49 </services> 50 </system.serviceModel> 51 52 Client: 53 static void Main(string[] args) 54 { 55 using (var client = new WcfService.AddServiceClient()) 56 { 57 var result = client.Add(new WcfService.CompositeType { PartA = 1, PartB = "Hello, " }, new WcfService.CompositeType { PartA = 2, PartB = "World!" }); 58 59 Console.WriteLine(string.Format("PartA: {0}, PartB: {1}", result.PartA, result.PartB)); 60 } 61 Console.Read(); 62 }