快速入门系列--WCF--01基础概念
转眼微软的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服务契约,之后将分节进行详细介绍
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
本节将介绍URI、端口共享、请求监听和消息分发等概念。正如之前所说的,WCF服务是通过终结点EndPoint发布,而终结点由地址、绑定和契约三要素组成,其中地址用于定位服务,并提供额外的寻址信息和认证信息。既然是服务定位,首先引入URI的概念,URI的全称为Uniform Resource Identifier统一资源标识,其形式是,[Schema传输协议]://[主机名|域名|IP地址]:[端口号]/[资源路经],其中支持的协议类型如下表所示。
协议类型 |
解释 |
HTTP/HTTP |
前者是互联网时代的核心--超文本传输协议,其是建立在TCP/IP协议簇上应用层协议。特点无状态、无连接、提供简单请求-回复消息传输方式;后者是采用了SSL(TLS)的HTTP,提供数据加密,实际上,大部分主流网站已实现全站HTTPS。 |
Net.TCP |
TCP全称传输控制协议,属于传输层协议,基于网络层IP协议,是应用层HTTP协议的基础。其特点是有状态、支持全双工、支持可靠通信,其是基于连接的协议,在数据传输前通过3次"握手"创建连接,在传输结束后,通过4次"握手"终止连接。 |
Net.Pipe |
命名管道是Windows等操作系统实现跨进程通信(Inter Process Communication, IPC)的标准实现方式,虽然命名管道本身可以跨机器通信,不过WCF中的命名管道专注于同一台机器中的跨进程通信,因此其主机名为localhost,此外由于基于同一台机器,端口变得没有意义。 |
Net.Msmq |
消息队列提供了支持离线的通信机制,其包括公共消息队列和私有消息队列两种方式,前者需要注册到AD域中。此外,除了存储业务数据消息的普通队列之外,还有存储消息拷贝的日志队列、存储确认消息的管理队列、存储回复消息的回复队列和存储死信消息的死信队列等。 其URI格式为: net.msmq://sory.com/private/xxxservice |
之前提及的核心概念终结点在WCF中,通过System.ServiceModel.Description.ServiceEndpoint类表示,其包括Address、Binding、Contract三个核心属性。其中的Address是EndpointAddress的实现类,其包含Uri、Headers、Identity三个属性,Uri即是服务的唯一标识,也是服务的目标地址,且这个地址可以使物理的,也可以是逻辑的。这儿的Headers其实就是SOAP消息中的消息头(类似于Http协议的,也包括消息头和消息体,前者主要提供一些控制信息,后者存放数据部分),它默认通过DataContractSerializer进行序列化和反序列化,最终转化为SOAP消息的MessageHeader,相应配置如下所示,添加了服务端消息头后,在客户端也需要增加相应消息头,否则会被地址过滤器给过滤掉(之后的客户端通过ChannelFactory调用服务的示例中可以看到)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <endpoint address="http://127.0.0.1:9901/addservice" binding="wsHttpBinding" contract="Sory.Entertainment.WCF.IAddService"> 2 <headers> 3 <authentication xmlns="http://www.sory.com/">{12345678}</authentication> 4 </headers> 5 </endpoint>
补充一点的是,可以通过将服务的ServiceBehavior特性中的AddressFilterModel属性设置为Any,跳过消息头的检验。
在基础概念一节的代码示例中,可以看到WCF通过ServiceHost完成服务寄宿,其中通过AddServiceEndpoint实现终结点的添加,当然也可以通过配置文件的方式添加终结点,在配置文件的<system.serviceModel>模块的<service>子节点中添加<endpoint>,并补全address、binding、contract属性,注意在IIS寄宿的情况下,无需提供address,因为.svc文件的地址就是服务的地址。同时,可以通过ServiceHost的Description属性(.NET中习惯使用Description获取元数据相关信息,无论是哪一种框架)获取终结点和服务行为的相关信息。
此外,除了使用绝对地址来指定某个服务的终结点地址外,还可以通过"基地址+相对地址"的方式,其配置形式如下,需要注意一种类型的协议只能有一个基地址,并且当一个服务实现类同时实现了多个服务接口时,该终结点地址可以共享。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <service name="XXX" behaviorConfiguration="XXX"> 2 <host> 3 <baseAddresses> 4 <add baseAddress="net.tcp://127.0.0.1/baseservice"/> 5 </baseAddresses> 6 </host> 7 </service>
客户端通过服务代理实现对服务的调用,包括两种方式:通过服务引用或者借助SvcUtil.exe工具来生成服务代理类,该生成类继承自ClientBase<TChannel>;直接通过ChannelFactory<TChannel>创建服务代理。前者比较简单,只需要在<system.serviceModel>的子节点<client>中添加对应的<endpoint>节点,然后直接生成的对应的Client类即可,后者如下所示。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 var uri = new Uri("http://127.0.0.1:9901/addservice"); 2 var header = AddressHeader.CreateAddressHeader("authentication", "http://www.sory.com/", "{12345678}"); 3 var address = new EndpointAddress(uri, header); 4 var binding = new WSHttpBinding(); 5 var contract = ContractDescription.GetContract(typeof(IAddService)); 6 7 var endpoint = new ServiceEndpoint(contract, binding, address); 8 using (var factory = new ChannelFactory<IAddService>(endpoint)) 9 { 10 var channel = factory.CreateChannel(); 11 var result = channel.Add(new CompositeType { PartA = 1, PartB = "Hello, " }, new CompositeType { PartA = 2, PartB = "World!" }); 12 Console.WriteLine(string.Format("PartA: {0}, PartB: {1}", result.PartA, result.PartB)); 13 }
-
端口共享
在Windows系统,为了安全,常常只开发少量端口,当有大量应用需要使用不同端口时,会显得捉襟见肘,因此多个应用共享同一个端口显得很有必要。对于Http/Https协议来说,由于其可以通过IIS来管理应用,其自身通过HTTP.SYS已经实现了80|443端口的共享。而对于TCP协议来说,其通过一个Windows服务(名称为Net.Tcp Port Sharing Service)来管理,可以通过如下方式实现其共享。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <bindings> 2 <netTcpBinding> 3 <binding name="portSharingBinding" portSharingEnabled="true"></binding> 4 </netTcpBinding> 5 </bindings>
-
逻辑地址和物理地址
之前在EndpointAddress中提及的Uri属性表示服务的逻辑地址,而物理地址对于服务端来说是监听地址,对于客户端来说是消息真正发送的目标地址。默认情况下,两个地址是统一的,但在需要中介进行消息转发的场景下,需要将两者分离。
对于服务端,可以设置终结点的ListenUri的属性和ListenUriMode属性(包括Explicit和Unique,前者严格使用ListenUri作为最终的监听地址,后者将通过不同的策略保证监听地址的唯一性,如针对端口共享的情况,将在默认Uri后加GUID以作识别),共同完成该需求,示例如下。
示例如下。
<endpoint address="http://127.0.0.1:9901/addservice" listenUri="http://127.0.0.1:9900/addservice" listenUriMode="Unique" …/>
对于客户端,需要借助ClientViaBehavior这一终结点行为来实现,示例如下。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <behaviors> 2 <endpointBehaviors> 3 <behavior name="clientViaBehavior"> 4 <clientVia viaUri="http://127.0.0.1:9900/addservice"/> 5 </behavior> 6 </endpointBehaviors> 7 </behaviors> 8 <client > 9 <endpoint behaviorConfiguration="clientViaBehavior"></endpoint> 10 </client>
补充:行为这个概念在WCF中非常的重要,很多的功能都是通过相应的行为实现的,接下来进行简要介绍。如果说契约是客户端和服务端达成的某种共识,是双边协议,而行为则是客户端或服务端在本地实现某个功能的一种方式,是一种单边行为。WCF提供了4种类型的行为,包括服务行为、契约行为、终结点行为和操作行为,它们一般可以通过特性或者配置文件的方式进行设置。
-
请求监听和消息分发
这部分内容涉及到整个WCF服务端的架构,下图展示了一个最简单的请求分发过程。
在整个消息监听和分发体系中,信道分发器和终结点分发器是两个核心的对象,前者负责请求监听、消息接收并通过消息筛选器选择正确的终结点,后者完成消息的处理。终结点分发器具有两个消息消息筛选器,分别是AddressFilter和ContractFilter,均是MessageFilter类型,前者对应的AddressFilterMode包含Exact、Prefix、Any三种枚举类型。WCF提供6种典型的消息筛选器,包括:ActionMessageFilter,判断请求消息(SOAP)的<Action>报头是否和终结点契约中任意操作的Action属性相匹配(Match);EndpointAddressMessageFilter判断<To>报头是否和终结点地址相匹配;MatchAllMessageFilter,表示全匹配;以及不常用的XPathMessageFilter、MatchNoneMeesageFilter和PrefixEndpointAddressMessageFilter。
从基础架构的角度上看,WCF可以分为服务模型层和信道层两个层次,服务模型层建立在信道层的基础是上,而信道层就是通过本节即将介绍的binding绑定创建,注意这儿的绑定与.NET很多地方的绑定概念不同(例如最常见的数据绑定),注意理解。那么binding是如何创建信道层的呢?它通过组合不同的信道,将其整合为一个指定的信道栈,这个过程其实就是一个职责链模式的实现,每个信道都只处理自己的一部分内容,最基本的有传输、编码,复杂一些的包括事务流转、安全传输和可靠传输,使得整个框架足够灵活,已于扩展,一个支持WS-*的信道栈如下图所示。
其中传输信道实现了基于某种协议的消息传输,消息编码信道实现了消息的编码(例如XML、Binary、MTOM),而WS-AT(WS-Atomic Transaction)实现了分布式的事务支持,WS-RM(WS-Reliable Messaging)实现了信息的可靠传输,WS-Security实现了消息的传输安全,他们都可以被称为协议信道。接下来通过一个简单的例子来演示通过绑定进行消息通信,在其中将引入信道、信道监听器、信道工厂等主要对象。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 服务端: 2 static void Main(string[] args) 3 { 4 var listenUri = new Uri("http://127.0.0.1:9902/listener"); 5 var binding = new BasicHttpBinding(); 6 //创建和开启信道监听器 7 var channelListener = binding.BuildChannelListener<IReplyChannel>(listenUri); 8 channelListener.Open(); 9 //创建、开启回复信道 10 var channel = channelListener.AcceptChannel(TimeSpan.MaxValue); 11 channel.Open(); 12 //开始监听 13 while (true) 14 { 15 //接受输入请求信息 16 var requestContext = channel.ReceiveRequest(TimeSpan.MaxValue); 17 Console.WriteLine(requestContext.RequestMessage); 18 requestContext.Reply(CreateReplyMessage(binding)); 19 } 20 } 21 22 private static Message CreateReplyMessage(Binding binding) 23 { 24 var action = "http://www.sory.com/addservice/AddResponse"; 25 XNamespace ns = "http://www.sory.com"; 26 XElement body = new XElement(new XElement(ns + "AddResponse", new XElement(ns + "AddResult", 3))); 27 return Message.CreateMessage(binding.MessageVersion, action, body); 28 } 29 30 客户端: 31 static void Main(string[] args) 32 { 33 var listenUri = new Uri("http://127.0.0.1:9902/listener"); 34 var binding = new BasicHttpBinding(); 35 //创建和开启信道工厂 36 var channelFactory = binding.BuildChannelFactory<IRequestChannel>(); 37 channelFactory.Open(); 38 //创建、开启请求信道 39 var channel = channelFactory.CreateChannel(new EndpointAddress(listenUri)); 40 channel.Open(); 41 //发送请求消息,接受回复消息 42 var replyMessage = channel.Request(CreateRequestMessage(binding)); 43 Console.WriteLine(replyMessage); 44 Console.Read(); 45 } 46 47 private static Message CreateRequestMessage(Binding binding) 48 { 49 var action = "http://www.sory.com/addservice/Add"; 50 XNamespace ns = "http://www.sory.com"; 51 XElement body = new XElement(new XElement(ns + "Add", new XElement(ns + "x", 1), new XElement(ns + "y", 2))); 52 return Message.CreateMessage(binding.MessageVersion, action, body); 53 }
通过这个例子看起来很像以前的Window网络编程中的Socket编程形式,首先服务端监听,然后客户端请求,服务端接收并绑定Socket(这儿是绑定信道),之后就可以在此基础上进行通讯了。这部分涉及到的类型很多,接下来通过一个表格简述部分主要类,浏览即可。
类别 |
介绍 |
信道与信道栈 |
最基础的ICommunicationObject接口,提供统一管理通信对象的状态机,可以作为一种设计范例用于实际项目中;DefaultCommunicationTimeouts类负责控制超时时限;IChannel和ChannelBase用于表示信道;ISession和ISessionChannel<TSession>用于表示会话信道。此外,支持3种消息交换模式。 数据报Datagram模式:一般使一部的消息发送方式,支持1或多个接收者,对应IOutputChannel, IInputChannel 请求-回复模式:对应IRequestChannel、IReplyChannel 双工模式:对应IDuplexChannel |
信道监听器(Server) |
IChannelListener, ChannelListenerBase |
信道工厂(Client) |
IChannelFactory, ChannelFactoryBase |
最后,进入绑定元素与绑定的介绍,之前提到过,绑定是用于创建信道栈的,而它其中的绑定元素则是用于创建具体的信道的。常见的系统绑定包括:BasicHttpBinding、WSHttpBinding、WS2007HttpBinding、WSDualHttpBinding、NetTcpBinding、NetNamedPipeBinding和NetMsmqBinding。其中BasicHttpBinding最为基础,在构建类似web服务形式的应用中使用最多,所有带Net前缀的绑定将局限于.NET平台,不同的绑定的运行效率有不小差异。一般来说,企业内部的服务推荐使用RPC类型的服务,如NetTcpBinding,而对外服务推荐使用WSHttpBinding,当然实际项目中,对外服务一般不会使用WCF框架,而是使用Restful风格的WebAPI。此外,也可以建立自定义的绑定,将框架提供的绑定元素进行重新组合,更有甚者,可以自定义绑定元素,不过这部分内容使用的场景非常的少。最后,提供一个简单自定义绑定配置作为参考,其组合了传输、编码和安全3个绑定元素,前两者是必选项,且必须按照顺序构建。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <bindings > 2 <customBinding> 3 <binding name="testBinding"> 4 <security></security> 5 <textMessageEncoding></textMessageEncoding> 6 <tcpTransport></tcpTransport> 7 </binding> 8 </customBinding> 9 </bindings>
契约其实就是一个生活中的概念,是一种双边和多边的协议,在WCF中,其保证了无论服务的实现有任何的改变,而服务的消费者始终可以通过契约约定方式来调用服务。由于整个WCF都是基于SOAP以及WS-*的,因此其XML是数据格式标准,通过XSD控制XML的数据结构,用WSDL(web服务描述语言)来提供跨平台的描述服务。
服务契约的定义通过ServiceContractAttribute和OperationContractAttribute两个特性来定义,前者定义整个服务,后者定义服务中具体的方法,接下来具体介绍一下这两个类。ServiceContractAttribute类,比较重要的属性包括:Name,可以定义服务的名称,默认为接口名;Namespace定义服务的命名空间,可以使用自己的公司名和项目名的组合来设定,其和之前的Name在wsdl文件中均是对<portType>元素的修饰;ConfigurationName实际上就对应配置中的Contract名称;SessionMode表示契约的会话模式,比如Allowed、Required等;ProtectionLevel表示消息的保护级别;CallbackContract在双工通信时指定回调操作的接口类型。OperationContractAttribute类,其属性Name、Namespace、ProtectionLevel与之前相似,值得一提的属性包括:Action/ReplyAction用于控制某个操作请求/回复信息的<Action>头,其默认通过命名空间、服务契约、操作名称组成,后者默认添加Response;IsOneWay控制消息交换的模式。提到消息交换的模式,记得之前提到过主要的三种请求-回复、单向和双工,前两项之前的例子中已有展示,之后的示例将展示双工模式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 服务端: 2 public interface IAddCallback 3 { 4 [OperationContract] 5 void DisplayResult(CompositeType result, CompositeType a, CompositeType b); 6 } 7 [ServiceContract(CallbackContract=typeof(IAddCallback))] 8 public interface IAddService 9 { 10 [OperationContract] 11 void Add(CompositeType a, CompositeType b); 12 } 13 14 [DataContract] 15 public class CompositeType 16 { 17 [DataMember] 18 public int PartA { get; set; } 19 [DataMember] 20 public string PartB { get; set; } 21 } 22 public class AddCallbackService : IAddCallback 23 { 24 public void DisplayResult(CompositeType result, CompositeType a, CompositeType b) 25 { 26 Console.WriteLine("x + y = {2} when x= {0} and y = {1}", a.PartA, b.PartA, result.PartA); 27 } 28 } 29 30 public class AddService : IAddService 31 { 32 public void Add(CompositeType a, CompositeType b) 33 { 34 var result = new CompositeType() { PartA = a.PartA + b.PartA, PartB = a.PartB + b.PartB }; 35 IAddCallback callback = OperationContext.Current.GetCallbackChannel<IAddCallback>(); 36 callback.DisplayResult(result, a, b); 37 } 38 } 39 40 配置: 41 <system.serviceModel> 42 <behaviors> 43 <serviceBehaviors> 44 <behavior name="metadataBehavior"> 45 <serviceMetadata httpGetEnabled="True" httpGetUrl="http://127.0.0.1:9901/addservice/metadata"/> 46 <serviceDebug includeExceptionDetailInFaults="true"/> 47 </behavior> 48 </serviceBehaviors> 49 </behaviors> 50 <services> 51 <service name="Sory.Entertainment.WCF.AddService" behaviorConfiguration="metadataBehavior"> 52 <endpoint address="net.tcp://127.0.0.1:1001/addservice" binding="netTcpBinding" contract="Sory.Entertainment.WCF.IAddService"/> 53 </service> 54 </services> 55 </system.serviceModel> 56 57 客户端: 58 59 InstanceContext callback = new InstanceContext(new AddCallbackService()); 60 using (DuplexChannelFactory<IAddService> channelFactory = new DuplexChannelFactory<IAddService>(callback, "addservice")) 61 { 62 var addChannel = channelFactory.CreateChannel(); 63 addChannel.Add(new CompositeType { PartA = 1 }, new CompositeType { PartA = 2 }); 64 } 65 66 配置: 67 <system.serviceModel> 68 <client> 69 <endpoint name="addservice" address="net.tcp://127.0.0.1:1001/addservice" binding="netTcpBinding" contract="Sory.Entertainment.WCF.IAddService"/> 70 </client> 71 </system.serviceModel>
当调用以上示例的服务时,会抛出一个关于死锁的异常,原因是其在并发场景下会造成回调死锁的情况,可以通过将请求或回调方法设置为单向即可。
此外,服务契约是不支持继承的,而操作契约支持继承,不过这部分也不太常用,而与契约相关的元数据描述类也非常简单,这儿就不展开介绍了。
-
多线程和异步操作
在《CLR via C#》中,将操作分为计算限制的和I/O限制的,一般来说,WCF中主要涉及到I/O限制的操作,这种类型的操作主要是通过异步模型来提高其并发性。谈到异步操作,在SOA这类应用中包含3个不同异步场景,这部分知识比较有意思,曾经困到鄙人多年。这3中场景包括:异步的信道调用,客户端可以通过代理对象异步的调用信道;单向消息交换,客户端的信道通过单向的消息交换模式向服务端发送消息,发送立刻返回;异步服务实现,服务端在具体实现服务操作时,采用异步调用的方式。
异步服务代理的创建,可以通过在添加服务引用时通过高级选项添加生成异步操作选项,之后可以通过使用BeginXX/EndXX方法、回调和事件注册等方式使用异步服务代理类。而异步的服务实现可以在服务接口中将原有方法修改为BeginXXX/EndXXX形式的异步方法名,并将OperationContract契约的AsyncPattern属性设置为true即可。
-
操作的选择与执行
之前提及的契约描述类中的Operations列表只包含了被OperationContractAttribute特性修饰的服务操作,而运行时的操作是通过DispatchOperation和ClientOperation两个类型表示。DispatchOperation在服务端的终结点分发器初始化时建立一个DispatchRuntime类,其通过一个SynchronizedKeyedCollection<string, DispatchOperation>集合类型来管理所有的运行时分发操作,OperationSelector用于操作选择,IOperationInvoker用于操作执行。ClientOperation和前者的结构基本一致,只不过它用于客户端而已。
Tip:在实际中,很多公司选用ServiceStack的开源架构来构建的自身的SOA服务,此外,过去也常常以通过WebService搭建企业服务总线ESB的方式构建SOA服务。这部分推荐两位大神的博文,寒江独钓的http://www.cnblogs.com/yangecnu/p/Introduce-ServiceStack.html和张善友的http://www.cnblogs.com/shanyou/p/3348347.html。
最后,分享一个好玩的东西,就是在微信中可以搜索微软的"小冰"(刚截稿前对面的程序媛告诉我的,挺逗的,能挖掘你的内心哦),然后就可以在编码无聊、寂寞空虚时…你懂得,哈哈!
参考资料:
[1]蒋金楠. WCF全面解析[M]. 上海:电子工业出版社, 2012.
出 处:http://www.cnblogs.com/xiong2ge/
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。